Version 3.18.1
Show:

File: charts/js/CartesianChart.js

  1. /**
  2. * The CartesianChart class creates a chart with horizontal and vertical axes.
  3. *
  4. * @class CartesianChart
  5. * @extends ChartBase
  6. * @constructor
  7. * @submodule charts-base
  8. */
  9. Y.CartesianChart = Y.Base.create("cartesianChart", Y.Widget, [Y.ChartBase, Y.Renderer], {
  10. /**
  11. * @method renderUI
  12. * @private
  13. */
  14. renderUI: function()
  15. {
  16. var bb = this.get("boundingBox"),
  17. cb = this.get("contentBox"),
  18. tt = this.get("tooltip"),
  19. overlayClass = _getClassName("overlay");
  20. //move the position = absolute logic to a class file
  21. bb.setStyle("position", "absolute");
  22. cb.setStyle("position", "absolute");
  23. this._addAxes();
  24. this._addGridlines();
  25. this._addSeries();
  26. if(tt && tt.show)
  27. {
  28. this._addTooltip();
  29. }
  30. if(this.get("interactionType") === "planar")
  31. {
  32. this._overlay = Y.Node.create("<div></div>");
  33. this._overlay.set("id", this.get("id") + "_overlay");
  34. this._overlay.setStyle("position", "absolute");
  35. this._overlay.setStyle("background", "#fff");
  36. this._overlay.setStyle("opacity", 0);
  37. this._overlay.addClass(overlayClass);
  38. this._overlay.setStyle("zIndex", 4);
  39. cb.append(this._overlay);
  40. }
  41. this._setAriaElements(bb, cb);
  42. this._redraw();
  43. },
  44. /**
  45. * When `interactionType` is set to `planar`, listens for mouse move events and fires `planarEvent:mouseover` or `planarEvent:mouseout`
  46. * depending on the position of the mouse in relation to data points on the `Chart`.
  47. *
  48. * @method _planarEventDispatcher
  49. * @param {Object} e Event object.
  50. * @private
  51. */
  52. _planarEventDispatcher: function(e)
  53. {
  54. var graph = this.get("graph"),
  55. bb = this.get("boundingBox"),
  56. cb = graph.get("contentBox"),
  57. isTouch = e && e.hasOwnProperty("changedTouches"),
  58. pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
  59. pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
  60. posX = pageX - bb.getX(),
  61. posY = pageY - bb.getY(),
  62. offset = {
  63. x: pageX - cb.getX(),
  64. y: pageY - cb.getY()
  65. },
  66. sc = graph.get("seriesCollection"),
  67. series,
  68. i = 0,
  69. index,
  70. oldIndex = this._selectedIndex,
  71. item,
  72. items = [],
  73. categoryItems = [],
  74. valueItems = [],
  75. direction = this.get("direction"),
  76. hasMarkers,
  77. catAxis,
  78. valAxis,
  79. coord,
  80. //data columns and area data could be created on a graph level
  81. markerPlane,
  82. len,
  83. coords;
  84. e.halt(true);
  85. if(direction === "horizontal")
  86. {
  87. catAxis = "x";
  88. valAxis = "y";
  89. }
  90. else
  91. {
  92. valAxis = "x";
  93. catAxis = "y";
  94. }
  95. coord = offset[catAxis];
  96. if(sc)
  97. {
  98. len = sc.length;
  99. while(i < len && !markerPlane)
  100. {
  101. if(sc[i])
  102. {
  103. markerPlane = sc[i].get(catAxis + "MarkerPlane");
  104. }
  105. i++;
  106. }
  107. }
  108. if(markerPlane)
  109. {
  110. len = markerPlane.length;
  111. for(i = 0; i < len; ++i)
  112. {
  113. if(coord <= markerPlane[i].end && coord >= markerPlane[i].start)
  114. {
  115. index = i;
  116. break;
  117. }
  118. }
  119. len = sc.length;
  120. for(i = 0; i < len; ++i)
  121. {
  122. series = sc[i];
  123. coords = series.get(valAxis + "coords");
  124. hasMarkers = series.get("markers");
  125. if(hasMarkers && !isNaN(oldIndex) && oldIndex > -1)
  126. {
  127. series.updateMarkerState("mouseout", oldIndex);
  128. }
  129. if(coords && coords[index] > -1)
  130. {
  131. if(hasMarkers && !isNaN(index) && index > -1)
  132. {
  133. series.updateMarkerState("mouseover", index);
  134. }
  135. item = this.getSeriesItems(series, index);
  136. categoryItems.push(item.category);
  137. valueItems.push(item.value);
  138. items.push(series);
  139. }
  140. }
  141. this._selectedIndex = index;
  142. /**
  143. * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseover event.
  144. *
  145. *
  146. * @event planarEvent:mouseover
  147. * @preventable false
  148. * @param {EventFacade} e Event facade with the following additional
  149. * properties:
  150. * <dl>
  151. * <dt>categoryItem</dt><dd>An array of hashes, each containing information about the category `Axis` of each marker
  152. * whose plane has been intersected.</dd>
  153. * <dt>valueItem</dt><dd>An array of hashes, each containing information about the value `Axis` of each marker whose
  154. * plane has been intersected.</dd>
  155. * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd>
  156. * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd>
  157. * <dt>pageX</dt><dd>The x location of the event on the page (including scroll)</dd>
  158. * <dt>pageY</dt><dd>The y location of the event on the page (including scroll)</dd>
  159. * <dt>items</dt><dd>An array including all the series which contain a marker whose plane has been intersected.</dd>
  160. * <dt>index</dt><dd>Index of the markers in their respective series.</dd>
  161. * <dt>originEvent</dt><dd>Underlying dom event.</dd>
  162. * </dl>
  163. */
  164. /**
  165. * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseout event.
  166. *
  167. * @event planarEvent:mouseout
  168. * @preventable false
  169. * @param {EventFacade} e
  170. */
  171. if(index > -1)
  172. {
  173. this.fire("planarEvent:mouseover", {
  174. categoryItem:categoryItems,
  175. valueItem:valueItems,
  176. x:posX,
  177. y:posY,
  178. pageX:pageX,
  179. pageY:pageY,
  180. items:items,
  181. index:index,
  182. originEvent:e
  183. });
  184. }
  185. else
  186. {
  187. this.fire("planarEvent:mouseout");
  188. }
  189. }
  190. },
  191. /**
  192. * Indicates the default series type for the chart.
  193. *
  194. * @property _type
  195. * @type {String}
  196. * @private
  197. */
  198. _type: "combo",
  199. /**
  200. * Queue of axes instances that will be updated. This method is used internally to determine when all axes have been updated.
  201. *
  202. * @property _itemRenderQueue
  203. * @type Array
  204. * @private
  205. */
  206. _itemRenderQueue: null,
  207. /**
  208. * Adds an `Axis` instance to the `_itemRenderQueue`.
  209. *
  210. * @method _addToAxesRenderQueue
  211. * @param {Axis} axis An `Axis` instance.
  212. * @private
  213. */
  214. _addToAxesRenderQueue: function(axis)
  215. {
  216. if(!this._itemRenderQueue)
  217. {
  218. this._itemRenderQueue = [];
  219. }
  220. if(Y.Array.indexOf(this._itemRenderQueue, axis) < 0)
  221. {
  222. this._itemRenderQueue.push(axis);
  223. }
  224. },
  225. /**
  226. * Adds axis instance to the appropriate array based on position
  227. *
  228. * @method _addToAxesCollection
  229. * @param {String} position The position of the axis
  230. * @param {Axis} axis The `Axis` instance
  231. */
  232. _addToAxesCollection: function(position, axis)
  233. {
  234. var axesCollection = this.get(position + "AxesCollection");
  235. if(!axesCollection)
  236. {
  237. axesCollection = [];
  238. this.set(position + "AxesCollection", axesCollection);
  239. }
  240. axesCollection.push(axis);
  241. },
  242. /**
  243. * Returns the default value for the `seriesCollection` attribute.
  244. *
  245. * @method _getDefaultSeriesCollection
  246. * @param {Array} val Array containing either `CartesianSeries` instances or objects containing data to construct series instances.
  247. * @return Array
  248. * @private
  249. */
  250. _getDefaultSeriesCollection: function()
  251. {
  252. var seriesCollection,
  253. dataProvider = this.get("dataProvider");
  254. if(dataProvider)
  255. {
  256. seriesCollection = this._parseSeriesCollection();
  257. }
  258. return seriesCollection;
  259. },
  260. /**
  261. * Parses and returns a series collection from an object and default properties.
  262. *
  263. * @method _parseSeriesCollection
  264. * @param {Object} val Object contain properties for series being set.
  265. * @return Object
  266. * @private
  267. */
  268. _parseSeriesCollection: function(val)
  269. {
  270. var dir = this.get("direction"),
  271. seriesStyles = this.get("styles").series,
  272. stylesAreArray = seriesStyles && Y_Lang.isArray(seriesStyles),
  273. stylesIndex,
  274. setStyles,
  275. globalStyles,
  276. sc = [],
  277. catAxis,
  278. valAxis,
  279. tempKeys = [],
  280. series,
  281. seriesKeys = this.get("seriesKeys").concat(),
  282. i,
  283. index,
  284. l,
  285. type = this.get("type"),
  286. key,
  287. catKey,
  288. seriesKey,
  289. graph,
  290. orphans = [],
  291. categoryKey = this.get("categoryKey"),
  292. showMarkers = this.get("showMarkers"),
  293. showAreaFill = this.get("showAreaFill"),
  294. showLines = this.get("showLines");
  295. val = val ? val.concat() : [];
  296. if(dir === "vertical")
  297. {
  298. catAxis = "yAxis";
  299. catKey = "yKey";
  300. valAxis = "xAxis";
  301. seriesKey = "xKey";
  302. }
  303. else
  304. {
  305. catAxis = "xAxis";
  306. catKey = "xKey";
  307. valAxis = "yAxis";
  308. seriesKey = "yKey";
  309. }
  310. l = val.length;
  311. while(val && val.length > 0)
  312. {
  313. series = val.shift();
  314. key = this._getBaseAttribute(series, seriesKey);
  315. if(key)
  316. {
  317. index = Y.Array.indexOf(seriesKeys, key);
  318. if(index > -1)
  319. {
  320. seriesKeys.splice(index, 1);
  321. tempKeys.push(key);
  322. sc.push(series);
  323. }
  324. else
  325. {
  326. orphans.push(series);
  327. }
  328. }
  329. else
  330. {
  331. orphans.push(series);
  332. }
  333. }
  334. while(orphans.length > 0)
  335. {
  336. series = orphans.shift();
  337. if(seriesKeys.length > 0)
  338. {
  339. key = seriesKeys.shift();
  340. this._setBaseAttribute(series, seriesKey, key);
  341. tempKeys.push(key);
  342. sc.push(series);
  343. }
  344. else if(series instanceof Y.CartesianSeries)
  345. {
  346. series.destroy(true);
  347. }
  348. }
  349. if(seriesKeys.length > 0)
  350. {
  351. tempKeys = tempKeys.concat(seriesKeys);
  352. }
  353. l = tempKeys.length;
  354. for(i = 0; i < l; ++i)
  355. {
  356. series = sc[i] || {type:type};
  357. if(series instanceof Y.CartesianSeries)
  358. {
  359. this._parseSeriesAxes(series);
  360. }
  361. else
  362. {
  363. series[catKey] = series[catKey] || categoryKey;
  364. series[seriesKey] = series[seriesKey] || seriesKeys.shift();
  365. series[catAxis] = this._getCategoryAxis();
  366. series[valAxis] = this._getSeriesAxis(series[seriesKey]);
  367. series.type = series.type || type;
  368. series.direction = series.direction || dir;
  369. if(series.type === "combo" ||
  370. series.type === "stackedcombo" ||
  371. series.type === "combospline" ||
  372. series.type === "stackedcombospline")
  373. {
  374. if(showAreaFill !== null)
  375. {
  376. series.showAreaFill = (series.showAreaFill !== null && series.showAreaFill !== undefined) ?
  377. series.showAreaFill : showAreaFill;
  378. }
  379. if(showMarkers !== null)
  380. {
  381. series.showMarkers = (series.showMarkers !== null && series.showMarkers !== undefined) ? series.showMarkers : showMarkers;
  382. }
  383. if(showLines !== null)
  384. {
  385. series.showLines = (series.showLines !== null && series.showLines !== undefined) ? series.showLines : showLines;
  386. }
  387. }
  388. if(seriesStyles)
  389. {
  390. stylesIndex = stylesAreArray ? i : series[seriesKey];
  391. globalStyles = seriesStyles[stylesIndex];
  392. if(globalStyles)
  393. {
  394. setStyles = series.styles;
  395. if(setStyles)
  396. {
  397. series.styles = this._mergeStyles(setStyles, globalStyles);
  398. }
  399. else
  400. {
  401. series.styles = globalStyles;
  402. }
  403. }
  404. }
  405. sc[i] = series;
  406. }
  407. }
  408. if(sc)
  409. {
  410. graph = this.get("graph");
  411. graph.set("seriesCollection", sc);
  412. sc = graph.get("seriesCollection");
  413. }
  414. return sc;
  415. },
  416. /**
  417. * Parse and sets the axes for a series instance.
  418. *
  419. * @method _parseSeriesAxes
  420. * @param {CartesianSeries} series A `CartesianSeries` instance.
  421. * @private
  422. */
  423. _parseSeriesAxes: function(series)
  424. {
  425. var axes = this.get("axes"),
  426. xAxis = series.get("xAxis"),
  427. yAxis = series.get("yAxis"),
  428. YAxis = Y.Axis,
  429. axis;
  430. if(xAxis && !(xAxis instanceof YAxis) && Y_Lang.isString(xAxis) && axes.hasOwnProperty(xAxis))
  431. {
  432. axis = axes[xAxis];
  433. if(axis instanceof YAxis)
  434. {
  435. series.set("xAxis", axis);
  436. }
  437. }
  438. if(yAxis && !(yAxis instanceof YAxis) && Y_Lang.isString(yAxis) && axes.hasOwnProperty(yAxis))
  439. {
  440. axis = axes[yAxis];
  441. if(axis instanceof YAxis)
  442. {
  443. series.set("yAxis", axis);
  444. }
  445. }
  446. },
  447. /**
  448. * Returns the category axis instance for the chart.
  449. *
  450. * @method _getCategoryAxis
  451. * @return Axis
  452. * @private
  453. */
  454. _getCategoryAxis: function()
  455. {
  456. var axis,
  457. axes = this.get("axes"),
  458. categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey");
  459. axis = axes[categoryAxisName];
  460. return axis;
  461. },
  462. /**
  463. * Returns the value axis for a series.
  464. *
  465. * @method _getSeriesAxis
  466. * @param {String} key The key value used to determine the axis instance.
  467. * @return Axis
  468. * @private
  469. */
  470. _getSeriesAxis:function(key, axisName)
  471. {
  472. var axes = this.get("axes"),
  473. i,
  474. keys,
  475. axis;
  476. if(axes)
  477. {
  478. if(axisName && axes.hasOwnProperty(axisName))
  479. {
  480. axis = axes[axisName];
  481. }
  482. else
  483. {
  484. for(i in axes)
  485. {
  486. if(axes.hasOwnProperty(i))
  487. {
  488. keys = axes[i].get("keys");
  489. if(keys && keys.hasOwnProperty(key))
  490. {
  491. axis = axes[i];
  492. break;
  493. }
  494. }
  495. }
  496. }
  497. }
  498. return axis;
  499. },
  500. /**
  501. * Gets an attribute from an object, using a getter for Base objects and a property for object
  502. * literals. Used for determining attributes from series/axis references which can be an actual class instance
  503. * or a hash of properties that will be used to create a class instance.
  504. *
  505. * @method _getBaseAttribute
  506. * @param {Object} item Object or instance in which the attribute resides.
  507. * @param {String} key Attribute whose value will be returned.
  508. * @return Object
  509. * @private
  510. */
  511. _getBaseAttribute: function(item, key)
  512. {
  513. if(item instanceof Y.Base)
  514. {
  515. return item.get(key);
  516. }
  517. if(item.hasOwnProperty(key))
  518. {
  519. return item[key];
  520. }
  521. return null;
  522. },
  523. /**
  524. * Sets an attribute on an object, using a setter of Base objects and a property for object
  525. * literals. Used for setting attributes on a Base class, either directly or to be stored in an object literal
  526. * for use at instantiation.
  527. *
  528. * @method _setBaseAttribute
  529. * @param {Object} item Object or instance in which the attribute resides.
  530. * @param {String} key Attribute whose value will be assigned.
  531. * @param {Object} value Value to be assigned to the attribute.
  532. * @private
  533. */
  534. _setBaseAttribute: function(item, key, value)
  535. {
  536. if(item instanceof Y.Base)
  537. {
  538. item.set(key, value);
  539. }
  540. else
  541. {
  542. item[key] = value;
  543. }
  544. },
  545. /**
  546. * Creates `Axis` instances.
  547. *
  548. * @method _setAxes
  549. * @param {Object} val Object containing `Axis` instances or objects in which to construct `Axis` instances.
  550. * @return Object
  551. * @private
  552. */
  553. _setAxes: function(val)
  554. {
  555. var hash = this._parseAxes(val),
  556. axes = {},
  557. axesAttrs = {
  558. edgeOffset: "edgeOffset",
  559. calculateEdgeOffset: "calculateEdgeOffset",
  560. position: "position",
  561. overlapGraph:"overlapGraph",
  562. labelValues: "labelValues",
  563. hideFirstMajorUnit: "hideFirstMajorUnit",
  564. hideLastMajorUnit: "hideLastMajorUnit",
  565. labelFunction:"labelFunction",
  566. labelFunctionScope:"labelFunctionScope",
  567. labelFormat:"labelFormat",
  568. appendLabelFunction: "appendLabelFunction",
  569. appendTitleFunction: "appendTitleFunction",
  570. maximum:"maximum",
  571. minimum:"minimum",
  572. roundingMethod:"roundingMethod",
  573. alwaysShowZero:"alwaysShowZero",
  574. scaleType: "scaleType",
  575. title:"title",
  576. width:"width",
  577. height:"height"
  578. },
  579. dp = this.get("dataProvider"),
  580. ai,
  581. i,
  582. pos,
  583. axis,
  584. axisPosition,
  585. dh,
  586. AxisClass,
  587. config,
  588. axesCollection;
  589. for(i in hash)
  590. {
  591. if(hash.hasOwnProperty(i))
  592. {
  593. dh = hash[i];
  594. if(dh instanceof Y.Axis)
  595. {
  596. axis = dh;
  597. }
  598. else
  599. {
  600. axis = null;
  601. config = {};
  602. config.dataProvider = dh.dataProvider || dp;
  603. config.keys = dh.keys;
  604. if(dh.hasOwnProperty("roundingUnit"))
  605. {
  606. config.roundingUnit = dh.roundingUnit;
  607. }
  608. pos = dh.position;
  609. if(dh.styles)
  610. {
  611. config.styles = dh.styles;
  612. }
  613. config.position = dh.position;
  614. for(ai in axesAttrs)
  615. {
  616. if(axesAttrs.hasOwnProperty(ai) && dh.hasOwnProperty(ai))
  617. {
  618. config[ai] = dh[ai];
  619. }
  620. }
  621. //only check for existing axis if we constructed the default axes already
  622. if(val)
  623. {
  624. axis = this.getAxisByKey(i);
  625. }
  626. if(axis && axis instanceof Y.Axis)
  627. {
  628. axisPosition = axis.get("position");
  629. if(pos !== axisPosition)
  630. {
  631. if(axisPosition !== "none")
  632. {
  633. axesCollection = this.get(axisPosition + "AxesCollection");
  634. axesCollection.splice(Y.Array.indexOf(axesCollection, axis), 1);
  635. }
  636. if(pos !== "none")
  637. {
  638. this._addToAxesCollection(pos, axis);
  639. }
  640. }
  641. axis.setAttrs(config);
  642. }
  643. else
  644. {
  645. AxisClass = this._getAxisClass(dh.type);
  646. axis = new AxisClass(config);
  647. axis.after("axisRendered", Y.bind(this._itemRendered, this));
  648. }
  649. }
  650. if(axis)
  651. {
  652. axesCollection = this.get(pos + "AxesCollection");
  653. if(axesCollection && Y.Array.indexOf(axesCollection, axis) > 0)
  654. {
  655. axis.set("overlapGraph", false);
  656. }
  657. axes[i] = axis;
  658. }
  659. }
  660. }
  661. return axes;
  662. },
  663. /**
  664. * Adds axes to the chart.
  665. *
  666. * @method _addAxes
  667. * @private
  668. */
  669. _addAxes: function()
  670. {
  671. var axes = this.get("axes"),
  672. i,
  673. axis,
  674. pos,
  675. w = this.get("width"),
  676. h = this.get("height"),
  677. node = Y.Node.one(this._parentNode);
  678. if(!this._axesCollection)
  679. {
  680. this._axesCollection = [];
  681. }
  682. for(i in axes)
  683. {
  684. if(axes.hasOwnProperty(i))
  685. {
  686. axis = axes[i];
  687. if(axis instanceof Y.Axis)
  688. {
  689. if(!w)
  690. {
  691. this.set("width", node.get("offsetWidth"));
  692. w = this.get("width");
  693. }
  694. if(!h)
  695. {
  696. this.set("height", node.get("offsetHeight"));
  697. h = this.get("height");
  698. }
  699. this._addToAxesRenderQueue(axis);
  700. pos = axis.get("position");
  701. if(!this.get(pos + "AxesCollection"))
  702. {
  703. this.set(pos + "AxesCollection", [axis]);
  704. }
  705. else
  706. {
  707. this.get(pos + "AxesCollection").push(axis);
  708. }
  709. this._axesCollection.push(axis);
  710. if(axis.get("keys").hasOwnProperty(this.get("categoryKey")))
  711. {
  712. this.set("categoryAxis", axis);
  713. }
  714. axis.render(this.get("contentBox"));
  715. }
  716. }
  717. }
  718. },
  719. /**
  720. * Renders the Graph.
  721. *
  722. * @method _addSeries
  723. * @private
  724. */
  725. _addSeries: function()
  726. {
  727. var graph = this.get("graph");
  728. graph.render(this.get("contentBox"));
  729. },
  730. /**
  731. * Adds gridlines to the chart.
  732. *
  733. * @method _addGridlines
  734. * @private
  735. */
  736. _addGridlines: function()
  737. {
  738. var graph = this.get("graph"),
  739. hgl = this.get("horizontalGridlines"),
  740. vgl = this.get("verticalGridlines"),
  741. direction = this.get("direction"),
  742. leftAxesCollection = this.get("leftAxesCollection"),
  743. rightAxesCollection = this.get("rightAxesCollection"),
  744. bottomAxesCollection = this.get("bottomAxesCollection"),
  745. topAxesCollection = this.get("topAxesCollection"),
  746. seriesAxesCollection,
  747. catAxis = this.get("categoryAxis"),
  748. hAxis,
  749. vAxis;
  750. if(this._axesCollection)
  751. {
  752. seriesAxesCollection = this._axesCollection.concat();
  753. seriesAxesCollection.splice(Y.Array.indexOf(seriesAxesCollection, catAxis), 1);
  754. }
  755. if(hgl)
  756. {
  757. if(leftAxesCollection && leftAxesCollection[0])
  758. {
  759. hAxis = leftAxesCollection[0];
  760. }
  761. else if(rightAxesCollection && rightAxesCollection[0])
  762. {
  763. hAxis = rightAxesCollection[0];
  764. }
  765. else
  766. {
  767. hAxis = direction === "horizontal" ? catAxis : seriesAxesCollection[0];
  768. }
  769. if(!this._getBaseAttribute(hgl, "axis") && hAxis)
  770. {
  771. this._setBaseAttribute(hgl, "axis", hAxis);
  772. }
  773. if(this._getBaseAttribute(hgl, "axis"))
  774. {
  775. graph.set("horizontalGridlines", hgl);
  776. }
  777. }
  778. if(vgl)
  779. {
  780. if(bottomAxesCollection && bottomAxesCollection[0])
  781. {
  782. vAxis = bottomAxesCollection[0];
  783. }
  784. else if (topAxesCollection && topAxesCollection[0])
  785. {
  786. vAxis = topAxesCollection[0];
  787. }
  788. else
  789. {
  790. vAxis = direction === "vertical" ? catAxis : seriesAxesCollection[0];
  791. }
  792. if(!this._getBaseAttribute(vgl, "axis") && vAxis)
  793. {
  794. this._setBaseAttribute(vgl, "axis", vAxis);
  795. }
  796. if(this._getBaseAttribute(vgl, "axis"))
  797. {
  798. graph.set("verticalGridlines", vgl);
  799. }
  800. }
  801. },
  802. /**
  803. * Default Function for the axes attribute.
  804. *
  805. * @method _getDefaultAxes
  806. * @return Object
  807. * @private
  808. */
  809. _getDefaultAxes: function()
  810. {
  811. var axes;
  812. if(this.get("dataProvider"))
  813. {
  814. axes = this._parseAxes();
  815. }
  816. return axes;
  817. },
  818. /**
  819. * Generates and returns a key-indexed object containing `Axis` instances or objects used to create `Axis` instances.
  820. *
  821. * @method _parseAxes
  822. * @param {Object} axes Object containing `Axis` instances or `Axis` attributes.
  823. * @return Object
  824. * @private
  825. */
  826. _parseAxes: function(axes)
  827. {
  828. var catKey = this.get("categoryKey"),
  829. axis,
  830. attr,
  831. keys,
  832. newAxes = {},
  833. claimedKeys = [],
  834. newKeys = [],
  835. categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"),
  836. valueAxisName = this.get("valueAxisName"),
  837. seriesKeys = this.get("seriesKeys").concat(),
  838. i,
  839. l,
  840. ii,
  841. ll,
  842. cIndex,
  843. direction = this.get("direction"),
  844. seriesPosition,
  845. categoryPosition,
  846. valueAxes = [],
  847. seriesAxis = this.get("stacked") ? "stacked" : "numeric";
  848. if(direction === "vertical")
  849. {
  850. seriesPosition = "bottom";
  851. categoryPosition = "left";
  852. }
  853. else
  854. {
  855. seriesPosition = "left";
  856. categoryPosition = "bottom";
  857. }
  858. if(axes)
  859. {
  860. for(i in axes)
  861. {
  862. if(axes.hasOwnProperty(i))
  863. {
  864. axis = axes[i];
  865. keys = this._getBaseAttribute(axis, "keys");
  866. attr = this._getBaseAttribute(axis, "type");
  867. if(attr === "time" || attr === "category")
  868. {
  869. categoryAxisName = i;
  870. this.set("categoryAxisName", i);
  871. if(Y_Lang.isArray(keys) && keys.length > 0)
  872. {
  873. catKey = keys[0];
  874. this.set("categoryKey", catKey);
  875. }
  876. newAxes[i] = axis;
  877. }
  878. else if(i === categoryAxisName)
  879. {
  880. newAxes[i] = axis;
  881. }
  882. else
  883. {
  884. newAxes[i] = axis;
  885. if(i !== valueAxisName && keys && Y_Lang.isArray(keys))
  886. {
  887. ll = keys.length;
  888. for(ii = 0; ii < ll; ++ii)
  889. {
  890. claimedKeys.push(keys[ii]);
  891. }
  892. valueAxes.push(newAxes[i]);
  893. }
  894. if(!(this._getBaseAttribute(newAxes[i], "type")))
  895. {
  896. this._setBaseAttribute(newAxes[i], "type", seriesAxis);
  897. }
  898. if(!(this._getBaseAttribute(newAxes[i], "position")))
  899. {
  900. this._setBaseAttribute(
  901. newAxes[i],
  902. "position",
  903. this._getDefaultAxisPosition(newAxes[i], valueAxes, seriesPosition)
  904. );
  905. }
  906. }
  907. }
  908. }
  909. }
  910. cIndex = Y.Array.indexOf(seriesKeys, catKey);
  911. if(cIndex > -1)
  912. {
  913. seriesKeys.splice(cIndex, 1);
  914. }
  915. l = seriesKeys.length;
  916. for(i = 0; i < l; ++i)
  917. {
  918. cIndex = Y.Array.indexOf(claimedKeys, seriesKeys[i]);
  919. if(cIndex > -1)
  920. {
  921. newKeys = newKeys.concat(claimedKeys.splice(cIndex, 1));
  922. }
  923. }
  924. claimedKeys = newKeys.concat(claimedKeys);
  925. l = claimedKeys.length;
  926. for(i = 0; i < l; i = i + 1)
  927. {
  928. cIndex = Y.Array.indexOf(seriesKeys, claimedKeys[i]);
  929. if(cIndex > -1)
  930. {
  931. seriesKeys.splice(cIndex, 1);
  932. }
  933. }
  934. if(!newAxes.hasOwnProperty(categoryAxisName))
  935. {
  936. newAxes[categoryAxisName] = {};
  937. }
  938. if(!(this._getBaseAttribute(newAxes[categoryAxisName], "keys")))
  939. {
  940. this._setBaseAttribute(newAxes[categoryAxisName], "keys", [catKey]);
  941. }
  942. if(!(this._getBaseAttribute(newAxes[categoryAxisName], "position")))
  943. {
  944. this._setBaseAttribute(newAxes[categoryAxisName], "position", categoryPosition);
  945. }
  946. if(!(this._getBaseAttribute(newAxes[categoryAxisName], "type")))
  947. {
  948. this._setBaseAttribute(newAxes[categoryAxisName], "type", this.get("categoryType"));
  949. }
  950. if(!newAxes.hasOwnProperty(valueAxisName) && seriesKeys && seriesKeys.length > 0)
  951. {
  952. newAxes[valueAxisName] = {keys:seriesKeys};
  953. valueAxes.push(newAxes[valueAxisName]);
  954. }
  955. if(claimedKeys.length > 0)
  956. {
  957. if(seriesKeys.length > 0)
  958. {
  959. seriesKeys = claimedKeys.concat(seriesKeys);
  960. }
  961. else
  962. {
  963. seriesKeys = claimedKeys;
  964. }
  965. }
  966. if(newAxes.hasOwnProperty(valueAxisName))
  967. {
  968. if(!(this._getBaseAttribute(newAxes[valueAxisName], "position")))
  969. {
  970. this._setBaseAttribute(
  971. newAxes[valueAxisName],
  972. "position",
  973. this._getDefaultAxisPosition(newAxes[valueAxisName], valueAxes, seriesPosition)
  974. );
  975. }
  976. this._setBaseAttribute(newAxes[valueAxisName], "type", seriesAxis);
  977. this._setBaseAttribute(newAxes[valueAxisName], "keys", seriesKeys);
  978. }
  979. if(!this._wereSeriesKeysExplicitlySet())
  980. {
  981. this.set("seriesKeys", seriesKeys, {src: "internal"});
  982. }
  983. return newAxes;
  984. },
  985. /**
  986. * Determines the position of an axis when one is not specified.
  987. *
  988. * @method _getDefaultAxisPosition
  989. * @param {Axis} axis `Axis` instance.
  990. * @param {Array} valueAxes Array of `Axis` instances.
  991. * @param {String} position Default position depending on the direction of the chart and type of axis.
  992. * @return String
  993. * @private
  994. */
  995. _getDefaultAxisPosition: function(axis, valueAxes, position)
  996. {
  997. var direction = this.get("direction"),
  998. i = Y.Array.indexOf(valueAxes, axis);
  999. if(valueAxes[i - 1] && valueAxes[i - 1].position)
  1000. {
  1001. if(direction === "horizontal")
  1002. {
  1003. if(valueAxes[i - 1].position === "left")
  1004. {
  1005. position = "right";
  1006. }
  1007. else if(valueAxes[i - 1].position === "right")
  1008. {
  1009. position = "left";
  1010. }
  1011. }
  1012. else
  1013. {
  1014. if (valueAxes[i -1].position === "bottom")
  1015. {
  1016. position = "top";
  1017. }
  1018. else
  1019. {
  1020. position = "bottom";
  1021. }
  1022. }
  1023. }
  1024. return position;
  1025. },
  1026. /**
  1027. * Returns an object literal containing a categoryItem and a valueItem for a given series index. Below is the structure of each:
  1028. *
  1029. * @method getSeriesItems
  1030. * @param {CartesianSeries} series Reference to a series.
  1031. * @param {Number} index Index of the specified item within a series.
  1032. * @return Object An object literal containing the following:
  1033. *
  1034. * <dl>
  1035. * <dt>categoryItem</dt><dd>Object containing the following data related to the category axis of the series.
  1036. * <dl>
  1037. * <dt>axis</dt><dd>Reference to the category axis of the series.</dd>
  1038. * <dt>key</dt><dd>Category key for the series.</dd>
  1039. * <dt>value</dt><dd>Value on the axis corresponding to the series index.</dd>
  1040. * </dl>
  1041. * </dd>
  1042. * <dt>valueItem</dt><dd>Object containing the following data related to the category axis of the series.
  1043. * <dl>
  1044. * <dt>axis</dt><dd>Reference to the value axis of the series.</dd>
  1045. * <dt>key</dt><dd>Value key for the series.</dd>
  1046. * <dt>value</dt><dd>Value on the axis corresponding to the series index.</dd>
  1047. * </dl>
  1048. * </dd>
  1049. * </dl>
  1050. */
  1051. getSeriesItems: function(series, index)
  1052. {
  1053. var xAxis = series.get("xAxis"),
  1054. yAxis = series.get("yAxis"),
  1055. xKey = series.get("xKey"),
  1056. yKey = series.get("yKey"),
  1057. categoryItem,
  1058. valueItem;
  1059. if(this.get("direction") === "vertical")
  1060. {
  1061. categoryItem = {
  1062. axis:yAxis,
  1063. key:yKey,
  1064. value:yAxis.getKeyValueAt(yKey, index)
  1065. };
  1066. valueItem = {
  1067. axis:xAxis,
  1068. key:xKey,
  1069. value: xAxis.getKeyValueAt(xKey, index)
  1070. };
  1071. }
  1072. else
  1073. {
  1074. valueItem = {
  1075. axis:yAxis,
  1076. key:yKey,
  1077. value:yAxis.getKeyValueAt(yKey, index)
  1078. };
  1079. categoryItem = {
  1080. axis:xAxis,
  1081. key:xKey,
  1082. value: xAxis.getKeyValueAt(xKey, index)
  1083. };
  1084. }
  1085. categoryItem.displayName = series.get("categoryDisplayName");
  1086. valueItem.displayName = series.get("valueDisplayName");
  1087. categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index);
  1088. valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index);
  1089. return {category:categoryItem, value:valueItem};
  1090. },
  1091. /**
  1092. * Handler for sizeChanged event.
  1093. *
  1094. * @method _sizeChanged
  1095. * @param {Object} e Event object.
  1096. * @private
  1097. */
  1098. _sizeChanged: function()
  1099. {
  1100. if(this._axesCollection)
  1101. {
  1102. var ac = this._axesCollection,
  1103. i = 0,
  1104. l = ac.length;
  1105. for(; i < l; ++i)
  1106. {
  1107. this._addToAxesRenderQueue(ac[i]);
  1108. }
  1109. this._redraw();
  1110. }
  1111. },
  1112. /**
  1113. * Returns the maximum distance in pixels that the extends outside the top bounds of all vertical axes.
  1114. *
  1115. * @method _getTopOverflow
  1116. * @param {Array} set1 Collection of axes to check.
  1117. * @param {Array} set2 Seconf collection of axes to check.
  1118. * @param {Number} width Width of the axes
  1119. * @return Number
  1120. * @private
  1121. */
  1122. _getTopOverflow: function(set1, set2, height)
  1123. {
  1124. var i = 0,
  1125. len,
  1126. overflow = 0,
  1127. axis;
  1128. if(set1)
  1129. {
  1130. len = set1.length;
  1131. for(; i < len; ++i)
  1132. {
  1133. axis = set1[i];
  1134. overflow = Math.max(
  1135. overflow,
  1136. Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
  1137. );
  1138. }
  1139. }
  1140. if(set2)
  1141. {
  1142. i = 0;
  1143. len = set2.length;
  1144. for(; i < len; ++i)
  1145. {
  1146. axis = set2[i];
  1147. overflow = Math.max(
  1148. overflow,
  1149. Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
  1150. );
  1151. }
  1152. }
  1153. return overflow;
  1154. },
  1155. /**
  1156. * Returns the maximum distance in pixels that the extends outside the right bounds of all horizontal axes.
  1157. *
  1158. * @method _getRightOverflow
  1159. * @param {Array} set1 Collection of axes to check.
  1160. * @param {Array} set2 Seconf collection of axes to check.
  1161. * @param {Number} width Width of the axes
  1162. * @return Number
  1163. * @private
  1164. */
  1165. _getRightOverflow: function(set1, set2, width)
  1166. {
  1167. var i = 0,
  1168. len,
  1169. overflow = 0,
  1170. axis;
  1171. if(set1)
  1172. {
  1173. len = set1.length;
  1174. for(; i < len; ++i)
  1175. {
  1176. axis = set1[i];
  1177. overflow = Math.max(
  1178. overflow,
  1179. axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
  1180. );
  1181. }
  1182. }
  1183. if(set2)
  1184. {
  1185. i = 0;
  1186. len = set2.length;
  1187. for(; i < len; ++i)
  1188. {
  1189. axis = set2[i];
  1190. overflow = Math.max(
  1191. overflow,
  1192. axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
  1193. );
  1194. }
  1195. }
  1196. return overflow;
  1197. },
  1198. /**
  1199. * Returns the maximum distance in pixels that the extends outside the left bounds of all horizontal axes.
  1200. *
  1201. * @method _getLeftOverflow
  1202. * @param {Array} set1 Collection of axes to check.
  1203. * @param {Array} set2 Seconf collection of axes to check.
  1204. * @param {Number} width Width of the axes
  1205. * @return Number
  1206. * @private
  1207. */
  1208. _getLeftOverflow: function(set1, set2, width)
  1209. {
  1210. var i = 0,
  1211. len,
  1212. overflow = 0,
  1213. axis;
  1214. if(set1)
  1215. {
  1216. len = set1.length;
  1217. for(; i < len; ++i)
  1218. {
  1219. axis = set1[i];
  1220. overflow = Math.max(
  1221. overflow,
  1222. Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
  1223. );
  1224. }
  1225. }
  1226. if(set2)
  1227. {
  1228. i = 0;
  1229. len = set2.length;
  1230. for(; i < len; ++i)
  1231. {
  1232. axis = set2[i];
  1233. overflow = Math.max(
  1234. overflow,
  1235. Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width)
  1236. );
  1237. }
  1238. }
  1239. return overflow;
  1240. },
  1241. /**
  1242. * Returns the maximum distance in pixels that the extends outside the bottom bounds of all vertical axes.
  1243. *
  1244. * @method _getBottomOverflow
  1245. * @param {Array} set1 Collection of axes to check.
  1246. * @param {Array} set2 Seconf collection of axes to check.
  1247. * @param {Number} height Height of the axes
  1248. * @return Number
  1249. * @private
  1250. */
  1251. _getBottomOverflow: function(set1, set2, height)
  1252. {
  1253. var i = 0,
  1254. len,
  1255. overflow = 0,
  1256. axis;
  1257. if(set1)
  1258. {
  1259. len = set1.length;
  1260. for(; i < len; ++i)
  1261. {
  1262. axis = set1[i];
  1263. overflow = Math.max(
  1264. overflow,
  1265. axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
  1266. );
  1267. }
  1268. }
  1269. if(set2)
  1270. {
  1271. i = 0;
  1272. len = set2.length;
  1273. for(; i < len; ++i)
  1274. {
  1275. axis = set2[i];
  1276. overflow = Math.max(
  1277. overflow,
  1278. axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height)
  1279. );
  1280. }
  1281. }
  1282. return overflow;
  1283. },
  1284. /**
  1285. * Redraws and position all the components of the chart instance.
  1286. *
  1287. * @method _redraw
  1288. * @private
  1289. */
  1290. _redraw: function()
  1291. {
  1292. if(this._drawing)
  1293. {
  1294. this._callLater = true;
  1295. return;
  1296. }
  1297. this._drawing = true;
  1298. this._callLater = false;
  1299. var w = this.get("width"),
  1300. h = this.get("height"),
  1301. leftPaneWidth = 0,
  1302. rightPaneWidth = 0,
  1303. topPaneHeight = 0,
  1304. bottomPaneHeight = 0,
  1305. leftAxesCollection = this.get("leftAxesCollection"),
  1306. rightAxesCollection = this.get("rightAxesCollection"),
  1307. topAxesCollection = this.get("topAxesCollection"),
  1308. bottomAxesCollection = this.get("bottomAxesCollection"),
  1309. i = 0,
  1310. l,
  1311. axis,
  1312. graphOverflow = "visible",
  1313. graph = this.get("graph"),
  1314. topOverflow,
  1315. bottomOverflow,
  1316. leftOverflow,
  1317. rightOverflow,
  1318. graphWidth,
  1319. graphHeight,
  1320. graphX,
  1321. graphY,
  1322. allowContentOverflow = this.get("allowContentOverflow"),
  1323. diff,
  1324. rightAxesXCoords,
  1325. leftAxesXCoords,
  1326. topAxesYCoords,
  1327. bottomAxesYCoords,
  1328. graphRect = {};
  1329. if(leftAxesCollection)
  1330. {
  1331. leftAxesXCoords = [];
  1332. l = leftAxesCollection.length;
  1333. for(i = l - 1; i > -1; --i)
  1334. {
  1335. leftAxesXCoords.unshift(leftPaneWidth);
  1336. leftPaneWidth += leftAxesCollection[i].get("width");
  1337. }
  1338. }
  1339. if(rightAxesCollection)
  1340. {
  1341. rightAxesXCoords = [];
  1342. l = rightAxesCollection.length;
  1343. i = 0;
  1344. for(i = l - 1; i > -1; --i)
  1345. {
  1346. rightPaneWidth += rightAxesCollection[i].get("width");
  1347. rightAxesXCoords.unshift(w - rightPaneWidth);
  1348. }
  1349. }
  1350. if(topAxesCollection)
  1351. {
  1352. topAxesYCoords = [];
  1353. l = topAxesCollection.length;
  1354. for(i = l - 1; i > -1; --i)
  1355. {
  1356. topAxesYCoords.unshift(topPaneHeight);
  1357. topPaneHeight += topAxesCollection[i].get("height");
  1358. }
  1359. }
  1360. if(bottomAxesCollection)
  1361. {
  1362. bottomAxesYCoords = [];
  1363. l = bottomAxesCollection.length;
  1364. for(i = l - 1; i > -1; --i)
  1365. {
  1366. bottomPaneHeight += bottomAxesCollection[i].get("height");
  1367. bottomAxesYCoords.unshift(h - bottomPaneHeight);
  1368. }
  1369. }
  1370. graphWidth = w - (leftPaneWidth + rightPaneWidth);
  1371. graphHeight = h - (bottomPaneHeight + topPaneHeight);
  1372. graphRect.left = leftPaneWidth;
  1373. graphRect.top = topPaneHeight;
  1374. graphRect.bottom = h - bottomPaneHeight;
  1375. graphRect.right = w - rightPaneWidth;
  1376. if(!allowContentOverflow)
  1377. {
  1378. topOverflow = this._getTopOverflow(leftAxesCollection, rightAxesCollection);
  1379. bottomOverflow = this._getBottomOverflow(leftAxesCollection, rightAxesCollection);
  1380. leftOverflow = this._getLeftOverflow(bottomAxesCollection, topAxesCollection);
  1381. rightOverflow = this._getRightOverflow(bottomAxesCollection, topAxesCollection);
  1382. diff = topOverflow - topPaneHeight;
  1383. if(diff > 0)
  1384. {
  1385. graphRect.top = topOverflow;
  1386. if(topAxesYCoords)
  1387. {
  1388. i = 0;
  1389. l = topAxesYCoords.length;
  1390. for(; i < l; ++i)
  1391. {
  1392. topAxesYCoords[i] += diff;
  1393. }
  1394. }
  1395. }
  1396. diff = bottomOverflow - bottomPaneHeight;
  1397. if(diff > 0)
  1398. {
  1399. graphRect.bottom = h - bottomOverflow;
  1400. if(bottomAxesYCoords)
  1401. {
  1402. i = 0;
  1403. l = bottomAxesYCoords.length;
  1404. for(; i < l; ++i)
  1405. {
  1406. bottomAxesYCoords[i] -= diff;
  1407. }
  1408. }
  1409. }
  1410. diff = leftOverflow - leftPaneWidth;
  1411. if(diff > 0)
  1412. {
  1413. graphRect.left = leftOverflow;
  1414. if(leftAxesXCoords)
  1415. {
  1416. i = 0;
  1417. l = leftAxesXCoords.length;
  1418. for(; i < l; ++i)
  1419. {
  1420. leftAxesXCoords[i] += diff;
  1421. }
  1422. }
  1423. }
  1424. diff = rightOverflow - rightPaneWidth;
  1425. if(diff > 0)
  1426. {
  1427. graphRect.right = w - rightOverflow;
  1428. if(rightAxesXCoords)
  1429. {
  1430. i = 0;
  1431. l = rightAxesXCoords.length;
  1432. for(; i < l; ++i)
  1433. {
  1434. rightAxesXCoords[i] -= diff;
  1435. }
  1436. }
  1437. }
  1438. }
  1439. graphWidth = graphRect.right - graphRect.left;
  1440. graphHeight = graphRect.bottom - graphRect.top;
  1441. graphX = graphRect.left;
  1442. graphY = graphRect.top;
  1443. if(topAxesCollection)
  1444. {
  1445. l = topAxesCollection.length;
  1446. i = 0;
  1447. for(; i < l; i++)
  1448. {
  1449. axis = topAxesCollection[i];
  1450. if(axis.get("width") !== graphWidth)
  1451. {
  1452. axis.set("width", graphWidth);
  1453. }
  1454. axis.get("boundingBox").setStyle("left", graphX + "px");
  1455. axis.get("boundingBox").setStyle("top", topAxesYCoords[i] + "px");
  1456. }
  1457. if(axis._hasDataOverflow())
  1458. {
  1459. graphOverflow = "hidden";
  1460. }
  1461. }
  1462. if(bottomAxesCollection)
  1463. {
  1464. l = bottomAxesCollection.length;
  1465. i = 0;
  1466. for(; i < l; i++)
  1467. {
  1468. axis = bottomAxesCollection[i];
  1469. if(axis.get("width") !== graphWidth)
  1470. {
  1471. axis.set("width", graphWidth);
  1472. }
  1473. axis.get("boundingBox").setStyle("left", graphX + "px");
  1474. axis.get("boundingBox").setStyle("top", bottomAxesYCoords[i] + "px");
  1475. }
  1476. if(axis._hasDataOverflow())
  1477. {
  1478. graphOverflow = "hidden";
  1479. }
  1480. }
  1481. if(leftAxesCollection)
  1482. {
  1483. l = leftAxesCollection.length;
  1484. i = 0;
  1485. for(; i < l; ++i)
  1486. {
  1487. axis = leftAxesCollection[i];
  1488. axis.get("boundingBox").setStyle("top", graphY + "px");
  1489. axis.get("boundingBox").setStyle("left", leftAxesXCoords[i] + "px");
  1490. if(axis.get("height") !== graphHeight)
  1491. {
  1492. axis.set("height", graphHeight);
  1493. }
  1494. }
  1495. if(axis._hasDataOverflow())
  1496. {
  1497. graphOverflow = "hidden";
  1498. }
  1499. }
  1500. if(rightAxesCollection)
  1501. {
  1502. l = rightAxesCollection.length;
  1503. i = 0;
  1504. for(; i < l; ++i)
  1505. {
  1506. axis = rightAxesCollection[i];
  1507. axis.get("boundingBox").setStyle("top", graphY + "px");
  1508. axis.get("boundingBox").setStyle("left", rightAxesXCoords[i] + "px");
  1509. if(axis.get("height") !== graphHeight)
  1510. {
  1511. axis.set("height", graphHeight);
  1512. }
  1513. }
  1514. if(axis._hasDataOverflow())
  1515. {
  1516. graphOverflow = "hidden";
  1517. }
  1518. }
  1519. this._drawing = false;
  1520. if(this._callLater)
  1521. {
  1522. this._redraw();
  1523. return;
  1524. }
  1525. if(graph)
  1526. {
  1527. graph.get("boundingBox").setStyle("left", graphX + "px");
  1528. graph.get("boundingBox").setStyle("top", graphY + "px");
  1529. graph.set("width", graphWidth);
  1530. graph.set("height", graphHeight);
  1531. graph.get("boundingBox").setStyle("overflow", graphOverflow);
  1532. }
  1533. if(this._overlay)
  1534. {
  1535. this._overlay.setStyle("left", graphX + "px");
  1536. this._overlay.setStyle("top", graphY + "px");
  1537. this._overlay.setStyle("width", graphWidth + "px");
  1538. this._overlay.setStyle("height", graphHeight + "px");
  1539. }
  1540. },
  1541. /**
  1542. * Destructor implementation for the CartesianChart class. Calls destroy on all axes, series and the Graph instance.
  1543. * Removes the tooltip and overlay HTML elements.
  1544. *
  1545. * @method destructor
  1546. * @protected
  1547. */
  1548. destructor: function()
  1549. {
  1550. var graph = this.get("graph"),
  1551. i = 0,
  1552. len,
  1553. seriesCollection = this.get("seriesCollection"),
  1554. axesCollection = this._axesCollection,
  1555. tooltip = this.get("tooltip").node;
  1556. if(this._description)
  1557. {
  1558. this._description.empty();
  1559. this._description.remove(true);
  1560. }
  1561. if(this._liveRegion)
  1562. {
  1563. this._liveRegion.empty();
  1564. this._liveRegion.remove(true);
  1565. }
  1566. len = seriesCollection ? seriesCollection.length : 0;
  1567. for(; i < len; ++i)
  1568. {
  1569. if(seriesCollection[i] instanceof Y.CartesianSeries)
  1570. {
  1571. seriesCollection[i].destroy(true);
  1572. }
  1573. }
  1574. len = axesCollection ? axesCollection.length : 0;
  1575. for(i = 0; i < len; ++i)
  1576. {
  1577. if(axesCollection[i] instanceof Y.Axis)
  1578. {
  1579. axesCollection[i].destroy(true);
  1580. }
  1581. }
  1582. if(graph)
  1583. {
  1584. graph.destroy(true);
  1585. }
  1586. if(tooltip)
  1587. {
  1588. tooltip.empty();
  1589. tooltip.remove(true);
  1590. }
  1591. if(this._overlay)
  1592. {
  1593. this._overlay.empty();
  1594. this._overlay.remove(true);
  1595. }
  1596. },
  1597. /**
  1598. * Returns the appropriate message based on the key press.
  1599. *
  1600. * @method _getAriaMessage
  1601. * @param {Number} key The keycode that was pressed.
  1602. * @return String
  1603. */
  1604. _getAriaMessage: function(key)
  1605. {
  1606. var msg = "",
  1607. series,
  1608. items,
  1609. categoryItem,
  1610. valueItem,
  1611. seriesIndex = this._seriesIndex,
  1612. itemIndex = this._itemIndex,
  1613. seriesCollection = this.get("seriesCollection"),
  1614. len = seriesCollection.length,
  1615. dataLength;
  1616. if(key % 2 === 0)
  1617. {
  1618. if(len > 1)
  1619. {
  1620. if(key === 38)
  1621. {
  1622. seriesIndex = seriesIndex < 1 ? len - 1 : seriesIndex - 1;
  1623. }
  1624. else if(key === 40)
  1625. {
  1626. seriesIndex = seriesIndex >= len - 1 ? 0 : seriesIndex + 1;
  1627. }
  1628. this._itemIndex = -1;
  1629. }
  1630. else
  1631. {
  1632. seriesIndex = 0;
  1633. }
  1634. this._seriesIndex = seriesIndex;
  1635. series = this.getSeries(parseInt(seriesIndex, 10));
  1636. msg = series.get("valueDisplayName") + " series.";
  1637. }
  1638. else
  1639. {
  1640. if(seriesIndex > -1)
  1641. {
  1642. msg = "";
  1643. series = this.getSeries(parseInt(seriesIndex, 10));
  1644. }
  1645. else
  1646. {
  1647. seriesIndex = 0;
  1648. this._seriesIndex = seriesIndex;
  1649. series = this.getSeries(parseInt(seriesIndex, 10));
  1650. msg = series.get("valueDisplayName") + " series.";
  1651. }
  1652. dataLength = series._dataLength ? series._dataLength : 0;
  1653. if(key === 37)
  1654. {
  1655. itemIndex = itemIndex > 0 ? itemIndex - 1 : dataLength - 1;
  1656. }
  1657. else if(key === 39)
  1658. {
  1659. itemIndex = itemIndex >= dataLength - 1 ? 0 : itemIndex + 1;
  1660. }
  1661. this._itemIndex = itemIndex;
  1662. items = this.getSeriesItems(series, itemIndex);
  1663. categoryItem = items.category;
  1664. valueItem = items.value;
  1665. if(categoryItem && valueItem && categoryItem.value && valueItem.value)
  1666. {
  1667. msg += categoryItem.displayName +
  1668. ": " +
  1669. categoryItem.axis.formatLabel.apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) +
  1670. ", ";
  1671. msg += valueItem.displayName +
  1672. ": " +
  1673. valueItem.axis.formatLabel.apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]) +
  1674. ", ";
  1675. }
  1676. else
  1677. {
  1678. msg += "No data available.";
  1679. }
  1680. msg += (itemIndex + 1) + " of " + dataLength + ". ";
  1681. }
  1682. return msg;
  1683. }
  1684. }, {
  1685. ATTRS: {
  1686. /**
  1687. * Indicates whether axis labels are allowed to overflow beyond the bounds of the chart's content box.
  1688. *
  1689. * @attribute allowContentOverflow
  1690. * @type Boolean
  1691. */
  1692. allowContentOverflow: {
  1693. value: false
  1694. },
  1695. /**
  1696. * Style object for the axes.
  1697. *
  1698. * @attribute axesStyles
  1699. * @type Object
  1700. * @private
  1701. */
  1702. axesStyles: {
  1703. lazyAdd: false,
  1704. getter: function()
  1705. {
  1706. var axes = this.get("axes"),
  1707. i,
  1708. styles = this._axesStyles;
  1709. if(axes)
  1710. {
  1711. for(i in axes)
  1712. {
  1713. if(axes.hasOwnProperty(i) && axes[i] instanceof Y.Axis)
  1714. {
  1715. if(!styles)
  1716. {
  1717. styles = {};
  1718. }
  1719. styles[i] = axes[i].get("styles");
  1720. }
  1721. }
  1722. }
  1723. return styles;
  1724. },
  1725. setter: function(val)
  1726. {
  1727. var axes = this.get("axes"),
  1728. i;
  1729. for(i in val)
  1730. {
  1731. if(val.hasOwnProperty(i) && axes.hasOwnProperty(i))
  1732. {
  1733. this._setBaseAttribute(axes[i], "styles", val[i]);
  1734. }
  1735. }
  1736. return val;
  1737. }
  1738. },
  1739. /**
  1740. * Style object for the series
  1741. *
  1742. * @attribute seriesStyles
  1743. * @type Object
  1744. * @private
  1745. */
  1746. seriesStyles: {
  1747. lazyAdd: false,
  1748. getter: function()
  1749. {
  1750. var styles = this._seriesStyles,
  1751. graph = this.get("graph"),
  1752. dict,
  1753. i;
  1754. if(graph)
  1755. {
  1756. dict = graph.get("seriesDictionary");
  1757. if(dict)
  1758. {
  1759. styles = {};
  1760. for(i in dict)
  1761. {
  1762. if(dict.hasOwnProperty(i))
  1763. {
  1764. styles[i] = dict[i].get("styles");
  1765. }
  1766. }
  1767. }
  1768. }
  1769. return styles;
  1770. },
  1771. setter: function(val)
  1772. {
  1773. var i,
  1774. l,
  1775. s;
  1776. if(Y_Lang.isArray(val))
  1777. {
  1778. s = this.get("seriesCollection");
  1779. i = 0;
  1780. l = val.length;
  1781. for(; i < l; ++i)
  1782. {
  1783. this._setBaseAttribute(s[i], "styles", val[i]);
  1784. }
  1785. }
  1786. else
  1787. {
  1788. for(i in val)
  1789. {
  1790. if(val.hasOwnProperty(i))
  1791. {
  1792. s = this.getSeries(i);
  1793. this._setBaseAttribute(s, "styles", val[i]);
  1794. }
  1795. }
  1796. }
  1797. return val;
  1798. }
  1799. },
  1800. /**
  1801. * Styles for the graph.
  1802. *
  1803. * @attribute graphStyles
  1804. * @type Object
  1805. * @private
  1806. */
  1807. graphStyles: {
  1808. lazyAdd: false,
  1809. getter: function()
  1810. {
  1811. var graph = this.get("graph");
  1812. if(graph)
  1813. {
  1814. return(graph.get("styles"));
  1815. }
  1816. return this._graphStyles;
  1817. },
  1818. setter: function(val)
  1819. {
  1820. var graph = this.get("graph");
  1821. this._setBaseAttribute(graph, "styles", val);
  1822. return val;
  1823. }
  1824. },
  1825. /**
  1826. * Style properties for the chart. Contains a key indexed hash of the following:
  1827. * <dl>
  1828. * <dt>series</dt><dd>A key indexed hash containing references to the `styles` attribute for each series in the chart.
  1829. * Specific style attributes vary depending on the series:
  1830. * <ul>
  1831. * <li><a href="AreaSeries.html#attr_styles">AreaSeries</a></li>
  1832. * <li><a href="BarSeries.html#attr_styles">BarSeries</a></li>
  1833. * <li><a href="ColumnSeries.html#attr_styles">ColumnSeries</a></li>
  1834. * <li><a href="ComboSeries.html#attr_styles">ComboSeries</a></li>
  1835. * <li><a href="LineSeries.html#attr_styles">LineSeries</a></li>
  1836. * <li><a href="MarkerSeries.html#attr_styles">MarkerSeries</a></li>
  1837. * <li><a href="SplineSeries.html#attr_styles">SplineSeries</a></li>
  1838. * </ul>
  1839. * </dd>
  1840. * <dt>axes</dt><dd>A key indexed hash containing references to the `styles` attribute for each axes in the chart. Specific
  1841. * style attributes can be found in the <a href="Axis.html#attr_styles">Axis</a> class.</dd>
  1842. * <dt>graph</dt><dd>A reference to the `styles` attribute in the chart. Specific style attributes can be found in the
  1843. * <a href="Graph.html#attr_styles">Graph</a> class.</dd>
  1844. * </dl>
  1845. *
  1846. * @attribute styles
  1847. * @type Object
  1848. */
  1849. styles: {
  1850. lazyAdd: false,
  1851. getter: function()
  1852. {
  1853. var styles = {
  1854. axes: this.get("axesStyles"),
  1855. series: this.get("seriesStyles"),
  1856. graph: this.get("graphStyles")
  1857. };
  1858. return styles;
  1859. },
  1860. setter: function(val)
  1861. {
  1862. if(val.hasOwnProperty("axes"))
  1863. {
  1864. if(this.get("axesStyles"))
  1865. {
  1866. this.set("axesStyles", val.axes);
  1867. }
  1868. else
  1869. {
  1870. this._axesStyles = val.axes;
  1871. }
  1872. }
  1873. if(val.hasOwnProperty("series"))
  1874. {
  1875. if(this.get("seriesStyles"))
  1876. {
  1877. this.set("seriesStyles", val.series);
  1878. }
  1879. else
  1880. {
  1881. this._seriesStyles = val.series;
  1882. }
  1883. }
  1884. if(val.hasOwnProperty("graph"))
  1885. {
  1886. this.set("graphStyles", val.graph);
  1887. }
  1888. }
  1889. },
  1890. /**
  1891. * Axes to appear in the chart. This can be a key indexed hash of axis instances or object literals
  1892. * used to construct the appropriate axes.
  1893. *
  1894. * @attribute axes
  1895. * @type Object
  1896. */
  1897. axes: {
  1898. lazyAdd: false,
  1899. valueFn: "_getDefaultAxes",
  1900. setter: function(val)
  1901. {
  1902. if(this.get("dataProvider"))
  1903. {
  1904. val = this._setAxes(val);
  1905. }
  1906. return val;
  1907. }
  1908. },
  1909. /**
  1910. * Collection of series to appear on the chart. This can be an array of Series instances or object literals
  1911. * used to construct the appropriate series.
  1912. *
  1913. * @attribute seriesCollection
  1914. * @type Array
  1915. */
  1916. seriesCollection: {
  1917. lazyAdd: false,
  1918. valueFn: "_getDefaultSeriesCollection",
  1919. setter: function(val)
  1920. {
  1921. if(this.get("dataProvider"))
  1922. {
  1923. return this._parseSeriesCollection(val);
  1924. }
  1925. return val;
  1926. }
  1927. },
  1928. /**
  1929. * Reference to the left-aligned axes for the chart.
  1930. *
  1931. * @attribute leftAxesCollection
  1932. * @type Array
  1933. * @private
  1934. */
  1935. leftAxesCollection: {},
  1936. /**
  1937. * Reference to the bottom-aligned axes for the chart.
  1938. *
  1939. * @attribute bottomAxesCollection
  1940. * @type Array
  1941. * @private
  1942. */
  1943. bottomAxesCollection: {},
  1944. /**
  1945. * Reference to the right-aligned axes for the chart.
  1946. *
  1947. * @attribute rightAxesCollection
  1948. * @type Array
  1949. * @private
  1950. */
  1951. rightAxesCollection: {},
  1952. /**
  1953. * Reference to the top-aligned axes for the chart.
  1954. *
  1955. * @attribute topAxesCollection
  1956. * @type Array
  1957. * @private
  1958. */
  1959. topAxesCollection: {},
  1960. /**
  1961. * Indicates whether or not the chart is stacked.
  1962. *
  1963. * @attribute stacked
  1964. * @type Boolean
  1965. */
  1966. stacked: {
  1967. value: false
  1968. },
  1969. /**
  1970. * Direction of chart's category axis when there is no series collection specified. Charts can
  1971. * be horizontal or vertical. When the chart type is column, the chart is horizontal.
  1972. * When the chart type is bar, the chart is vertical.
  1973. *
  1974. * @attribute direction
  1975. * @type String
  1976. */
  1977. direction: {
  1978. getter: function()
  1979. {
  1980. var type = this.get("type");
  1981. if(type === "bar")
  1982. {
  1983. return "vertical";
  1984. }
  1985. else if(type === "column")
  1986. {
  1987. return "horizontal";
  1988. }
  1989. return this._direction;
  1990. },
  1991. setter: function(val)
  1992. {
  1993. this._direction = val;
  1994. return this._direction;
  1995. }
  1996. },
  1997. /**
  1998. * Indicates whether or not an area is filled in a combo chart.
  1999. *
  2000. * @attribute showAreaFill
  2001. * @type Boolean
  2002. */
  2003. showAreaFill: {},
  2004. /**
  2005. * Indicates whether to display markers in a combo chart.
  2006. *
  2007. * @attribute showMarkers
  2008. * @type Boolean
  2009. */
  2010. showMarkers:{},
  2011. /**
  2012. * Indicates whether to display lines in a combo chart.
  2013. *
  2014. * @attribute showLines
  2015. * @type Boolean
  2016. */
  2017. showLines:{},
  2018. /**
  2019. * Indicates the key value used to identify a category axis in the `axes` hash. If
  2020. * not specified, the categoryKey attribute value will be used.
  2021. *
  2022. * @attribute categoryAxisName
  2023. * @type String
  2024. */
  2025. categoryAxisName: {
  2026. },
  2027. /**
  2028. * Indicates the key value used to identify a the series axis when an axis not generated.
  2029. *
  2030. * @attribute valueAxisName
  2031. * @type String
  2032. */
  2033. valueAxisName: {
  2034. value: "values"
  2035. },
  2036. /**
  2037. * Reference to the horizontalGridlines for the chart.
  2038. *
  2039. * @attribute horizontalGridlines
  2040. * @type Gridlines
  2041. */
  2042. horizontalGridlines: {
  2043. getter: function()
  2044. {
  2045. var graph = this.get("graph");
  2046. if(graph)
  2047. {
  2048. return graph.get("horizontalGridlines");
  2049. }
  2050. return this._horizontalGridlines;
  2051. },
  2052. setter: function(val)
  2053. {
  2054. var graph = this.get("graph");
  2055. if(val && !Y_Lang.isObject(val))
  2056. {
  2057. val = {};
  2058. }
  2059. if(graph)
  2060. {
  2061. graph.set("horizontalGridlines", val);
  2062. }
  2063. else
  2064. {
  2065. this._horizontalGridlines = val;
  2066. }
  2067. }
  2068. },
  2069. /**
  2070. * Reference to the verticalGridlines for the chart.
  2071. *
  2072. * @attribute verticalGridlines
  2073. * @type Gridlines
  2074. */
  2075. verticalGridlines: {
  2076. getter: function()
  2077. {
  2078. var graph = this.get("graph");
  2079. if(graph)
  2080. {
  2081. return graph.get("verticalGridlines");
  2082. }
  2083. return this._verticalGridlines;
  2084. },
  2085. setter: function(val)
  2086. {
  2087. var graph = this.get("graph");
  2088. if(val && !Y_Lang.isObject(val))
  2089. {
  2090. val = {};
  2091. }
  2092. if(graph)
  2093. {
  2094. graph.set("verticalGridlines", val);
  2095. }
  2096. else
  2097. {
  2098. this._verticalGridlines = val;
  2099. }
  2100. }
  2101. },
  2102. /**
  2103. * Type of chart when there is no series collection specified.
  2104. *
  2105. * @attribute type
  2106. * @type String
  2107. */
  2108. type: {
  2109. getter: function()
  2110. {
  2111. if(this.get("stacked"))
  2112. {
  2113. return "stacked" + this._type;
  2114. }
  2115. return this._type;
  2116. },
  2117. setter: function(val)
  2118. {
  2119. if(this._type === "bar")
  2120. {
  2121. if(val !== "bar")
  2122. {
  2123. this.set("direction", "horizontal");
  2124. }
  2125. }
  2126. else
  2127. {
  2128. if(val === "bar")
  2129. {
  2130. this.set("direction", "vertical");
  2131. }
  2132. }
  2133. this._type = val;
  2134. return this._type;
  2135. }
  2136. },
  2137. /**
  2138. * Reference to the category axis used by the chart.
  2139. *
  2140. * @attribute categoryAxis
  2141. * @type Axis
  2142. */
  2143. categoryAxis:{}
  2144. }
  2145. });