[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('charts-base', function (Y, NAME) { 9 10 /** 11 * Provides functionality for creating charts. 12 * 13 * @module charts 14 * @submodule charts-base 15 */ 16 var CONFIG = Y.config, 17 WINDOW = CONFIG.win, 18 DOCUMENT = CONFIG.doc, 19 Y_Lang = Y.Lang, 20 IS_STRING = Y_Lang.isString, 21 _getClassName = Y.ClassNameManager.getClassName, 22 SERIES_MARKER = _getClassName("seriesmarker"); 23 24 /** 25 * Gridlines draws gridlines on a Graph. 26 * 27 * @class Gridlines 28 * @constructor 29 * @extends Base 30 * @uses Renderer 31 * @param {Object} config (optional) Configuration parameters. 32 * @submodule charts-base 33 */ 34 Y.Gridlines = Y.Base.create("gridlines", Y.Base, [Y.Renderer], { 35 /** 36 * Reference to the `Path` element used for drawing Gridlines. 37 * 38 * @property _path 39 * @type Path 40 * @private 41 */ 42 _path: null, 43 44 /** 45 * Removes the Gridlines. 46 * 47 * @method remove 48 * @private 49 */ 50 remove: function() 51 { 52 var path = this._path; 53 if(path) 54 { 55 path.destroy(); 56 } 57 }, 58 59 /** 60 * Draws the gridlines 61 * 62 * @method draw 63 * @protected 64 */ 65 draw: function() 66 { 67 if(this.get("axis") && this.get("graph")) 68 { 69 this._drawGridlines(); 70 } 71 }, 72 73 /** 74 * Algorithm for drawing gridlines 75 * 76 * @method _drawGridlines 77 * @private 78 */ 79 _drawGridlines: function() 80 { 81 var path, 82 axis = this.get("axis"), 83 axisPosition = axis.get("position"), 84 points, 85 i = 0, 86 l, 87 direction = this.get("direction"), 88 graph = this.get("graph"), 89 w = graph.get("width"), 90 h = graph.get("height"), 91 line = this.get("styles").line, 92 color = line.color, 93 weight = line.weight, 94 alpha = line.alpha, 95 count = this.get("count"), 96 length, 97 lineFunction; 98 if(isFinite(w) && isFinite(h) && w > 0 && h > 0) 99 { 100 if(count && Y.Lang.isNumber(count)) 101 { 102 points = this._getPoints(count, w, h); 103 } 104 else if(axisPosition !== "none" && axis && axis.get("tickPoints")) 105 { 106 points = axis.get("tickPoints"); 107 } 108 else 109 { 110 points = this._getPoints(axis.get("styles").majorUnit.count, w, h); 111 } 112 l = points.length; 113 path = graph.get("gridlines"); 114 path.set("width", w); 115 path.set("height", h); 116 path.set("stroke", { 117 weight: weight, 118 color: color, 119 opacity: alpha 120 }); 121 if(direction === "vertical") 122 { 123 lineFunction = this._verticalLine; 124 length = h; 125 } 126 else 127 { 128 lineFunction = this._horizontalLine; 129 length = w; 130 } 131 for(i = 0; i < l; i = i + 1) 132 { 133 lineFunction(path, points[i], length); 134 } 135 path.end(); 136 } 137 }, 138 139 /** 140 * Calculates the coordinates for the gridlines based on a count. 141 * 142 * @method _getPoints 143 * @param {Number} count Number of gridlines 144 * @return Array 145 * @private 146 */ 147 _getPoints: function(count, w, h) 148 { 149 var i, 150 points = [], 151 multiplier, 152 divisor = count - 1; 153 for(i = 0; i < count; i = i + 1) 154 { 155 multiplier = i/divisor; 156 points[i] = { 157 x: w * multiplier, 158 y: h * multiplier 159 }; 160 } 161 return points; 162 }, 163 164 /** 165 * Algorithm for horizontal lines. 166 * 167 * @method _horizontalLine 168 * @param {Path} path Reference to path element 169 * @param {Object} pt Coordinates corresponding to a major unit of an axis. 170 * @param {Number} w Width of the Graph 171 * @private 172 */ 173 _horizontalLine: function(path, pt, w) 174 { 175 path.moveTo(0, pt.y); 176 path.lineTo(w, pt.y); 177 }, 178 179 /** 180 * Algorithm for vertical lines. 181 * 182 * @method _verticalLine 183 * @param {Path} path Reference to path element 184 * @param {Object} pt Coordinates corresponding to a major unit of an axis. 185 * @param {Number} h Height of the Graph 186 * @private 187 */ 188 _verticalLine: function(path, pt, h) 189 { 190 path.moveTo(pt.x, 0); 191 path.lineTo(pt.x, h); 192 }, 193 194 /** 195 * Gets the default value for the `styles` attribute. Overrides 196 * base implementation. 197 * 198 * @method _getDefaultStyles 199 * @return Object 200 * @protected 201 */ 202 _getDefaultStyles: function() 203 { 204 var defs = { 205 line: { 206 color:"#f0efe9", 207 weight: 1, 208 alpha: 1 209 } 210 }; 211 return defs; 212 } 213 214 }, 215 { 216 ATTRS: { 217 /** 218 * Indicates the direction of the gridline. 219 * 220 * @attribute direction 221 * @type String 222 */ 223 direction: {}, 224 225 /** 226 * Indicate the `Axis` in which to bind 227 * the gridlines. 228 * 229 * @attribute axis 230 * @type Axis 231 */ 232 axis: {}, 233 234 /** 235 * Indicates the `Graph` in which the gridlines 236 * are drawn. 237 * 238 * @attribute graph 239 * @type Graph 240 */ 241 graph: {}, 242 243 /** 244 * Indicates the number of gridlines to display. If no value is set, gridlines will equal the number of ticks in 245 * the corresponding axis. 246 * 247 * @attribute count 248 * @type Number 249 */ 250 count: {} 251 } 252 }); 253 /** 254 * Graph manages and contains series instances for a `CartesianChart` 255 * instance. 256 * 257 * @class Graph 258 * @constructor 259 * @extends Widget 260 * @uses Renderer 261 * @submodule charts-base 262 */ 263 Y.Graph = Y.Base.create("graph", Y.Widget, [Y.Renderer], { 264 /** 265 * @method bindUI 266 * @private 267 */ 268 bindUI: function() 269 { 270 var bb = this.get("boundingBox"); 271 bb.setStyle("position", "absolute"); 272 this.after("widthChange", this._sizeChangeHandler); 273 this.after("heightChange", this._sizeChangeHandler); 274 this.after("stylesChange", this._updateStyles); 275 this.after("groupMarkersChange", this._drawSeries); 276 }, 277 278 /** 279 * @method syncUI 280 * @private 281 */ 282 syncUI: function() 283 { 284 var background, 285 cb, 286 bg, 287 sc = this.get("seriesCollection"), 288 series, 289 i = 0, 290 len = sc ? sc.length : 0, 291 hgl = this.get("horizontalGridlines"), 292 vgl = this.get("verticalGridlines"); 293 if(this.get("showBackground")) 294 { 295 background = this.get("background"); 296 cb = this.get("contentBox"); 297 bg = this.get("styles").background; 298 bg.stroke = bg.border; 299 bg.stroke.opacity = bg.stroke.alpha; 300 bg.fill.opacity = bg.fill.alpha; 301 bg.width = this.get("width"); 302 bg.height = this.get("height"); 303 bg.type = bg.shape; 304 background.set(bg); 305 } 306 for(; i < len; ++i) 307 { 308 series = sc[i]; 309 if(series instanceof Y.SeriesBase) 310 { 311 series.render(); 312 } 313 } 314 if(hgl && hgl instanceof Y.Gridlines) 315 { 316 hgl.draw(); 317 } 318 if(vgl && vgl instanceof Y.Gridlines) 319 { 320 vgl.draw(); 321 } 322 }, 323 324 /** 325 * Object of arrays containing series mapped to a series type. 326 * 327 * @property seriesTypes 328 * @type Object 329 * @private 330 */ 331 seriesTypes: null, 332 333 /** 334 * Returns a series instance based on an index. 335 * 336 * @method getSeriesByIndex 337 * @param {Number} val index of the series 338 * @return CartesianSeries 339 */ 340 getSeriesByIndex: function(val) 341 { 342 var col = this.get("seriesCollection"), 343 series; 344 if(col && col.length > val) 345 { 346 series = col[val]; 347 } 348 return series; 349 }, 350 351 /** 352 * Returns a series instance based on a key value. 353 * 354 * @method getSeriesByKey 355 * @param {String} val key value of the series 356 * @return CartesianSeries 357 */ 358 getSeriesByKey: function(val) 359 { 360 var obj = this._seriesDictionary, 361 series; 362 if(obj && obj.hasOwnProperty(val)) 363 { 364 series = obj[val]; 365 } 366 return series; 367 }, 368 369 /** 370 * Adds dispatcher to a `_dispatcher` used to 371 * to ensure all series have redrawn before for firing event. 372 * 373 * @method addDispatcher 374 * @param {CartesianSeries} val series instance to add 375 * @protected 376 */ 377 addDispatcher: function(val) 378 { 379 if(!this._dispatchers) 380 { 381 this._dispatchers = []; 382 } 383 this._dispatchers.push(val); 384 }, 385 386 /** 387 * Collection of series to be displayed in the graph. 388 * 389 * @property _seriesCollection 390 * @type Array 391 * @private 392 */ 393 _seriesCollection: null, 394 395 /** 396 * Object containing key value pairs of `CartesianSeries` instances. 397 * 398 * @property _seriesDictionary 399 * @type Object 400 * @private 401 */ 402 _seriesDictionary: null, 403 404 /** 405 * Parses series instances to be displayed in the graph. 406 * 407 * @method _parseSeriesCollection 408 * @param {Array} Collection of `CartesianSeries` instances or objects container `CartesianSeries` attributes values. 409 * @private 410 */ 411 _parseSeriesCollection: function(val) 412 { 413 if(!val) 414 { 415 return; 416 } 417 var len = val.length, 418 i = 0, 419 series, 420 seriesKey; 421 this._seriesCollection = []; 422 this._seriesDictionary = {}; 423 this.seriesTypes = []; 424 for(; i < len; ++i) 425 { 426 series = val[i]; 427 if(!(series instanceof Y.CartesianSeries) && !(series instanceof Y.PieSeries)) 428 { 429 this._createSeries(series); 430 continue; 431 } 432 this._addSeries(series); 433 } 434 len = this._seriesCollection.length; 435 for(i = 0; i < len; ++i) 436 { 437 series = this.get("seriesCollection")[i]; 438 seriesKey = series.get("direction") === "horizontal" ? "yKey" : "xKey"; 439 this._seriesDictionary[series.get(seriesKey)] = series; 440 } 441 }, 442 443 /** 444 * Adds a series to the graph. 445 * 446 * @method _addSeries 447 * @param {CartesianSeries} series Series to add to the graph. 448 * @private 449 */ 450 _addSeries: function(series) 451 { 452 var type = series.get("type"), 453 seriesCollection = this.get("seriesCollection"), 454 graphSeriesLength = seriesCollection.length, 455 seriesTypes = this.seriesTypes, 456 typeSeriesCollection; 457 if(!series.get("graph")) 458 { 459 series.set("graph", this); 460 } 461 seriesCollection.push(series); 462 if(!seriesTypes.hasOwnProperty(type)) 463 { 464 this.seriesTypes[type] = []; 465 } 466 typeSeriesCollection = this.seriesTypes[type]; 467 series.set("graphOrder", graphSeriesLength); 468 series.set("order", typeSeriesCollection.length); 469 typeSeriesCollection.push(series); 470 series.set("seriesTypeCollection", typeSeriesCollection); 471 this.addDispatcher(series); 472 series.after("drawingComplete", Y.bind(this._drawingCompleteHandler, this)); 473 this.fire("seriesAdded", series); 474 }, 475 476 /** 477 * Creates a `CartesianSeries` instance from an object containing attribute key value pairs. The key value pairs include 478 * attributes for the specific series and a type value which defines the type of series to be used. 479 * 480 * @method createSeries 481 * @param {Object} seriesData Series attribute key value pairs. 482 * @private 483 */ 484 _createSeries: function(seriesData) 485 { 486 var type = seriesData.type, 487 seriesCollection = this.get("seriesCollection"), 488 seriesTypes = this.seriesTypes, 489 typeSeriesCollection, 490 SeriesClass, 491 series; 492 seriesData.graph = this; 493 if(!seriesTypes.hasOwnProperty(type)) 494 { 495 seriesTypes[type] = []; 496 } 497 typeSeriesCollection = seriesTypes[type]; 498 seriesData.graph = this; 499 seriesData.order = typeSeriesCollection.length; 500 seriesData.graphOrder = seriesCollection.length; 501 SeriesClass = this._getSeries(seriesData.type); 502 series = new SeriesClass(seriesData); 503 this.addDispatcher(series); 504 series.after("drawingComplete", Y.bind(this._drawingCompleteHandler, this)); 505 typeSeriesCollection.push(series); 506 seriesCollection.push(series); 507 series.set("seriesTypeCollection", typeSeriesCollection); 508 if(this.get("rendered")) 509 { 510 series.render(); 511 } 512 }, 513 514 /** 515 * String reference for pre-defined `Series` classes. 516 * 517 * @property _seriesMap 518 * @type Object 519 * @private 520 */ 521 _seriesMap: { 522 line : Y.LineSeries, 523 column : Y.ColumnSeries, 524 bar : Y.BarSeries, 525 area : Y.AreaSeries, 526 candlestick : Y.CandlestickSeries, 527 ohlc : Y.OHLCSeries, 528 stackedarea : Y.StackedAreaSeries, 529 stackedline : Y.StackedLineSeries, 530 stackedcolumn : Y.StackedColumnSeries, 531 stackedbar : Y.StackedBarSeries, 532 markerseries : Y.MarkerSeries, 533 spline : Y.SplineSeries, 534 areaspline : Y.AreaSplineSeries, 535 stackedspline : Y.StackedSplineSeries, 536 stackedareaspline : Y.StackedAreaSplineSeries, 537 stackedmarkerseries : Y.StackedMarkerSeries, 538 pie : Y.PieSeries, 539 combo : Y.ComboSeries, 540 stackedcombo : Y.StackedComboSeries, 541 combospline : Y.ComboSplineSeries, 542 stackedcombospline : Y.StackedComboSplineSeries 543 }, 544 545 /** 546 * Returns a specific `CartesianSeries` class based on key value from a look up table of a direct reference to a 547 * class. When specifying a key value, the following options are available: 548 * 549 * <table> 550 * <tr><th>Key Value</th><th>Class</th></tr> 551 * <tr><td>line</td><td>Y.LineSeries</td></tr> 552 * <tr><td>column</td><td>Y.ColumnSeries</td></tr> 553 * <tr><td>bar</td><td>Y.BarSeries</td></tr> 554 * <tr><td>area</td><td>Y.AreaSeries</td></tr> 555 * <tr><td>stackedarea</td><td>Y.StackedAreaSeries</td></tr> 556 * <tr><td>stackedline</td><td>Y.StackedLineSeries</td></tr> 557 * <tr><td>stackedcolumn</td><td>Y.StackedColumnSeries</td></tr> 558 * <tr><td>stackedbar</td><td>Y.StackedBarSeries</td></tr> 559 * <tr><td>markerseries</td><td>Y.MarkerSeries</td></tr> 560 * <tr><td>spline</td><td>Y.SplineSeries</td></tr> 561 * <tr><td>areaspline</td><td>Y.AreaSplineSeries</td></tr> 562 * <tr><td>stackedspline</td><td>Y.StackedSplineSeries</td></tr> 563 * <tr><td>stackedareaspline</td><td>Y.StackedAreaSplineSeries</td></tr> 564 * <tr><td>stackedmarkerseries</td><td>Y.StackedMarkerSeries</td></tr> 565 * <tr><td>pie</td><td>Y.PieSeries</td></tr> 566 * <tr><td>combo</td><td>Y.ComboSeries</td></tr> 567 * <tr><td>stackedcombo</td><td>Y.StackedComboSeries</td></tr> 568 * <tr><td>combospline</td><td>Y.ComboSplineSeries</td></tr> 569 * <tr><td>stackedcombospline</td><td>Y.StackedComboSplineSeries</td></tr> 570 * </table> 571 * 572 * When referencing a class directly, you can specify any of the above classes or any custom class that extends 573 * `CartesianSeries` or `PieSeries`. 574 * 575 * @method _getSeries 576 * @param {String | Object} type Series type. 577 * @return CartesianSeries 578 * @private 579 */ 580 _getSeries: function(type) 581 { 582 var seriesClass; 583 if(Y_Lang.isString(type)) 584 { 585 seriesClass = this._seriesMap[type]; 586 } 587 else 588 { 589 seriesClass = type; 590 } 591 return seriesClass; 592 }, 593 594 /** 595 * Event handler for marker events. 596 * 597 * @method _markerEventHandler 598 * @param {Object} e Event object. 599 * @private 600 */ 601 _markerEventHandler: function(e) 602 { 603 var type = e.type, 604 markerNode = e.currentTarget, 605 strArr = markerNode.getAttribute("id").split("_"), 606 series = this.getSeriesByIndex(strArr[1]), 607 index = strArr[2]; 608 series.updateMarkerState(type, index); 609 }, 610 611 /** 612 * Collection of `CartesianSeries` instances to be redrawn. 613 * 614 * @property _dispatchers 615 * @type Array 616 * @private 617 */ 618 _dispatchers: null, 619 620 /** 621 * Updates the `Graph` styles. 622 * 623 * @method _updateStyles 624 * @private 625 */ 626 _updateStyles: function() 627 { 628 var styles = this.get("styles").background, 629 border = styles.border; 630 border.opacity = border.alpha; 631 styles.stroke = border; 632 styles.fill.opacity = styles.fill.alpha; 633 this.get("background").set(styles); 634 this._sizeChangeHandler(); 635 }, 636 637 /** 638 * Event handler for size changes. 639 * 640 * @method _sizeChangeHandler 641 * @param {Object} e Event object. 642 * @private 643 */ 644 _sizeChangeHandler: function() 645 { 646 var hgl = this.get("horizontalGridlines"), 647 vgl = this.get("verticalGridlines"), 648 w = this.get("width"), 649 h = this.get("height"), 650 bg = this.get("styles").background, 651 weight, 652 background; 653 if(bg && bg.border) 654 { 655 weight = bg.border.weight || 0; 656 } 657 if(this.get("showBackground")) 658 { 659 background = this.get("background"); 660 if(w && h) 661 { 662 background.set("width", w); 663 background.set("height", h); 664 } 665 } 666 if(this._gridlines) 667 { 668 this._gridlines.clear(); 669 } 670 if(hgl && hgl instanceof Y.Gridlines) 671 { 672 hgl.draw(); 673 } 674 if(vgl && vgl instanceof Y.Gridlines) 675 { 676 vgl.draw(); 677 } 678 this._drawSeries(); 679 }, 680 681 /** 682 * Draws each series. 683 * 684 * @method _drawSeries 685 * @private 686 */ 687 _drawSeries: function() 688 { 689 if(this._drawing) 690 { 691 this._callLater = true; 692 return; 693 } 694 var sc, 695 i, 696 len, 697 graphic = this.get("graphic"); 698 graphic.set("autoDraw", false); 699 graphic.set("width", this.get("width")); 700 graphic.set("height", this.get("height")); 701 this._callLater = false; 702 this._drawing = true; 703 sc = this.get("seriesCollection"); 704 i = 0; 705 len = sc ? sc.length : 0; 706 for(; i < len; ++i) 707 { 708 sc[i].draw(); 709 if((!sc[i].get("xcoords") || !sc[i].get("ycoords")) && !sc[i] instanceof Y.PieSeries) 710 { 711 this._callLater = true; 712 break; 713 } 714 } 715 this._drawing = false; 716 if(this._callLater) 717 { 718 this._drawSeries(); 719 } 720 }, 721 722 /** 723 * Event handler for series drawingComplete event. 724 * 725 * @method _drawingCompleteHandler 726 * @param {Object} e Event object. 727 * @private 728 */ 729 _drawingCompleteHandler: function(e) 730 { 731 var series = e.currentTarget, 732 graphic, 733 index = Y.Array.indexOf(this._dispatchers, series); 734 if(index > -1) 735 { 736 this._dispatchers.splice(index, 1); 737 } 738 if(this._dispatchers.length < 1) 739 { 740 graphic = this.get("graphic"); 741 if(!graphic.get("autoDraw")) 742 { 743 graphic._redraw(); 744 } 745 this.fire("chartRendered"); 746 } 747 }, 748 749 /** 750 * Gets the default value for the `styles` attribute. Overrides 751 * base implementation. 752 * 753 * @method _getDefaultStyles 754 * @return Object 755 * @protected 756 */ 757 _getDefaultStyles: function() 758 { 759 var defs = { 760 background: { 761 shape: "rect", 762 fill:{ 763 color:"#faf9f2" 764 }, 765 border: { 766 color:"#dad8c9", 767 weight: 1 768 } 769 } 770 }; 771 return defs; 772 }, 773 774 /** 775 * Destructor implementation Graph class. Removes all Graphic instances from the widget. 776 * 777 * @method destructor 778 * @protected 779 */ 780 destructor: function() 781 { 782 if(this._graphic) 783 { 784 this._graphic.destroy(); 785 this._graphic = null; 786 } 787 if(this._background) 788 { 789 this._background.get("graphic").destroy(); 790 this._background = null; 791 } 792 if(this._gridlines) 793 { 794 this._gridlines.get("graphic").destroy(); 795 this._gridlines = null; 796 } 797 } 798 }, { 799 ATTRS: { 800 /** 801 * The x-coordinate for the graph. 802 * 803 * @attribute x 804 * @type Number 805 * @protected 806 */ 807 x: { 808 setter: function(val) 809 { 810 this.get("boundingBox").setStyle("left", val + "px"); 811 return val; 812 } 813 }, 814 815 /** 816 * The y-coordinate for the graph. 817 * 818 * @attribute y 819 * @type Number 820 * @protected 821 */ 822 y: { 823 setter: function(val) 824 { 825 this.get("boundingBox").setStyle("top", val + "px"); 826 return val; 827 } 828 }, 829 830 /** 831 * Reference to the chart instance using the graph. 832 * 833 * @attribute chart 834 * @type ChartBase 835 * @readOnly 836 */ 837 chart: { 838 getter: function() { 839 var chart = this._state.chart || this; 840 return chart; 841 } 842 }, 843 844 /** 845 * Collection of series. When setting the `seriesCollection` the array can contain a combination of either 846 * `CartesianSeries` instances or object literals with properties that will define a series. 847 * 848 * @attribute seriesCollection 849 * @type CartesianSeries 850 */ 851 seriesCollection: { 852 getter: function() 853 { 854 return this._seriesCollection; 855 }, 856 857 setter: function(val) 858 { 859 this._parseSeriesCollection(val); 860 return this._seriesCollection; 861 } 862 }, 863 864 /** 865 * Indicates whether the `Graph` has a background. 866 * 867 * @attribute showBackground 868 * @type Boolean 869 * @default true 870 */ 871 showBackground: { 872 value: true 873 }, 874 875 /** 876 * Read-only hash lookup for all series on in the `Graph`. 877 * 878 * @attribute seriesDictionary 879 * @type Object 880 * @readOnly 881 */ 882 seriesDictionary: { 883 readOnly: true, 884 885 getter: function() 886 { 887 return this._seriesDictionary; 888 } 889 }, 890 891 /** 892 * Reference to the horizontal `Gridlines` instance. 893 * 894 * @attribute horizontalGridlines 895 * @type Gridlines 896 * @default null 897 */ 898 horizontalGridlines: { 899 value: null, 900 901 setter: function(val) 902 { 903 var cfg, 904 key, 905 gl = this.get("horizontalGridlines"); 906 if(gl && gl instanceof Y.Gridlines) 907 { 908 gl.remove(); 909 } 910 if(val instanceof Y.Gridlines) 911 { 912 gl = val; 913 val.set("graph", this); 914 return val; 915 } 916 else if(val) 917 { 918 cfg = { 919 direction: "horizonal", 920 graph: this 921 }; 922 for(key in val) 923 { 924 if(val.hasOwnProperty(key)) 925 { 926 cfg[key] = val[key]; 927 } 928 } 929 gl = new Y.Gridlines(cfg); 930 return gl; 931 } 932 } 933 }, 934 935 /** 936 * Reference to the vertical `Gridlines` instance. 937 * 938 * @attribute verticalGridlines 939 * @type Gridlines 940 * @default null 941 */ 942 verticalGridlines: { 943 value: null, 944 945 setter: function(val) 946 { 947 var cfg, 948 key, 949 gl = this.get("verticalGridlines"); 950 if(gl && gl instanceof Y.Gridlines) 951 { 952 gl.remove(); 953 } 954 if(val instanceof Y.Gridlines) 955 { 956 gl = val; 957 val.set("graph", this); 958 return val; 959 } 960 else if(val) 961 { 962 cfg = { 963 direction: "vertical", 964 graph: this 965 }; 966 for(key in val) 967 { 968 if(val.hasOwnProperty(key)) 969 { 970 cfg[key] = val[key]; 971 } 972 } 973 gl = new Y.Gridlines(cfg); 974 return gl; 975 } 976 } 977 }, 978 979 /** 980 * Reference to graphic instance used for the background. 981 * 982 * @attribute background 983 * @type Graphic 984 * @readOnly 985 */ 986 background: { 987 getter: function() 988 { 989 if(!this._background) 990 { 991 this._backgroundGraphic = new Y.Graphic({render:this.get("contentBox")}); 992 this._backgroundGraphic.get("node").style.zIndex = 0; 993 this._background = this._backgroundGraphic.addShape({type: "rect"}); 994 } 995 return this._background; 996 } 997 }, 998 999 /** 1000 * Reference to graphic instance used for gridlines. 1001 * 1002 * @attribute gridlines 1003 * @type Graphic 1004 * @readOnly 1005 */ 1006 gridlines: { 1007 readOnly: true, 1008 1009 getter: function() 1010 { 1011 if(!this._gridlines) 1012 { 1013 this._gridlinesGraphic = new Y.Graphic({render:this.get("contentBox")}); 1014 this._gridlinesGraphic.get("node").style.zIndex = 1; 1015 this._gridlines = this._gridlinesGraphic.addShape({type: "path"}); 1016 } 1017 return this._gridlines; 1018 } 1019 }, 1020 1021 /** 1022 * Reference to graphic instance used for series. 1023 * 1024 * @attribute graphic 1025 * @type Graphic 1026 * @readOnly 1027 */ 1028 graphic: { 1029 readOnly: true, 1030 1031 getter: function() 1032 { 1033 if(!this._graphic) 1034 { 1035 this._graphic = new Y.Graphic({render:this.get("contentBox")}); 1036 this._graphic.get("node").style.zIndex = 2; 1037 this._graphic.set("autoDraw", false); 1038 } 1039 return this._graphic; 1040 } 1041 }, 1042 1043 /** 1044 * Indicates whether or not markers for a series will be grouped and rendered in a single complex shape instance. 1045 * 1046 * @attribute groupMarkers 1047 * @type Boolean 1048 */ 1049 groupMarkers: { 1050 value: false 1051 } 1052 1053 /** 1054 * Style properties used for drawing a background. Below are the default values: 1055 * <dl> 1056 * <dt>background</dt><dd>An object containing the following values: 1057 * <dl> 1058 * <dt>fill</dt><dd>Defines the style properties for the fill. Contains the following values: 1059 * <dl> 1060 * <dt>color</dt><dd>Color of the fill. The default value is #faf9f2.</dd> 1061 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the background fill. 1062 * The default value is 1.</dd> 1063 * </dl> 1064 * </dd> 1065 * <dt>border</dt><dd>Defines the style properties for the border. Contains the following values: 1066 * <dl> 1067 * <dt>color</dt><dd>Color of the border. The default value is #dad8c9.</dd> 1068 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the background border. 1069 * The default value is 1.</dd> 1070 * <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd> 1071 * </dl> 1072 * </dd> 1073 * </dl> 1074 * </dd> 1075 * </dl> 1076 * 1077 * @attribute styles 1078 * @type Object 1079 */ 1080 } 1081 }); 1082 /** 1083 * The ChartBase class is an abstract class used to create charts. 1084 * 1085 * @class ChartBase 1086 * @constructor 1087 * @submodule charts-base 1088 */ 1089 function ChartBase() {} 1090 1091 ChartBase.ATTRS = { 1092 /** 1093 * Data used to generate the chart. 1094 * 1095 * @attribute dataProvider 1096 * @type Array 1097 */ 1098 dataProvider: { 1099 lazyAdd: false, 1100 1101 valueFn: function() 1102 { 1103 var defDataProvider = []; 1104 if(!this._wereSeriesKeysExplicitlySet()) 1105 { 1106 this.set("seriesKeys", this._buildSeriesKeys(defDataProvider), {src: "internal"}); 1107 } 1108 return defDataProvider; 1109 }, 1110 1111 setter: function(val) 1112 { 1113 var dataProvider = this._setDataValues(val); 1114 if(!this._wereSeriesKeysExplicitlySet()) 1115 { 1116 this.set("seriesKeys", this._buildSeriesKeys(dataProvider), {src: "internal"}); 1117 } 1118 return dataProvider; 1119 } 1120 }, 1121 1122 /** 1123 * A collection of keys that map to the series axes. If no keys are set, 1124 * they will be generated automatically depending on the data structure passed into 1125 * the chart. 1126 * 1127 * @attribute seriesKeys 1128 * @type Array 1129 */ 1130 seriesKeys: { 1131 lazyAdd: false, 1132 1133 setter: function(val) 1134 { 1135 var opts = arguments[2]; 1136 if(!val || (opts && opts.src && opts.src === "internal")) 1137 { 1138 this._seriesKeysExplicitlySet = false; 1139 } 1140 else 1141 { 1142 this._seriesKeysExplicitlySet = true; 1143 } 1144 return val; 1145 } 1146 }, 1147 1148 /** 1149 * Sets the `aria-label` for the chart. 1150 * 1151 * @attribute ariaLabel 1152 * @type String 1153 */ 1154 ariaLabel: { 1155 value: "Chart Application", 1156 1157 setter: function(val) 1158 { 1159 var cb = this.get("contentBox"); 1160 if(cb) 1161 { 1162 cb.setAttribute("aria-label", val); 1163 } 1164 return val; 1165 } 1166 }, 1167 1168 /** 1169 * Sets the aria description for the chart. 1170 * 1171 * @attribute ariaDescription 1172 * @type String 1173 */ 1174 ariaDescription: { 1175 value: "Use the up and down keys to navigate between series. Use the left and right keys to navigate through items in a series.", 1176 1177 setter: function(val) 1178 { 1179 if(this._description) 1180 { 1181 this._description.set("text", val); 1182 } 1183 return val; 1184 } 1185 }, 1186 1187 /** 1188 * Reference to the default tooltip available for the chart. 1189 * <p>Contains the following properties:</p> 1190 * <dl> 1191 * <dt>node</dt><dd>Reference to the actual dom node</dd> 1192 * <dt>showEvent</dt><dd>Event that should trigger the tooltip</dd> 1193 * <dt>hideEvent</dt><dd>Event that should trigger the removal of a tooltip (can be an event or an array of events)</dd> 1194 * <dt>styles</dt><dd>A hash of style properties that will be applied to the tooltip node</dd> 1195 * <dt>show</dt><dd>Indicates whether or not to show the tooltip</dd> 1196 * <dt>markerEventHandler</dt><dd>Displays and hides tooltip based on marker events</dd> 1197 * <dt>planarEventHandler</dt><dd>Displays and hides tooltip based on planar events</dd> 1198 * <dt>markerLabelFunction</dt><dd>Reference to the function used to format a marker event triggered tooltip's text. 1199 * The method contains the following arguments: 1200 * <dl> 1201 * <dt>categoryItem</dt><dd>An object containing the following: 1202 * <dl> 1203 * <dt>axis</dt><dd>The axis to which the category is bound.</dd> 1204 * <dt>displayName</dt><dd>The display name set to the category (defaults to key if not provided).</dd> 1205 * <dt>key</dt><dd>The key of the category.</dd> 1206 * <dt>value</dt><dd>The value of the category.</dd> 1207 * </dl> 1208 * </dd> 1209 * <dt>valueItem</dt><dd>An object containing the following: 1210 * <dl> 1211 * <dt>axis</dt><dd>The axis to which the item's series is bound.</dd> 1212 * <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd> 1213 * <dt>key</dt><dd>The key for the series.</dd> 1214 * <dt>value</dt><dd>The value for the series item.</dd> 1215 * </dl> 1216 * </dd> 1217 * <dt>itemIndex</dt><dd>The index of the item within the series.</dd> 1218 * <dt>series</dt><dd> The `CartesianSeries` instance of the item.</dd> 1219 * <dt>seriesIndex</dt><dd>The index of the series in the `seriesCollection`.</dd> 1220 * </dl> 1221 * The method returns an `HTMLElement` which is written into the DOM using `appendChild`. If you override this method and choose 1222 * to return an html string, you will also need to override the tooltip's `setTextFunction` method to accept an html string. 1223 * </dd> 1224 * <dt>planarLabelFunction</dt><dd>Reference to the function used to format a planar event triggered tooltip's text 1225 * <dl> 1226 * <dt>categoryAxis</dt><dd> `CategoryAxis` Reference to the categoryAxis of the chart. 1227 * <dt>valueItems</dt><dd>Array of objects for each series that has a data point in the coordinate plane of the event. Each 1228 * object contains the following data: 1229 * <dl> 1230 * <dt>axis</dt><dd>The value axis of the series.</dd> 1231 * <dt>key</dt><dd>The key for the series.</dd> 1232 * <dt>value</dt><dd>The value for the series item.</dd> 1233 * <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd> 1234 * </dl> 1235 * </dd> 1236 * <dt>index</dt><dd>The index of the item within its series.</dd> 1237 * <dt>seriesArray</dt><dd>Array of series instances for each value item.</dd> 1238 * <dt>seriesIndex</dt><dd>The index of the series in the `seriesCollection`.</dd> 1239 * </dl> 1240 * </dd> 1241 * </dl> 1242 * The method returns an `HTMLElement` which is written into the DOM using `appendChild`. If you override this method and choose 1243 * to return an html string, you will also need to override the tooltip's `setTextFunction` method to accept an html string. 1244 * </dd> 1245 * <dt>setTextFunction</dt><dd>Method that writes content returned from `planarLabelFunction` or `markerLabelFunction` into the 1246 * the tooltip node. Has the following signature: 1247 * <dl> 1248 * <dt>label</dt><dd>The `HTMLElement` that the content is to be added.</dd> 1249 * <dt>val</dt><dd>The content to be rendered into tooltip. This can be a `String` or `HTMLElement`. If an HTML string is used, 1250 * it will be rendered as a string.</dd> 1251 * </dl> 1252 * </dd> 1253 * </dl> 1254 * @attribute tooltip 1255 * @type Object 1256 */ 1257 tooltip: { 1258 valueFn: "_getTooltip", 1259 1260 setter: function(val) 1261 { 1262 return this._updateTooltip(val); 1263 } 1264 }, 1265 1266 /** 1267 * The key value used for the chart's category axis. 1268 * 1269 * @attribute categoryKey 1270 * @type String 1271 * @default category 1272 */ 1273 categoryKey: { 1274 value: "category" 1275 }, 1276 1277 /** 1278 * Indicates the type of axis to use for the category axis. 1279 * 1280 * <dl> 1281 * <dt>category</dt><dd>Specifies a `CategoryAxis`.</dd> 1282 * <dt>time</dt><dd>Specifies a `TimeAxis</dd> 1283 * </dl> 1284 * 1285 * @attribute categoryType 1286 * @type String 1287 * @default category 1288 */ 1289 categoryType:{ 1290 value:"category" 1291 }, 1292 1293 /** 1294 * Indicates the the type of interactions that will fire events. 1295 * 1296 * <dl> 1297 * <dt>marker</dt><dd>Events will be broadcasted when the mouse interacts with individual markers.</dd> 1298 * <dt>planar</dt><dd>Events will be broadcasted when the mouse intersects the plane of any markers on the chart.</dd> 1299 * <dt>none</dt><dd>No events will be broadcasted.</dd> 1300 * </dl> 1301 * 1302 * @attribute interactionType 1303 * @type String 1304 * @default marker 1305 */ 1306 interactionType: { 1307 value: "marker" 1308 }, 1309 1310 /** 1311 * Reference to all the axes in the chart. 1312 * 1313 * @attribute axesCollection 1314 * @type Array 1315 */ 1316 axesCollection: {}, 1317 1318 /** 1319 * Reference to graph instance. 1320 * 1321 * @attribute graph 1322 * @type Graph 1323 */ 1324 graph: { 1325 valueFn: "_getGraph" 1326 }, 1327 1328 /** 1329 * Indicates whether or not markers for a series will be grouped and rendered in a single complex shape instance. 1330 * 1331 * @attribute groupMarkers 1332 * @type Boolean 1333 */ 1334 groupMarkers: { 1335 value: false 1336 } 1337 }; 1338 1339 ChartBase.prototype = { 1340 1341 /** 1342 * Utility method to determine if `seriesKeys` was explicitly provided 1343 * (for example during construction, or set by the user), as opposed to 1344 * being derived from the dataProvider for example. 1345 * 1346 * @method _wereSeriesKeysExplicitlySet 1347 * @private 1348 * @return boolean true if the `seriesKeys` attribute was explicitly set. 1349 */ 1350 _wereSeriesKeysExplicitlySet : function() 1351 { 1352 var seriesKeys = this.get("seriesKeys"); 1353 return seriesKeys && this._seriesKeysExplicitlySet; 1354 }, 1355 1356 /** 1357 * Handles groupMarkers change event. 1358 * 1359 * @method _groupMarkersChangeHandler 1360 * @param {Object} e Event object. 1361 * @private 1362 */ 1363 _groupMarkersChangeHandler: function(e) 1364 { 1365 var graph = this.get("graph"), 1366 useGroupMarkers = e.newVal; 1367 if(graph) 1368 { 1369 graph.set("groupMarkers", useGroupMarkers); 1370 } 1371 }, 1372 1373 /** 1374 * Handler for itemRendered event. 1375 * 1376 * @method _itemRendered 1377 * @param {Object} e Event object. 1378 * @private 1379 */ 1380 _itemRendered: function(e) 1381 { 1382 this._itemRenderQueue = this._itemRenderQueue.splice(1 + Y.Array.indexOf(this._itemRenderQueue, e.currentTarget), 1); 1383 if(this._itemRenderQueue.length < 1) 1384 { 1385 this._redraw(); 1386 } 1387 }, 1388 1389 /** 1390 * Default value function for the `Graph` attribute. 1391 * 1392 * @method _getGraph 1393 * @return Graph 1394 * @private 1395 */ 1396 _getGraph: function() 1397 { 1398 var graph = new Y.Graph({ 1399 chart:this, 1400 groupMarkers: this.get("groupMarkers") 1401 }); 1402 graph.after("chartRendered", Y.bind(function() { 1403 this.fire("chartRendered"); 1404 }, this)); 1405 return graph; 1406 }, 1407 1408 /** 1409 * Returns a series instance by index or key value. 1410 * 1411 * @method getSeries 1412 * @param val 1413 * @return CartesianSeries 1414 */ 1415 getSeries: function(val) 1416 { 1417 var series = null, 1418 graph = this.get("graph"); 1419 if(graph) 1420 { 1421 if(Y_Lang.isNumber(val)) 1422 { 1423 series = graph.getSeriesByIndex(val); 1424 } 1425 else 1426 { 1427 series = graph.getSeriesByKey(val); 1428 } 1429 } 1430 return series; 1431 }, 1432 1433 /** 1434 * Returns an `Axis` instance by key reference. If the axis was explicitly set through the `axes` attribute, 1435 * the key will be the same as the key used in the `axes` object. For default axes, the key for 1436 * the category axis is the value of the `categoryKey` (`category`). For the value axis, the default 1437 * key is `values`. 1438 * 1439 * @method getAxisByKey 1440 * @param {String} val Key reference used to look up the axis. 1441 * @return Axis 1442 */ 1443 getAxisByKey: function(val) 1444 { 1445 var axis, 1446 axes = this.get("axes"); 1447 if(axes && axes.hasOwnProperty(val)) 1448 { 1449 axis = axes[val]; 1450 } 1451 return axis; 1452 }, 1453 1454 /** 1455 * Returns the category axis for the chart. 1456 * 1457 * @method getCategoryAxis 1458 * @return Axis 1459 */ 1460 getCategoryAxis: function() 1461 { 1462 var axis, 1463 key = this.get("categoryKey"), 1464 axes = this.get("axes"); 1465 if(axes.hasOwnProperty(key)) 1466 { 1467 axis = axes[key]; 1468 } 1469 return axis; 1470 }, 1471 1472 /** 1473 * Default direction of the chart. 1474 * 1475 * @property _direction 1476 * @type String 1477 * @default horizontal 1478 * @private 1479 */ 1480 _direction: "horizontal", 1481 1482 /** 1483 * Storage for the `dataProvider` attribute. 1484 * 1485 * @property _dataProvider 1486 * @type Array 1487 * @private 1488 */ 1489 _dataProvider: null, 1490 1491 /** 1492 * Setter method for `dataProvider` attribute. 1493 * 1494 * @method _setDataValues 1495 * @param {Array} val Array to be set as `dataProvider`. 1496 * @return Array 1497 * @private 1498 */ 1499 _setDataValues: function(val) 1500 { 1501 if(Y_Lang.isArray(val[0])) 1502 { 1503 var hash, 1504 dp = [], 1505 cats = val[0], 1506 i = 0, 1507 l = cats.length, 1508 n, 1509 sl = val.length; 1510 for(; i < l; ++i) 1511 { 1512 hash = {category:cats[i]}; 1513 for(n = 1; n < sl; ++n) 1514 { 1515 hash["series" + n] = val[n][i]; 1516 } 1517 dp[i] = hash; 1518 } 1519 return dp; 1520 } 1521 return val; 1522 }, 1523 1524 /** 1525 * Storage for `seriesCollection` attribute. 1526 * 1527 * @property _seriesCollection 1528 * @type Array 1529 * @private 1530 */ 1531 _seriesCollection: null, 1532 1533 /** 1534 * Setter method for `seriesCollection` attribute. 1535 * 1536 * @property _setSeriesCollection 1537 * @param {Array} val Array of either `CartesianSeries` instances or objects containing series attribute key value pairs. 1538 * @private 1539 */ 1540 _setSeriesCollection: function(val) 1541 { 1542 this._seriesCollection = val; 1543 }, 1544 /** 1545 * Helper method that returns the axis class that a key references. 1546 * 1547 * @method _getAxisClass 1548 * @param {String} t The type of axis. 1549 * @return Axis 1550 * @private 1551 */ 1552 _getAxisClass: function(t) 1553 { 1554 return this._axisClass[t]; 1555 }, 1556 1557 /** 1558 * Key value pairs of axis types. 1559 * 1560 * @property _axisClass 1561 * @type Object 1562 * @private 1563 */ 1564 _axisClass: { 1565 stacked: Y.StackedAxis, 1566 numeric: Y.NumericAxis, 1567 category: Y.CategoryAxis, 1568 time: Y.TimeAxis 1569 }, 1570 1571 /** 1572 * Collection of axes. 1573 * 1574 * @property _axes 1575 * @type Array 1576 * @private 1577 */ 1578 _axes: null, 1579 1580 /** 1581 * @method initializer 1582 * @private 1583 */ 1584 initializer: function() 1585 { 1586 this._itemRenderQueue = []; 1587 this._seriesIndex = -1; 1588 this._itemIndex = -1; 1589 this.after("dataProviderChange", this._dataProviderChangeHandler); 1590 }, 1591 1592 /** 1593 * @method renderUI 1594 * @private 1595 */ 1596 renderUI: function() 1597 { 1598 var tt = this.get("tooltip"), 1599 bb = this.get("boundingBox"), 1600 cb = this.get("contentBox"); 1601 //move the position = absolute logic to a class file 1602 bb.setStyle("position", "absolute"); 1603 cb.setStyle("position", "absolute"); 1604 this._addAxes(); 1605 this._addSeries(); 1606 if(tt && tt.show) 1607 { 1608 this._addTooltip(); 1609 } 1610 this._setAriaElements(bb, cb); 1611 }, 1612 1613 /** 1614 * Creates an aria `live-region`, `aria-label` and `aria-describedby` for the Chart. 1615 * 1616 * @method _setAriaElements 1617 * @param {Node} cb Reference to the Chart's `contentBox` attribute. 1618 * @private 1619 */ 1620 _setAriaElements: function(bb, cb) 1621 { 1622 var description = this._getAriaOffscreenNode(), 1623 id = this.get("id") + "_description", 1624 liveRegion = this._getAriaOffscreenNode(); 1625 cb.set("tabIndex", 0); 1626 cb.set("role", "img"); 1627 cb.setAttribute("aria-label", this.get("ariaLabel")); 1628 cb.setAttribute("aria-describedby", id); 1629 description.set("id", id); 1630 description.set("tabIndex", -1); 1631 description.set("text", this.get("ariaDescription")); 1632 liveRegion.set("id", "live-region"); 1633 liveRegion.set("aria-live", "polite"); 1634 liveRegion.set("aria-atomic", "true"); 1635 liveRegion.set("role", "status"); 1636 bb.setAttribute("role", "application"); 1637 bb.appendChild(description); 1638 bb.appendChild(liveRegion); 1639 this._description = description; 1640 this._liveRegion = liveRegion; 1641 }, 1642 1643 /** 1644 * Sets a node offscreen for use as aria-description or aria-live-regin. 1645 * 1646 * @method _setOffscreen 1647 * @return Node 1648 * @private 1649 */ 1650 _getAriaOffscreenNode: function() 1651 { 1652 var node = Y.Node.create("<div></div>"), 1653 ie = Y.UA.ie, 1654 clipRect = (ie && ie < 8) ? "rect(1px 1px 1px 1px)" : "rect(1px, 1px, 1px, 1px)"; 1655 node.setStyle("position", "absolute"); 1656 node.setStyle("height", "1px"); 1657 node.setStyle("width", "1px"); 1658 node.setStyle("overflow", "hidden"); 1659 node.setStyle("clip", clipRect); 1660 return node; 1661 }, 1662 1663 /** 1664 * @method syncUI 1665 * @private 1666 */ 1667 syncUI: function() 1668 { 1669 this._redraw(); 1670 }, 1671 1672 /** 1673 * @method bindUI 1674 * @private 1675 */ 1676 bindUI: function() 1677 { 1678 this.after("tooltipChange", Y.bind(this._tooltipChangeHandler, this)); 1679 this.after("widthChange", this._sizeChanged); 1680 this.after("heightChange", this._sizeChanged); 1681 this.after("groupMarkersChange", this._groupMarkersChangeHandler); 1682 var tt = this.get("tooltip"), 1683 hideEvent = "mouseout", 1684 showEvent = "mouseover", 1685 cb = this.get("contentBox"), 1686 interactionType = this.get("interactionType"), 1687 i = 0, 1688 len, 1689 markerClassName = "." + SERIES_MARKER, 1690 isTouch = ((WINDOW && ("ontouchstart" in WINDOW)) && !(Y.UA.chrome && Y.UA.chrome < 6)); 1691 Y.on("keydown", Y.bind(function(e) { 1692 var key = e.keyCode, 1693 numKey = parseFloat(key), 1694 msg; 1695 if(numKey > 36 && numKey < 41) 1696 { 1697 e.halt(); 1698 msg = this._getAriaMessage(numKey); 1699 this._liveRegion.set("text", msg); 1700 } 1701 }, this), this.get("contentBox")); 1702 if(interactionType === "marker") 1703 { 1704 //if touch capabilities, toggle tooltip on touchend. otherwise, the tooltip attribute's hideEvent/showEvent types. 1705 hideEvent = tt.hideEvent; 1706 showEvent = tt.showEvent; 1707 if(isTouch) 1708 { 1709 Y.delegate("touchend", Y.bind(this._markerEventDispatcher, this), cb, markerClassName); 1710 //hide active tooltip if the chart is touched 1711 Y.on("touchend", Y.bind(function(e) { 1712 //only halt the event if it originated from the chart 1713 if(cb.contains(e.target)) 1714 { 1715 e.halt(true); 1716 } 1717 if(this._activeMarker) 1718 { 1719 this._activeMarker = null; 1720 this.hideTooltip(e); 1721 } 1722 }, this)); 1723 } 1724 else 1725 { 1726 Y.delegate("mouseenter", Y.bind(this._markerEventDispatcher, this), cb, markerClassName); 1727 Y.delegate("mousedown", Y.bind(this._markerEventDispatcher, this), cb, markerClassName); 1728 Y.delegate("mouseup", Y.bind(this._markerEventDispatcher, this), cb, markerClassName); 1729 Y.delegate("mouseleave", Y.bind(this._markerEventDispatcher, this), cb, markerClassName); 1730 Y.delegate("click", Y.bind(this._markerEventDispatcher, this), cb, markerClassName); 1731 Y.delegate("mousemove", Y.bind(this._positionTooltip, this), cb, markerClassName); 1732 } 1733 } 1734 else if(interactionType === "planar") 1735 { 1736 if(isTouch) 1737 { 1738 this._overlay.on("touchend", Y.bind(this._planarEventDispatcher, this)); 1739 } 1740 else 1741 { 1742 this._overlay.on("mousemove", Y.bind(this._planarEventDispatcher, this)); 1743 this.on("mouseout", this.hideTooltip); 1744 } 1745 } 1746 if(tt) 1747 { 1748 this.on("markerEvent:touchend", Y.bind(function(e) { 1749 var marker = e.series.get("markers")[e.index]; 1750 if(this._activeMarker && marker === this._activeMarker) 1751 { 1752 this._activeMarker = null; 1753 this.hideTooltip(e); 1754 } 1755 else 1756 { 1757 1758 this._activeMarker = marker; 1759 tt.markerEventHandler.apply(this, [e]); 1760 } 1761 }, this)); 1762 if(hideEvent && showEvent && hideEvent === showEvent) 1763 { 1764 this.on(interactionType + "Event:" + hideEvent, this.toggleTooltip); 1765 } 1766 else 1767 { 1768 if(showEvent) 1769 { 1770 this.on(interactionType + "Event:" + showEvent, tt[interactionType + "EventHandler"]); 1771 } 1772 if(hideEvent) 1773 { 1774 if(Y_Lang.isArray(hideEvent)) 1775 { 1776 len = hideEvent.length; 1777 for(; i < len; ++i) 1778 { 1779 this.on(interactionType + "Event:" + hideEvent[i], this.hideTooltip); 1780 } 1781 } 1782 this.on(interactionType + "Event:" + hideEvent, this.hideTooltip); 1783 } 1784 } 1785 } 1786 }, 1787 1788 /** 1789 * Event handler for marker events. 1790 * 1791 * @method _markerEventDispatcher 1792 * @param {Object} e Event object. 1793 * @private 1794 */ 1795 _markerEventDispatcher: function(e) 1796 { 1797 var type = e.type, 1798 cb = this.get("contentBox"), 1799 markerNode = e.currentTarget, 1800 strArr = markerNode.getAttribute("id").split("_"), 1801 index = strArr.pop(), 1802 seriesIndex = strArr.pop(), 1803 series = this.getSeries(parseInt(seriesIndex, 10)), 1804 items = this.getSeriesItems(series, index), 1805 isTouch = e && e.hasOwnProperty("changedTouches"), 1806 pageX = isTouch ? e.changedTouches[0].pageX : e.pageX, 1807 pageY = isTouch ? e.changedTouches[0].pageY : e.pageY, 1808 x = pageX - cb.getX(), 1809 y = pageY - cb.getY(); 1810 if(type === "mouseenter") 1811 { 1812 type = "mouseover"; 1813 } 1814 else if(type === "mouseleave") 1815 { 1816 type = "mouseout"; 1817 } 1818 series.updateMarkerState(type, index); 1819 e.halt(); 1820 /** 1821 * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mouseover event. 1822 * 1823 * 1824 * @event markerEvent:mouseover 1825 * @preventable false 1826 * @param {EventFacade} e Event facade with the following additional 1827 * properties: 1828 * <dl> 1829 * <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd> 1830 * <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd> 1831 * <dt>node</dt><dd>The dom node of the marker.</dd> 1832 * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd> 1833 * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd> 1834 * <dt>series</dt><dd>Reference to the series of the marker.</dd> 1835 * <dt>index</dt><dd>Index of the marker in the series.</dd> 1836 * <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd> 1837 * </dl> 1838 */ 1839 /** 1840 * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mouseout event. 1841 * 1842 * @event markerEvent:mouseout 1843 * @preventable false 1844 * @param {EventFacade} e Event facade with the following additional 1845 * properties: 1846 * <dl> 1847 * <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd> 1848 * <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd> 1849 * <dt>node</dt><dd>The dom node of the marker.</dd> 1850 * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd> 1851 * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd> 1852 * <dt>series</dt><dd>Reference to the series of the marker.</dd> 1853 * <dt>index</dt><dd>Index of the marker in the series.</dd> 1854 * <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd> 1855 * </dl> 1856 */ 1857 /** 1858 * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mousedown event. 1859 * 1860 * @event markerEvent:mousedown 1861 * @preventable false 1862 * @param {EventFacade} e Event facade with the following additional 1863 * properties: 1864 * <dl> 1865 * <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd> 1866 * <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd> 1867 * <dt>node</dt><dd>The dom node of the marker.</dd> 1868 * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd> 1869 * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd> 1870 * <dt>series</dt><dd>Reference to the series of the marker.</dd> 1871 * <dt>index</dt><dd>Index of the marker in the series.</dd> 1872 * <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd> 1873 * </dl> 1874 */ 1875 /** 1876 * Broadcasts when `interactionType` is set to `marker` and a series marker has received a mouseup event. 1877 * 1878 * @event markerEvent:mouseup 1879 * @preventable false 1880 * @param {EventFacade} e Event facade with the following additional 1881 * properties: 1882 * <dl> 1883 * <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd> 1884 * <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd> 1885 * <dt>node</dt><dd>The dom node of the marker.</dd> 1886 * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd> 1887 * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd> 1888 * <dt>series</dt><dd>Reference to the series of the marker.</dd> 1889 * <dt>index</dt><dd>Index of the marker in the series.</dd> 1890 * <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd> 1891 * </dl> 1892 */ 1893 /** 1894 * Broadcasts when `interactionType` is set to `marker` and a series marker has received a click event. 1895 * 1896 * @event markerEvent:click 1897 * @preventable false 1898 * @param {EventFacade} e Event facade with the following additional 1899 * properties: 1900 * <dl> 1901 * <dt>categoryItem</dt><dd>Hash containing information about the category `Axis`.</dd> 1902 * <dt>valueItem</dt><dd>Hash containing information about the value `Axis`.</dd> 1903 * <dt>node</dt><dd>The dom node of the marker.</dd> 1904 * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd> 1905 * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd> 1906 * <dt>pageX</dt><dd>The x location of the event on the page (including scroll)</dd> 1907 * <dt>pageY</dt><dd>The y location of the event on the page (including scroll)</dd> 1908 * <dt>series</dt><dd>Reference to the series of the marker.</dd> 1909 * <dt>index</dt><dd>Index of the marker in the series.</dd> 1910 * <dt>seriesIndex</dt><dd>The `order` of the marker's series.</dd> 1911 * <dt>originEvent</dt><dd>Underlying dom event.</dd> 1912 * </dl> 1913 */ 1914 this.fire("markerEvent:" + type, { 1915 originEvent: e, 1916 pageX:pageX, 1917 pageY:pageY, 1918 categoryItem:items.category, 1919 valueItem:items.value, 1920 node:markerNode, 1921 x:x, 1922 y:y, 1923 series:series, 1924 index:index, 1925 seriesIndex:seriesIndex 1926 }); 1927 }, 1928 1929 /** 1930 * Event handler for dataProviderChange. 1931 * 1932 * @method _dataProviderChangeHandler 1933 * @param {Object} e Event object. 1934 * @private 1935 */ 1936 _dataProviderChangeHandler: function(e) 1937 { 1938 var dataProvider = e.newVal, 1939 axes, 1940 i, 1941 axis; 1942 this._seriesIndex = -1; 1943 this._itemIndex = -1; 1944 if(this instanceof Y.CartesianChart) 1945 { 1946 this.set("axes", this.get("axes")); 1947 this.set("seriesCollection", this.get("seriesCollection")); 1948 } 1949 axes = this.get("axes"); 1950 if(axes) 1951 { 1952 for(i in axes) 1953 { 1954 if(axes.hasOwnProperty(i)) 1955 { 1956 axis = axes[i]; 1957 if(axis instanceof Y.Axis) 1958 { 1959 if(axis.get("position") !== "none") 1960 { 1961 this._addToAxesRenderQueue(axis); 1962 } 1963 axis.set("dataProvider", dataProvider); 1964 } 1965 } 1966 } 1967 } 1968 }, 1969 1970 /** 1971 * Event listener for toggling the tooltip. If a tooltip is visible, hide it. If not, it 1972 * will create and show a tooltip based on the event object. 1973 * 1974 * @method toggleTooltip 1975 * @param {Object} e Event object. 1976 */ 1977 toggleTooltip: function(e) 1978 { 1979 var tt = this.get("tooltip"); 1980 if(tt.visible) 1981 { 1982 this.hideTooltip(); 1983 } 1984 else 1985 { 1986 tt.markerEventHandler.apply(this, [e]); 1987 } 1988 }, 1989 1990 /** 1991 * Shows a tooltip 1992 * 1993 * @method _showTooltip 1994 * @param {String} msg Message to dispaly in the tooltip. 1995 * @param {Number} x x-coordinate 1996 * @param {Number} y y-coordinate 1997 * @private 1998 */ 1999 _showTooltip: function(msg, x, y) 2000 { 2001 var tt = this.get("tooltip"), 2002 node = tt.node; 2003 if(msg) 2004 { 2005 tt.visible = true; 2006 tt.setTextFunction(node, msg); 2007 node.setStyle("top", y + "px"); 2008 node.setStyle("left", x + "px"); 2009 node.setStyle("visibility", "visible"); 2010 } 2011 }, 2012 2013 /** 2014 * Positions the tooltip 2015 * 2016 * @method _positionTooltip 2017 * @param {Object} e Event object. 2018 * @private 2019 */ 2020 _positionTooltip: function(e) 2021 { 2022 var tt = this.get("tooltip"), 2023 node = tt.node, 2024 cb = this.get("contentBox"), 2025 x = (e.pageX + 10) - cb.getX(), 2026 y = (e.pageY + 10) - cb.getY(); 2027 if(node) 2028 { 2029 node.setStyle("left", x + "px"); 2030 node.setStyle("top", y + "px"); 2031 } 2032 }, 2033 2034 /** 2035 * Hides the default tooltip 2036 * 2037 * @method hideTooltip 2038 */ 2039 hideTooltip: function() 2040 { 2041 var tt = this.get("tooltip"), 2042 node = tt.node; 2043 tt.visible = false; 2044 node.set("innerHTML", ""); 2045 node.setStyle("left", -10000); 2046 node.setStyle("top", -10000); 2047 node.setStyle("visibility", "hidden"); 2048 }, 2049 2050 /** 2051 * Adds a tooltip to the dom. 2052 * 2053 * @method _addTooltip 2054 * @private 2055 */ 2056 _addTooltip: function() 2057 { 2058 var tt = this.get("tooltip"), 2059 id = this.get("id") + "_tooltip", 2060 cb = this.get("contentBox"), 2061 oldNode = DOCUMENT.getElementById(id); 2062 if(oldNode) 2063 { 2064 cb.removeChild(oldNode); 2065 } 2066 tt.node.set("id", id); 2067 tt.node.setStyle("visibility", "hidden"); 2068 cb.appendChild(tt.node); 2069 }, 2070 2071 /** 2072 * Updates the tooltip attribute. 2073 * 2074 * @method _updateTooltip 2075 * @param {Object} val Object containing properties for the tooltip. 2076 * @return Object 2077 * @private 2078 */ 2079 _updateTooltip: function(val) 2080 { 2081 var tt = this.get("tooltip") || this._getTooltip(), 2082 i, 2083 styles, 2084 node, 2085 props = { 2086 markerLabelFunction:"markerLabelFunction", 2087 planarLabelFunction:"planarLabelFunction", 2088 setTextFunction:"setTextFunction", 2089 showEvent:"showEvent", 2090 hideEvent:"hideEvent", 2091 markerEventHandler:"markerEventHandler", 2092 planarEventHandler:"planarEventHandler", 2093 show:"show" 2094 }; 2095 if(Y_Lang.isObject(val)) 2096 { 2097 styles = val.styles; 2098 if(val.node && tt.node) 2099 { 2100 tt.node.destroy(true); 2101 node = Y.one(val.node); 2102 } 2103 else 2104 { 2105 node = tt.node; 2106 } 2107 if(styles) 2108 { 2109 for(i in styles) 2110 { 2111 if(styles.hasOwnProperty(i)) 2112 { 2113 node.setStyle(i, styles[i]); 2114 } 2115 } 2116 } 2117 for(i in props) 2118 { 2119 if(val.hasOwnProperty(i)) 2120 { 2121 tt[i] = val[i]; 2122 } 2123 } 2124 tt.node = node; 2125 } 2126 return tt; 2127 }, 2128 2129 /** 2130 * Default getter for `tooltip` attribute. 2131 * 2132 * @method _getTooltip 2133 * @return Object 2134 * @private 2135 */ 2136 _getTooltip: function() 2137 { 2138 var node = DOCUMENT.createElement("div"), 2139 tooltipClass = _getClassName("chart-tooltip"), 2140 tt = { 2141 setTextFunction: this._setText, 2142 markerLabelFunction: this._tooltipLabelFunction, 2143 planarLabelFunction: this._planarLabelFunction, 2144 show: true, 2145 hideEvent: "mouseout", 2146 showEvent: "mouseover", 2147 markerEventHandler: function(e) 2148 { 2149 var tt = this.get("tooltip"), 2150 msg = tt.markerLabelFunction.apply(this, [e.categoryItem, e.valueItem, e.index, e.series, e.seriesIndex]); 2151 this._showTooltip(msg, e.x + 10, e.y + 10); 2152 }, 2153 planarEventHandler: function(e) 2154 { 2155 var tt = this.get("tooltip"), 2156 msg , 2157 categoryAxis = this.get("categoryAxis"); 2158 msg = tt.planarLabelFunction.apply(this, [categoryAxis, e.valueItem, e.index, e.items, e.seriesIndex]); 2159 this._showTooltip(msg, e.x + 10, e.y + 10); 2160 } 2161 }; 2162 node = Y.one(node); 2163 node.set("id", this.get("id") + "_tooltip"); 2164 node.setStyle("fontSize", "85%"); 2165 node.setStyle("opacity", "0.83"); 2166 node.setStyle("position", "absolute"); 2167 node.setStyle("paddingTop", "2px"); 2168 node.setStyle("paddingRight", "5px"); 2169 node.setStyle("paddingBottom", "4px"); 2170 node.setStyle("paddingLeft", "2px"); 2171 node.setStyle("backgroundColor", "#fff"); 2172 node.setStyle("border", "1px solid #dbdccc"); 2173 node.setStyle("pointerEvents", "none"); 2174 node.setStyle("zIndex", 3); 2175 node.setStyle("whiteSpace", "noWrap"); 2176 node.setStyle("visibility", "hidden"); 2177 node.addClass(tooltipClass); 2178 tt.node = Y.one(node); 2179 return tt; 2180 }, 2181 2182 /** 2183 * Formats tooltip text when `interactionType` is `planar`. 2184 * 2185 * @method _planarLabelFunction 2186 * @param {Axis} categoryAxis Reference to the categoryAxis of the chart. 2187 * @param {Array} valueItems Array of objects for each series that has a data point in the coordinate plane of the event. 2188 * Each object contains the following data: 2189 * <dl> 2190 * <dt>axis</dt><dd>The value axis of the series.</dd> 2191 * <dt>key</dt><dd>The key for the series.</dd> 2192 * <dt>value</dt><dd>The value for the series item.</dd> 2193 * <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd> 2194 * </dl> 2195 * @param {Number} index The index of the item within its series. 2196 * @param {Array} seriesArray Array of series instances for each value item. 2197 * @param {Number} seriesIndex The index of the series in the `seriesCollection`. 2198 * @return {HTMLElement} 2199 * @private 2200 */ 2201 _planarLabelFunction: function(categoryAxis, valueItems, index, seriesArray) 2202 { 2203 var msg = DOCUMENT.createElement("div"), 2204 valueItem, 2205 i = 0, 2206 len = seriesArray.length, 2207 axis, 2208 categoryValue, 2209 seriesValue, 2210 series; 2211 if(categoryAxis) 2212 { 2213 categoryValue = categoryAxis.get("labelFunction").apply( 2214 this, 2215 [categoryAxis.getKeyValueAt(this.get("categoryKey"), index), categoryAxis.get("labelFormat")] 2216 ); 2217 if(!Y_Lang.isObject(categoryValue)) 2218 { 2219 categoryValue = DOCUMENT.createTextNode(categoryValue); 2220 } 2221 msg.appendChild(categoryValue); 2222 } 2223 2224 for(; i < len; ++i) 2225 { 2226 series = seriesArray[i]; 2227 if(series.get("visible")) 2228 { 2229 valueItem = valueItems[i]; 2230 axis = valueItem.axis; 2231 seriesValue = axis.get("labelFunction").apply( 2232 this, 2233 [axis.getKeyValueAt(valueItem.key, index), axis.get("labelFormat")] 2234 ); 2235 msg.appendChild(DOCUMENT.createElement("br")); 2236 msg.appendChild(DOCUMENT.createTextNode(valueItem.displayName)); 2237 msg.appendChild(DOCUMENT.createTextNode(": ")); 2238 if(!Y_Lang.isObject(seriesValue)) 2239 { 2240 seriesValue = DOCUMENT.createTextNode(seriesValue); 2241 } 2242 msg.appendChild(seriesValue); 2243 } 2244 } 2245 return msg; 2246 }, 2247 2248 /** 2249 * Formats tooltip text when `interactionType` is `marker`. 2250 * 2251 * @method _tooltipLabelFunction 2252 * @param {Object} categoryItem An object containing the following: 2253 * <dl> 2254 * <dt>axis</dt><dd>The axis to which the category is bound.</dd> 2255 * <dt>displayName</dt><dd>The display name set to the category (defaults to key if not provided)</dd> 2256 * <dt>key</dt><dd>The key of the category.</dd> 2257 * <dt>value</dt><dd>The value of the category</dd> 2258 * </dl> 2259 * @param {Object} valueItem An object containing the following: 2260 * <dl> 2261 * <dt>axis</dt><dd>The axis to which the item's series is bound.</dd> 2262 * <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd> 2263 * <dt>key</dt><dd>The key for the series.</dd> 2264 * <dt>value</dt><dd>The value for the series item.</dd> 2265 * </dl> 2266 * @return {HTMLElement} 2267 * @private 2268 */ 2269 _tooltipLabelFunction: function(categoryItem, valueItem) 2270 { 2271 var msg = DOCUMENT.createElement("div"), 2272 categoryValue = categoryItem.axis.get("labelFunction").apply( 2273 this, 2274 [categoryItem.value, categoryItem.axis.get("labelFormat")] 2275 ), 2276 seriesValue = valueItem.axis.get("labelFunction").apply( 2277 this, 2278 [valueItem.value, valueItem.axis.get("labelFormat")] 2279 ); 2280 msg.appendChild(DOCUMENT.createTextNode(categoryItem.displayName)); 2281 msg.appendChild(DOCUMENT.createTextNode(": ")); 2282 if(!Y_Lang.isObject(categoryValue)) 2283 { 2284 categoryValue = DOCUMENT.createTextNode(categoryValue); 2285 } 2286 msg.appendChild(categoryValue); 2287 msg.appendChild(DOCUMENT.createElement("br")); 2288 msg.appendChild(DOCUMENT.createTextNode(valueItem.displayName)); 2289 msg.appendChild(DOCUMENT.createTextNode(": ")); 2290 if(!Y_Lang.isObject(seriesValue)) 2291 { 2292 seriesValue = DOCUMENT.createTextNode(seriesValue); 2293 } 2294 msg.appendChild(seriesValue); 2295 return msg; 2296 }, 2297 2298 /** 2299 * Event handler for the tooltipChange. 2300 * 2301 * @method _tooltipChangeHandler 2302 * @param {Object} e Event object. 2303 * @private 2304 */ 2305 _tooltipChangeHandler: function() 2306 { 2307 if(this.get("tooltip")) 2308 { 2309 var tt = this.get("tooltip"), 2310 node = tt.node, 2311 show = tt.show, 2312 cb = this.get("contentBox"); 2313 if(node && show) 2314 { 2315 if(!cb.contains(node)) 2316 { 2317 this._addTooltip(); 2318 } 2319 } 2320 } 2321 }, 2322 2323 /** 2324 * Updates the content of text field. This method writes a value into a text field using 2325 * `appendChild`. If the value is a `String`, it is converted to a `TextNode` first. 2326 * 2327 * @method _setText 2328 * @param label {HTMLElement} label to be updated 2329 * @param val {String} value with which to update the label 2330 * @private 2331 */ 2332 _setText: function(textField, val) 2333 { 2334 textField.empty(); 2335 if(Y_Lang.isNumber(val)) 2336 { 2337 val = val + ""; 2338 } 2339 else if(!val) 2340 { 2341 val = ""; 2342 } 2343 if(IS_STRING(val)) 2344 { 2345 val = DOCUMENT.createTextNode(val); 2346 } 2347 textField.appendChild(val); 2348 }, 2349 2350 /** 2351 * Returns all the keys contained in a `dataProvider`. 2352 * 2353 * @method _getAllKeys 2354 * @param {Array} dp Collection of objects to be parsed. 2355 * @return Object 2356 */ 2357 _getAllKeys: function(dp) 2358 { 2359 var i = 0, 2360 len = dp.length, 2361 item, 2362 key, 2363 keys = {}; 2364 for(; i < len; ++i) 2365 { 2366 item = dp[i]; 2367 for(key in item) 2368 { 2369 if(item.hasOwnProperty(key)) 2370 { 2371 keys[key] = true; 2372 } 2373 } 2374 } 2375 return keys; 2376 }, 2377 2378 /** 2379 * Constructs seriesKeys if not explicitly specified. 2380 * 2381 * @method _buildSeriesKeys 2382 * @param {Array} dataProvider The dataProvider for the chart. 2383 * @return Array 2384 * @private 2385 */ 2386 _buildSeriesKeys: function(dataProvider) 2387 { 2388 var allKeys, 2389 catKey = this.get("categoryKey"), 2390 keys = [], 2391 i; 2392 if(this._seriesKeysExplicitlySet) 2393 { 2394 return this._seriesKeys; 2395 } 2396 allKeys = this._getAllKeys(dataProvider); 2397 for(i in allKeys) 2398 { 2399 if(allKeys.hasOwnProperty(i) && i !== catKey) 2400 { 2401 keys.push(i); 2402 } 2403 } 2404 return keys; 2405 } 2406 }; 2407 Y.ChartBase = ChartBase; 2408 /** 2409 * The CartesianChart class creates a chart with horizontal and vertical axes. 2410 * 2411 * @class CartesianChart 2412 * @extends ChartBase 2413 * @constructor 2414 * @submodule charts-base 2415 */ 2416 Y.CartesianChart = Y.Base.create("cartesianChart", Y.Widget, [Y.ChartBase, Y.Renderer], { 2417 /** 2418 * @method renderUI 2419 * @private 2420 */ 2421 renderUI: function() 2422 { 2423 var bb = this.get("boundingBox"), 2424 cb = this.get("contentBox"), 2425 tt = this.get("tooltip"), 2426 overlayClass = _getClassName("overlay"); 2427 //move the position = absolute logic to a class file 2428 bb.setStyle("position", "absolute"); 2429 cb.setStyle("position", "absolute"); 2430 this._addAxes(); 2431 this._addGridlines(); 2432 this._addSeries(); 2433 if(tt && tt.show) 2434 { 2435 this._addTooltip(); 2436 } 2437 if(this.get("interactionType") === "planar") 2438 { 2439 this._overlay = Y.Node.create("<div></div>"); 2440 this._overlay.set("id", this.get("id") + "_overlay"); 2441 this._overlay.setStyle("position", "absolute"); 2442 this._overlay.setStyle("background", "#fff"); 2443 this._overlay.setStyle("opacity", 0); 2444 this._overlay.addClass(overlayClass); 2445 this._overlay.setStyle("zIndex", 4); 2446 cb.append(this._overlay); 2447 } 2448 this._setAriaElements(bb, cb); 2449 this._redraw(); 2450 }, 2451 2452 /** 2453 * When `interactionType` is set to `planar`, listens for mouse move events and fires `planarEvent:mouseover` or `planarEvent:mouseout` 2454 * depending on the position of the mouse in relation to data points on the `Chart`. 2455 * 2456 * @method _planarEventDispatcher 2457 * @param {Object} e Event object. 2458 * @private 2459 */ 2460 _planarEventDispatcher: function(e) 2461 { 2462 var graph = this.get("graph"), 2463 bb = this.get("boundingBox"), 2464 cb = graph.get("contentBox"), 2465 isTouch = e && e.hasOwnProperty("changedTouches"), 2466 pageX = isTouch ? e.changedTouches[0].pageX : e.pageX, 2467 pageY = isTouch ? e.changedTouches[0].pageY : e.pageY, 2468 posX = pageX - bb.getX(), 2469 posY = pageY - bb.getY(), 2470 offset = { 2471 x: pageX - cb.getX(), 2472 y: pageY - cb.getY() 2473 }, 2474 sc = graph.get("seriesCollection"), 2475 series, 2476 i = 0, 2477 index, 2478 oldIndex = this._selectedIndex, 2479 item, 2480 items = [], 2481 categoryItems = [], 2482 valueItems = [], 2483 direction = this.get("direction"), 2484 hasMarkers, 2485 catAxis, 2486 valAxis, 2487 coord, 2488 //data columns and area data could be created on a graph level 2489 markerPlane, 2490 len, 2491 coords; 2492 e.halt(true); 2493 if(direction === "horizontal") 2494 { 2495 catAxis = "x"; 2496 valAxis = "y"; 2497 } 2498 else 2499 { 2500 valAxis = "x"; 2501 catAxis = "y"; 2502 } 2503 coord = offset[catAxis]; 2504 if(sc) 2505 { 2506 len = sc.length; 2507 while(i < len && !markerPlane) 2508 { 2509 if(sc[i]) 2510 { 2511 markerPlane = sc[i].get(catAxis + "MarkerPlane"); 2512 } 2513 i++; 2514 } 2515 } 2516 if(markerPlane) 2517 { 2518 len = markerPlane.length; 2519 for(i = 0; i < len; ++i) 2520 { 2521 if(coord <= markerPlane[i].end && coord >= markerPlane[i].start) 2522 { 2523 index = i; 2524 break; 2525 } 2526 } 2527 len = sc.length; 2528 for(i = 0; i < len; ++i) 2529 { 2530 series = sc[i]; 2531 coords = series.get(valAxis + "coords"); 2532 hasMarkers = series.get("markers"); 2533 if(hasMarkers && !isNaN(oldIndex) && oldIndex > -1) 2534 { 2535 series.updateMarkerState("mouseout", oldIndex); 2536 } 2537 if(coords && coords[index] > -1) 2538 { 2539 if(hasMarkers && !isNaN(index) && index > -1) 2540 { 2541 series.updateMarkerState("mouseover", index); 2542 } 2543 item = this.getSeriesItems(series, index); 2544 categoryItems.push(item.category); 2545 valueItems.push(item.value); 2546 items.push(series); 2547 } 2548 2549 } 2550 this._selectedIndex = index; 2551 2552 /** 2553 * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseover event. 2554 * 2555 * 2556 * @event planarEvent:mouseover 2557 * @preventable false 2558 * @param {EventFacade} e Event facade with the following additional 2559 * properties: 2560 * <dl> 2561 * <dt>categoryItem</dt><dd>An array of hashes, each containing information about the category `Axis` of each marker 2562 * whose plane has been intersected.</dd> 2563 * <dt>valueItem</dt><dd>An array of hashes, each containing information about the value `Axis` of each marker whose 2564 * plane has been intersected.</dd> 2565 * <dt>x</dt><dd>The x-coordinate of the mouse in relation to the Chart.</dd> 2566 * <dt>y</dt><dd>The y-coordinate of the mouse in relation to the Chart.</dd> 2567 * <dt>pageX</dt><dd>The x location of the event on the page (including scroll)</dd> 2568 * <dt>pageY</dt><dd>The y location of the event on the page (including scroll)</dd> 2569 * <dt>items</dt><dd>An array including all the series which contain a marker whose plane has been intersected.</dd> 2570 * <dt>index</dt><dd>Index of the markers in their respective series.</dd> 2571 * <dt>originEvent</dt><dd>Underlying dom event.</dd> 2572 * </dl> 2573 */ 2574 /** 2575 * Broadcasts when `interactionType` is set to `planar` and a series' marker plane has received a mouseout event. 2576 * 2577 * @event planarEvent:mouseout 2578 * @preventable false 2579 * @param {EventFacade} e 2580 */ 2581 if(index > -1) 2582 { 2583 this.fire("planarEvent:mouseover", { 2584 categoryItem:categoryItems, 2585 valueItem:valueItems, 2586 x:posX, 2587 y:posY, 2588 pageX:pageX, 2589 pageY:pageY, 2590 items:items, 2591 index:index, 2592 originEvent:e 2593 }); 2594 } 2595 else 2596 { 2597 this.fire("planarEvent:mouseout"); 2598 } 2599 } 2600 }, 2601 2602 /** 2603 * Indicates the default series type for the chart. 2604 * 2605 * @property _type 2606 * @type {String} 2607 * @private 2608 */ 2609 _type: "combo", 2610 2611 /** 2612 * Queue of axes instances that will be updated. This method is used internally to determine when all axes have been updated. 2613 * 2614 * @property _itemRenderQueue 2615 * @type Array 2616 * @private 2617 */ 2618 _itemRenderQueue: null, 2619 2620 /** 2621 * Adds an `Axis` instance to the `_itemRenderQueue`. 2622 * 2623 * @method _addToAxesRenderQueue 2624 * @param {Axis} axis An `Axis` instance. 2625 * @private 2626 */ 2627 _addToAxesRenderQueue: function(axis) 2628 { 2629 if(!this._itemRenderQueue) 2630 { 2631 this._itemRenderQueue = []; 2632 } 2633 if(Y.Array.indexOf(this._itemRenderQueue, axis) < 0) 2634 { 2635 this._itemRenderQueue.push(axis); 2636 } 2637 }, 2638 2639 /** 2640 * Adds axis instance to the appropriate array based on position 2641 * 2642 * @method _addToAxesCollection 2643 * @param {String} position The position of the axis 2644 * @param {Axis} axis The `Axis` instance 2645 */ 2646 _addToAxesCollection: function(position, axis) 2647 { 2648 var axesCollection = this.get(position + "AxesCollection"); 2649 if(!axesCollection) 2650 { 2651 axesCollection = []; 2652 this.set(position + "AxesCollection", axesCollection); 2653 } 2654 axesCollection.push(axis); 2655 }, 2656 2657 /** 2658 * Returns the default value for the `seriesCollection` attribute. 2659 * 2660 * @method _getDefaultSeriesCollection 2661 * @param {Array} val Array containing either `CartesianSeries` instances or objects containing data to construct series instances. 2662 * @return Array 2663 * @private 2664 */ 2665 _getDefaultSeriesCollection: function() 2666 { 2667 var seriesCollection, 2668 dataProvider = this.get("dataProvider"); 2669 if(dataProvider) 2670 { 2671 seriesCollection = this._parseSeriesCollection(); 2672 } 2673 return seriesCollection; 2674 }, 2675 2676 /** 2677 * Parses and returns a series collection from an object and default properties. 2678 * 2679 * @method _parseSeriesCollection 2680 * @param {Object} val Object contain properties for series being set. 2681 * @return Object 2682 * @private 2683 */ 2684 _parseSeriesCollection: function(val) 2685 { 2686 var dir = this.get("direction"), 2687 seriesStyles = this.get("styles").series, 2688 stylesAreArray = seriesStyles && Y_Lang.isArray(seriesStyles), 2689 stylesIndex, 2690 setStyles, 2691 globalStyles, 2692 sc = [], 2693 catAxis, 2694 valAxis, 2695 tempKeys = [], 2696 series, 2697 seriesKeys = this.get("seriesKeys").concat(), 2698 i, 2699 index, 2700 l, 2701 type = this.get("type"), 2702 key, 2703 catKey, 2704 seriesKey, 2705 graph, 2706 orphans = [], 2707 categoryKey = this.get("categoryKey"), 2708 showMarkers = this.get("showMarkers"), 2709 showAreaFill = this.get("showAreaFill"), 2710 showLines = this.get("showLines"); 2711 val = val ? val.concat() : []; 2712 if(dir === "vertical") 2713 { 2714 catAxis = "yAxis"; 2715 catKey = "yKey"; 2716 valAxis = "xAxis"; 2717 seriesKey = "xKey"; 2718 } 2719 else 2720 { 2721 catAxis = "xAxis"; 2722 catKey = "xKey"; 2723 valAxis = "yAxis"; 2724 seriesKey = "yKey"; 2725 } 2726 l = val.length; 2727 while(val && val.length > 0) 2728 { 2729 series = val.shift(); 2730 key = this._getBaseAttribute(series, seriesKey); 2731 if(key) 2732 { 2733 index = Y.Array.indexOf(seriesKeys, key); 2734 if(index > -1) 2735 { 2736 seriesKeys.splice(index, 1); 2737 tempKeys.push(key); 2738 sc.push(series); 2739 } 2740 else 2741 { 2742 orphans.push(series); 2743 } 2744 } 2745 else 2746 { 2747 orphans.push(series); 2748 } 2749 } 2750 while(orphans.length > 0) 2751 { 2752 series = orphans.shift(); 2753 if(seriesKeys.length > 0) 2754 { 2755 key = seriesKeys.shift(); 2756 this._setBaseAttribute(series, seriesKey, key); 2757 tempKeys.push(key); 2758 sc.push(series); 2759 } 2760 else if(series instanceof Y.CartesianSeries) 2761 { 2762 series.destroy(true); 2763 } 2764 } 2765 if(seriesKeys.length > 0) 2766 { 2767 tempKeys = tempKeys.concat(seriesKeys); 2768 } 2769 l = tempKeys.length; 2770 for(i = 0; i < l; ++i) 2771 { 2772 series = sc[i] || {type:type}; 2773 if(series instanceof Y.CartesianSeries) 2774 { 2775 this._parseSeriesAxes(series); 2776 } 2777 else 2778 { 2779 series[catKey] = series[catKey] || categoryKey; 2780 series[seriesKey] = series[seriesKey] || seriesKeys.shift(); 2781 series[catAxis] = this._getCategoryAxis(); 2782 series[valAxis] = this._getSeriesAxis(series[seriesKey]); 2783 2784 series.type = series.type || type; 2785 series.direction = series.direction || dir; 2786 2787 if(series.type === "combo" || 2788 series.type === "stackedcombo" || 2789 series.type === "combospline" || 2790 series.type === "stackedcombospline") 2791 { 2792 if(showAreaFill !== null) 2793 { 2794 series.showAreaFill = (series.showAreaFill !== null && series.showAreaFill !== undefined) ? 2795 series.showAreaFill : showAreaFill; 2796 } 2797 if(showMarkers !== null) 2798 { 2799 series.showMarkers = (series.showMarkers !== null && series.showMarkers !== undefined) ? series.showMarkers : showMarkers; 2800 } 2801 if(showLines !== null) 2802 { 2803 series.showLines = (series.showLines !== null && series.showLines !== undefined) ? series.showLines : showLines; 2804 } 2805 } 2806 if(seriesStyles) 2807 { 2808 stylesIndex = stylesAreArray ? i : series[seriesKey]; 2809 globalStyles = seriesStyles[stylesIndex]; 2810 if(globalStyles) 2811 { 2812 setStyles = series.styles; 2813 if(setStyles) 2814 { 2815 series.styles = this._mergeStyles(setStyles, globalStyles); 2816 } 2817 else 2818 { 2819 series.styles = globalStyles; 2820 } 2821 } 2822 } 2823 sc[i] = series; 2824 } 2825 } 2826 if(sc) 2827 { 2828 graph = this.get("graph"); 2829 graph.set("seriesCollection", sc); 2830 sc = graph.get("seriesCollection"); 2831 } 2832 return sc; 2833 }, 2834 2835 /** 2836 * Parse and sets the axes for a series instance. 2837 * 2838 * @method _parseSeriesAxes 2839 * @param {CartesianSeries} series A `CartesianSeries` instance. 2840 * @private 2841 */ 2842 _parseSeriesAxes: function(series) 2843 { 2844 var axes = this.get("axes"), 2845 xAxis = series.get("xAxis"), 2846 yAxis = series.get("yAxis"), 2847 YAxis = Y.Axis, 2848 axis; 2849 if(xAxis && !(xAxis instanceof YAxis) && Y_Lang.isString(xAxis) && axes.hasOwnProperty(xAxis)) 2850 { 2851 axis = axes[xAxis]; 2852 if(axis instanceof YAxis) 2853 { 2854 series.set("xAxis", axis); 2855 } 2856 } 2857 if(yAxis && !(yAxis instanceof YAxis) && Y_Lang.isString(yAxis) && axes.hasOwnProperty(yAxis)) 2858 { 2859 axis = axes[yAxis]; 2860 if(axis instanceof YAxis) 2861 { 2862 series.set("yAxis", axis); 2863 } 2864 } 2865 2866 }, 2867 2868 /** 2869 * Returns the category axis instance for the chart. 2870 * 2871 * @method _getCategoryAxis 2872 * @return Axis 2873 * @private 2874 */ 2875 _getCategoryAxis: function() 2876 { 2877 var axis, 2878 axes = this.get("axes"), 2879 categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"); 2880 axis = axes[categoryAxisName]; 2881 return axis; 2882 }, 2883 2884 /** 2885 * Returns the value axis for a series. 2886 * 2887 * @method _getSeriesAxis 2888 * @param {String} key The key value used to determine the axis instance. 2889 * @return Axis 2890 * @private 2891 */ 2892 _getSeriesAxis:function(key, axisName) 2893 { 2894 var axes = this.get("axes"), 2895 i, 2896 keys, 2897 axis; 2898 if(axes) 2899 { 2900 if(axisName && axes.hasOwnProperty(axisName)) 2901 { 2902 axis = axes[axisName]; 2903 } 2904 else 2905 { 2906 for(i in axes) 2907 { 2908 if(axes.hasOwnProperty(i)) 2909 { 2910 keys = axes[i].get("keys"); 2911 if(keys && keys.hasOwnProperty(key)) 2912 { 2913 axis = axes[i]; 2914 break; 2915 } 2916 } 2917 } 2918 } 2919 } 2920 return axis; 2921 }, 2922 2923 /** 2924 * Gets an attribute from an object, using a getter for Base objects and a property for object 2925 * literals. Used for determining attributes from series/axis references which can be an actual class instance 2926 * or a hash of properties that will be used to create a class instance. 2927 * 2928 * @method _getBaseAttribute 2929 * @param {Object} item Object or instance in which the attribute resides. 2930 * @param {String} key Attribute whose value will be returned. 2931 * @return Object 2932 * @private 2933 */ 2934 _getBaseAttribute: function(item, key) 2935 { 2936 if(item instanceof Y.Base) 2937 { 2938 return item.get(key); 2939 } 2940 if(item.hasOwnProperty(key)) 2941 { 2942 return item[key]; 2943 } 2944 return null; 2945 }, 2946 2947 /** 2948 * Sets an attribute on an object, using a setter of Base objects and a property for object 2949 * literals. Used for setting attributes on a Base class, either directly or to be stored in an object literal 2950 * for use at instantiation. 2951 * 2952 * @method _setBaseAttribute 2953 * @param {Object} item Object or instance in which the attribute resides. 2954 * @param {String} key Attribute whose value will be assigned. 2955 * @param {Object} value Value to be assigned to the attribute. 2956 * @private 2957 */ 2958 _setBaseAttribute: function(item, key, value) 2959 { 2960 if(item instanceof Y.Base) 2961 { 2962 item.set(key, value); 2963 } 2964 else 2965 { 2966 item[key] = value; 2967 } 2968 }, 2969 2970 /** 2971 * Creates `Axis` instances. 2972 * 2973 * @method _setAxes 2974 * @param {Object} val Object containing `Axis` instances or objects in which to construct `Axis` instances. 2975 * @return Object 2976 * @private 2977 */ 2978 _setAxes: function(val) 2979 { 2980 var hash = this._parseAxes(val), 2981 axes = {}, 2982 axesAttrs = { 2983 edgeOffset: "edgeOffset", 2984 calculateEdgeOffset: "calculateEdgeOffset", 2985 position: "position", 2986 overlapGraph:"overlapGraph", 2987 labelValues: "labelValues", 2988 hideFirstMajorUnit: "hideFirstMajorUnit", 2989 hideLastMajorUnit: "hideLastMajorUnit", 2990 labelFunction:"labelFunction", 2991 labelFunctionScope:"labelFunctionScope", 2992 labelFormat:"labelFormat", 2993 appendLabelFunction: "appendLabelFunction", 2994 appendTitleFunction: "appendTitleFunction", 2995 maximum:"maximum", 2996 minimum:"minimum", 2997 roundingMethod:"roundingMethod", 2998 alwaysShowZero:"alwaysShowZero", 2999 scaleType: "scaleType", 3000 title:"title", 3001 width:"width", 3002 height:"height" 3003 }, 3004 dp = this.get("dataProvider"), 3005 ai, 3006 i, 3007 pos, 3008 axis, 3009 axisPosition, 3010 dh, 3011 AxisClass, 3012 config, 3013 axesCollection; 3014 for(i in hash) 3015 { 3016 if(hash.hasOwnProperty(i)) 3017 { 3018 dh = hash[i]; 3019 if(dh instanceof Y.Axis) 3020 { 3021 axis = dh; 3022 } 3023 else 3024 { 3025 axis = null; 3026 config = {}; 3027 config.dataProvider = dh.dataProvider || dp; 3028 config.keys = dh.keys; 3029 3030 if(dh.hasOwnProperty("roundingUnit")) 3031 { 3032 config.roundingUnit = dh.roundingUnit; 3033 } 3034 pos = dh.position; 3035 if(dh.styles) 3036 { 3037 config.styles = dh.styles; 3038 } 3039 config.position = dh.position; 3040 for(ai in axesAttrs) 3041 { 3042 if(axesAttrs.hasOwnProperty(ai) && dh.hasOwnProperty(ai)) 3043 { 3044 config[ai] = dh[ai]; 3045 } 3046 } 3047 3048 //only check for existing axis if we constructed the default axes already 3049 if(val) 3050 { 3051 axis = this.getAxisByKey(i); 3052 } 3053 3054 if(axis && axis instanceof Y.Axis) 3055 { 3056 axisPosition = axis.get("position"); 3057 if(pos !== axisPosition) 3058 { 3059 if(axisPosition !== "none") 3060 { 3061 axesCollection = this.get(axisPosition + "AxesCollection"); 3062 axesCollection.splice(Y.Array.indexOf(axesCollection, axis), 1); 3063 } 3064 if(pos !== "none") 3065 { 3066 this._addToAxesCollection(pos, axis); 3067 } 3068 } 3069 axis.setAttrs(config); 3070 } 3071 else 3072 { 3073 AxisClass = this._getAxisClass(dh.type); 3074 axis = new AxisClass(config); 3075 axis.after("axisRendered", Y.bind(this._itemRendered, this)); 3076 } 3077 } 3078 3079 if(axis) 3080 { 3081 axesCollection = this.get(pos + "AxesCollection"); 3082 if(axesCollection && Y.Array.indexOf(axesCollection, axis) > 0) 3083 { 3084 axis.set("overlapGraph", false); 3085 } 3086 axes[i] = axis; 3087 } 3088 } 3089 } 3090 return axes; 3091 }, 3092 3093 /** 3094 * Adds axes to the chart. 3095 * 3096 * @method _addAxes 3097 * @private 3098 */ 3099 _addAxes: function() 3100 { 3101 var axes = this.get("axes"), 3102 i, 3103 axis, 3104 pos, 3105 w = this.get("width"), 3106 h = this.get("height"), 3107 node = Y.Node.one(this._parentNode); 3108 if(!this._axesCollection) 3109 { 3110 this._axesCollection = []; 3111 } 3112 for(i in axes) 3113 { 3114 if(axes.hasOwnProperty(i)) 3115 { 3116 axis = axes[i]; 3117 if(axis instanceof Y.Axis) 3118 { 3119 if(!w) 3120 { 3121 this.set("width", node.get("offsetWidth")); 3122 w = this.get("width"); 3123 } 3124 if(!h) 3125 { 3126 this.set("height", node.get("offsetHeight")); 3127 h = this.get("height"); 3128 } 3129 this._addToAxesRenderQueue(axis); 3130 pos = axis.get("position"); 3131 if(!this.get(pos + "AxesCollection")) 3132 { 3133 this.set(pos + "AxesCollection", [axis]); 3134 } 3135 else 3136 { 3137 this.get(pos + "AxesCollection").push(axis); 3138 } 3139 this._axesCollection.push(axis); 3140 if(axis.get("keys").hasOwnProperty(this.get("categoryKey"))) 3141 { 3142 this.set("categoryAxis", axis); 3143 } 3144 axis.render(this.get("contentBox")); 3145 } 3146 } 3147 } 3148 }, 3149 3150 /** 3151 * Renders the Graph. 3152 * 3153 * @method _addSeries 3154 * @private 3155 */ 3156 _addSeries: function() 3157 { 3158 var graph = this.get("graph"); 3159 graph.render(this.get("contentBox")); 3160 3161 }, 3162 3163 /** 3164 * Adds gridlines to the chart. 3165 * 3166 * @method _addGridlines 3167 * @private 3168 */ 3169 _addGridlines: function() 3170 { 3171 var graph = this.get("graph"), 3172 hgl = this.get("horizontalGridlines"), 3173 vgl = this.get("verticalGridlines"), 3174 direction = this.get("direction"), 3175 leftAxesCollection = this.get("leftAxesCollection"), 3176 rightAxesCollection = this.get("rightAxesCollection"), 3177 bottomAxesCollection = this.get("bottomAxesCollection"), 3178 topAxesCollection = this.get("topAxesCollection"), 3179 seriesAxesCollection, 3180 catAxis = this.get("categoryAxis"), 3181 hAxis, 3182 vAxis; 3183 if(this._axesCollection) 3184 { 3185 seriesAxesCollection = this._axesCollection.concat(); 3186 seriesAxesCollection.splice(Y.Array.indexOf(seriesAxesCollection, catAxis), 1); 3187 } 3188 if(hgl) 3189 { 3190 if(leftAxesCollection && leftAxesCollection[0]) 3191 { 3192 hAxis = leftAxesCollection[0]; 3193 } 3194 else if(rightAxesCollection && rightAxesCollection[0]) 3195 { 3196 hAxis = rightAxesCollection[0]; 3197 } 3198 else 3199 { 3200 hAxis = direction === "horizontal" ? catAxis : seriesAxesCollection[0]; 3201 } 3202 if(!this._getBaseAttribute(hgl, "axis") && hAxis) 3203 { 3204 this._setBaseAttribute(hgl, "axis", hAxis); 3205 } 3206 if(this._getBaseAttribute(hgl, "axis")) 3207 { 3208 graph.set("horizontalGridlines", hgl); 3209 } 3210 } 3211 if(vgl) 3212 { 3213 if(bottomAxesCollection && bottomAxesCollection[0]) 3214 { 3215 vAxis = bottomAxesCollection[0]; 3216 } 3217 else if (topAxesCollection && topAxesCollection[0]) 3218 { 3219 vAxis = topAxesCollection[0]; 3220 } 3221 else 3222 { 3223 vAxis = direction === "vertical" ? catAxis : seriesAxesCollection[0]; 3224 } 3225 if(!this._getBaseAttribute(vgl, "axis") && vAxis) 3226 { 3227 this._setBaseAttribute(vgl, "axis", vAxis); 3228 } 3229 if(this._getBaseAttribute(vgl, "axis")) 3230 { 3231 graph.set("verticalGridlines", vgl); 3232 } 3233 } 3234 }, 3235 3236 /** 3237 * Default Function for the axes attribute. 3238 * 3239 * @method _getDefaultAxes 3240 * @return Object 3241 * @private 3242 */ 3243 _getDefaultAxes: function() 3244 { 3245 var axes; 3246 if(this.get("dataProvider")) 3247 { 3248 axes = this._parseAxes(); 3249 } 3250 return axes; 3251 }, 3252 3253 /** 3254 * Generates and returns a key-indexed object containing `Axis` instances or objects used to create `Axis` instances. 3255 * 3256 * @method _parseAxes 3257 * @param {Object} axes Object containing `Axis` instances or `Axis` attributes. 3258 * @return Object 3259 * @private 3260 */ 3261 _parseAxes: function(axes) 3262 { 3263 var catKey = this.get("categoryKey"), 3264 axis, 3265 attr, 3266 keys, 3267 newAxes = {}, 3268 claimedKeys = [], 3269 newKeys = [], 3270 categoryAxisName = this.get("categoryAxisName") || this.get("categoryKey"), 3271 valueAxisName = this.get("valueAxisName"), 3272 seriesKeys = this.get("seriesKeys").concat(), 3273 i, 3274 l, 3275 ii, 3276 ll, 3277 cIndex, 3278 direction = this.get("direction"), 3279 seriesPosition, 3280 categoryPosition, 3281 valueAxes = [], 3282 seriesAxis = this.get("stacked") ? "stacked" : "numeric"; 3283 if(direction === "vertical") 3284 { 3285 seriesPosition = "bottom"; 3286 categoryPosition = "left"; 3287 } 3288 else 3289 { 3290 seriesPosition = "left"; 3291 categoryPosition = "bottom"; 3292 } 3293 if(axes) 3294 { 3295 for(i in axes) 3296 { 3297 if(axes.hasOwnProperty(i)) 3298 { 3299 axis = axes[i]; 3300 keys = this._getBaseAttribute(axis, "keys"); 3301 attr = this._getBaseAttribute(axis, "type"); 3302 if(attr === "time" || attr === "category") 3303 { 3304 categoryAxisName = i; 3305 this.set("categoryAxisName", i); 3306 if(Y_Lang.isArray(keys) && keys.length > 0) 3307 { 3308 catKey = keys[0]; 3309 this.set("categoryKey", catKey); 3310 } 3311 newAxes[i] = axis; 3312 } 3313 else if(i === categoryAxisName) 3314 { 3315 newAxes[i] = axis; 3316 } 3317 else 3318 { 3319 newAxes[i] = axis; 3320 if(i !== valueAxisName && keys && Y_Lang.isArray(keys)) 3321 { 3322 ll = keys.length; 3323 for(ii = 0; ii < ll; ++ii) 3324 { 3325 claimedKeys.push(keys[ii]); 3326 } 3327 valueAxes.push(newAxes[i]); 3328 } 3329 if(!(this._getBaseAttribute(newAxes[i], "type"))) 3330 { 3331 this._setBaseAttribute(newAxes[i], "type", seriesAxis); 3332 } 3333 if(!(this._getBaseAttribute(newAxes[i], "position"))) 3334 { 3335 this._setBaseAttribute( 3336 newAxes[i], 3337 "position", 3338 this._getDefaultAxisPosition(newAxes[i], valueAxes, seriesPosition) 3339 ); 3340 } 3341 } 3342 } 3343 } 3344 } 3345 cIndex = Y.Array.indexOf(seriesKeys, catKey); 3346 if(cIndex > -1) 3347 { 3348 seriesKeys.splice(cIndex, 1); 3349 } 3350 l = seriesKeys.length; 3351 for(i = 0; i < l; ++i) 3352 { 3353 cIndex = Y.Array.indexOf(claimedKeys, seriesKeys[i]); 3354 if(cIndex > -1) 3355 { 3356 newKeys = newKeys.concat(claimedKeys.splice(cIndex, 1)); 3357 } 3358 } 3359 claimedKeys = newKeys.concat(claimedKeys); 3360 l = claimedKeys.length; 3361 for(i = 0; i < l; i = i + 1) 3362 { 3363 cIndex = Y.Array.indexOf(seriesKeys, claimedKeys[i]); 3364 if(cIndex > -1) 3365 { 3366 seriesKeys.splice(cIndex, 1); 3367 } 3368 } 3369 if(!newAxes.hasOwnProperty(categoryAxisName)) 3370 { 3371 newAxes[categoryAxisName] = {}; 3372 } 3373 if(!(this._getBaseAttribute(newAxes[categoryAxisName], "keys"))) 3374 { 3375 this._setBaseAttribute(newAxes[categoryAxisName], "keys", [catKey]); 3376 } 3377 3378 if(!(this._getBaseAttribute(newAxes[categoryAxisName], "position"))) 3379 { 3380 this._setBaseAttribute(newAxes[categoryAxisName], "position", categoryPosition); 3381 } 3382 3383 if(!(this._getBaseAttribute(newAxes[categoryAxisName], "type"))) 3384 { 3385 this._setBaseAttribute(newAxes[categoryAxisName], "type", this.get("categoryType")); 3386 } 3387 if(!newAxes.hasOwnProperty(valueAxisName) && seriesKeys && seriesKeys.length > 0) 3388 { 3389 newAxes[valueAxisName] = {keys:seriesKeys}; 3390 valueAxes.push(newAxes[valueAxisName]); 3391 } 3392 if(claimedKeys.length > 0) 3393 { 3394 if(seriesKeys.length > 0) 3395 { 3396 seriesKeys = claimedKeys.concat(seriesKeys); 3397 } 3398 else 3399 { 3400 seriesKeys = claimedKeys; 3401 } 3402 } 3403 if(newAxes.hasOwnProperty(valueAxisName)) 3404 { 3405 if(!(this._getBaseAttribute(newAxes[valueAxisName], "position"))) 3406 { 3407 this._setBaseAttribute( 3408 newAxes[valueAxisName], 3409 "position", 3410 this._getDefaultAxisPosition(newAxes[valueAxisName], valueAxes, seriesPosition) 3411 ); 3412 } 3413 this._setBaseAttribute(newAxes[valueAxisName], "type", seriesAxis); 3414 this._setBaseAttribute(newAxes[valueAxisName], "keys", seriesKeys); 3415 } 3416 if(!this._wereSeriesKeysExplicitlySet()) 3417 { 3418 this.set("seriesKeys", seriesKeys, {src: "internal"}); 3419 } 3420 return newAxes; 3421 }, 3422 3423 /** 3424 * Determines the position of an axis when one is not specified. 3425 * 3426 * @method _getDefaultAxisPosition 3427 * @param {Axis} axis `Axis` instance. 3428 * @param {Array} valueAxes Array of `Axis` instances. 3429 * @param {String} position Default position depending on the direction of the chart and type of axis. 3430 * @return String 3431 * @private 3432 */ 3433 _getDefaultAxisPosition: function(axis, valueAxes, position) 3434 { 3435 var direction = this.get("direction"), 3436 i = Y.Array.indexOf(valueAxes, axis); 3437 3438 if(valueAxes[i - 1] && valueAxes[i - 1].position) 3439 { 3440 if(direction === "horizontal") 3441 { 3442 if(valueAxes[i - 1].position === "left") 3443 { 3444 position = "right"; 3445 } 3446 else if(valueAxes[i - 1].position === "right") 3447 { 3448 position = "left"; 3449 } 3450 } 3451 else 3452 { 3453 if (valueAxes[i -1].position === "bottom") 3454 { 3455 position = "top"; 3456 } 3457 else 3458 { 3459 position = "bottom"; 3460 } 3461 } 3462 } 3463 return position; 3464 }, 3465 3466 3467 /** 3468 * Returns an object literal containing a categoryItem and a valueItem for a given series index. Below is the structure of each: 3469 * 3470 * @method getSeriesItems 3471 * @param {CartesianSeries} series Reference to a series. 3472 * @param {Number} index Index of the specified item within a series. 3473 * @return Object An object literal containing the following: 3474 * 3475 * <dl> 3476 * <dt>categoryItem</dt><dd>Object containing the following data related to the category axis of the series. 3477 * <dl> 3478 * <dt>axis</dt><dd>Reference to the category axis of the series.</dd> 3479 * <dt>key</dt><dd>Category key for the series.</dd> 3480 * <dt>value</dt><dd>Value on the axis corresponding to the series index.</dd> 3481 * </dl> 3482 * </dd> 3483 * <dt>valueItem</dt><dd>Object containing the following data related to the category axis of the series. 3484 * <dl> 3485 * <dt>axis</dt><dd>Reference to the value axis of the series.</dd> 3486 * <dt>key</dt><dd>Value key for the series.</dd> 3487 * <dt>value</dt><dd>Value on the axis corresponding to the series index.</dd> 3488 * </dl> 3489 * </dd> 3490 * </dl> 3491 */ 3492 getSeriesItems: function(series, index) 3493 { 3494 var xAxis = series.get("xAxis"), 3495 yAxis = series.get("yAxis"), 3496 xKey = series.get("xKey"), 3497 yKey = series.get("yKey"), 3498 categoryItem, 3499 valueItem; 3500 if(this.get("direction") === "vertical") 3501 { 3502 categoryItem = { 3503 axis:yAxis, 3504 key:yKey, 3505 value:yAxis.getKeyValueAt(yKey, index) 3506 }; 3507 valueItem = { 3508 axis:xAxis, 3509 key:xKey, 3510 value: xAxis.getKeyValueAt(xKey, index) 3511 }; 3512 } 3513 else 3514 { 3515 valueItem = { 3516 axis:yAxis, 3517 key:yKey, 3518 value:yAxis.getKeyValueAt(yKey, index) 3519 }; 3520 categoryItem = { 3521 axis:xAxis, 3522 key:xKey, 3523 value: xAxis.getKeyValueAt(xKey, index) 3524 }; 3525 } 3526 categoryItem.displayName = series.get("categoryDisplayName"); 3527 valueItem.displayName = series.get("valueDisplayName"); 3528 categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index); 3529 valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index); 3530 return {category:categoryItem, value:valueItem}; 3531 }, 3532 3533 /** 3534 * Handler for sizeChanged event. 3535 * 3536 * @method _sizeChanged 3537 * @param {Object} e Event object. 3538 * @private 3539 */ 3540 _sizeChanged: function() 3541 { 3542 if(this._axesCollection) 3543 { 3544 var ac = this._axesCollection, 3545 i = 0, 3546 l = ac.length; 3547 for(; i < l; ++i) 3548 { 3549 this._addToAxesRenderQueue(ac[i]); 3550 } 3551 this._redraw(); 3552 } 3553 }, 3554 3555 /** 3556 * Returns the maximum distance in pixels that the extends outside the top bounds of all vertical axes. 3557 * 3558 * @method _getTopOverflow 3559 * @param {Array} set1 Collection of axes to check. 3560 * @param {Array} set2 Seconf collection of axes to check. 3561 * @param {Number} width Width of the axes 3562 * @return Number 3563 * @private 3564 */ 3565 _getTopOverflow: function(set1, set2, height) 3566 { 3567 var i = 0, 3568 len, 3569 overflow = 0, 3570 axis; 3571 if(set1) 3572 { 3573 len = set1.length; 3574 for(; i < len; ++i) 3575 { 3576 axis = set1[i]; 3577 overflow = Math.max( 3578 overflow, 3579 Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height) 3580 ); 3581 } 3582 } 3583 if(set2) 3584 { 3585 i = 0; 3586 len = set2.length; 3587 for(; i < len; ++i) 3588 { 3589 axis = set2[i]; 3590 overflow = Math.max( 3591 overflow, 3592 Math.abs(axis.getMaxLabelBounds().top) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height) 3593 ); 3594 } 3595 } 3596 return overflow; 3597 }, 3598 3599 /** 3600 * Returns the maximum distance in pixels that the extends outside the right bounds of all horizontal axes. 3601 * 3602 * @method _getRightOverflow 3603 * @param {Array} set1 Collection of axes to check. 3604 * @param {Array} set2 Seconf collection of axes to check. 3605 * @param {Number} width Width of the axes 3606 * @return Number 3607 * @private 3608 */ 3609 _getRightOverflow: function(set1, set2, width) 3610 { 3611 var i = 0, 3612 len, 3613 overflow = 0, 3614 axis; 3615 if(set1) 3616 { 3617 len = set1.length; 3618 for(; i < len; ++i) 3619 { 3620 axis = set1[i]; 3621 overflow = Math.max( 3622 overflow, 3623 axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width) 3624 ); 3625 } 3626 } 3627 if(set2) 3628 { 3629 i = 0; 3630 len = set2.length; 3631 for(; i < len; ++i) 3632 { 3633 axis = set2[i]; 3634 overflow = Math.max( 3635 overflow, 3636 axis.getMaxLabelBounds().right - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width) 3637 ); 3638 } 3639 } 3640 return overflow; 3641 }, 3642 3643 /** 3644 * Returns the maximum distance in pixels that the extends outside the left bounds of all horizontal axes. 3645 * 3646 * @method _getLeftOverflow 3647 * @param {Array} set1 Collection of axes to check. 3648 * @param {Array} set2 Seconf collection of axes to check. 3649 * @param {Number} width Width of the axes 3650 * @return Number 3651 * @private 3652 */ 3653 _getLeftOverflow: function(set1, set2, width) 3654 { 3655 var i = 0, 3656 len, 3657 overflow = 0, 3658 axis; 3659 if(set1) 3660 { 3661 len = set1.length; 3662 for(; i < len; ++i) 3663 { 3664 axis = set1[i]; 3665 overflow = Math.max( 3666 overflow, 3667 Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width) 3668 ); 3669 } 3670 } 3671 if(set2) 3672 { 3673 i = 0; 3674 len = set2.length; 3675 for(; i < len; ++i) 3676 { 3677 axis = set2[i]; 3678 overflow = Math.max( 3679 overflow, 3680 Math.abs(axis.getMinLabelBounds().left) - axis.getEdgeOffset(axis.get("styles").majorTicks.count, width) 3681 ); 3682 } 3683 } 3684 return overflow; 3685 }, 3686 3687 /** 3688 * Returns the maximum distance in pixels that the extends outside the bottom bounds of all vertical axes. 3689 * 3690 * @method _getBottomOverflow 3691 * @param {Array} set1 Collection of axes to check. 3692 * @param {Array} set2 Seconf collection of axes to check. 3693 * @param {Number} height Height of the axes 3694 * @return Number 3695 * @private 3696 */ 3697 _getBottomOverflow: function(set1, set2, height) 3698 { 3699 var i = 0, 3700 len, 3701 overflow = 0, 3702 axis; 3703 if(set1) 3704 { 3705 len = set1.length; 3706 for(; i < len; ++i) 3707 { 3708 axis = set1[i]; 3709 overflow = Math.max( 3710 overflow, 3711 axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height) 3712 ); 3713 } 3714 } 3715 if(set2) 3716 { 3717 i = 0; 3718 len = set2.length; 3719 for(; i < len; ++i) 3720 { 3721 axis = set2[i]; 3722 overflow = Math.max( 3723 overflow, 3724 axis.getMinLabelBounds().bottom - axis.getEdgeOffset(axis.get("styles").majorTicks.count, height) 3725 ); 3726 } 3727 } 3728 return overflow; 3729 }, 3730 3731 /** 3732 * Redraws and position all the components of the chart instance. 3733 * 3734 * @method _redraw 3735 * @private 3736 */ 3737 _redraw: function() 3738 { 3739 if(this._drawing) 3740 { 3741 this._callLater = true; 3742 return; 3743 } 3744 this._drawing = true; 3745 this._callLater = false; 3746 var w = this.get("width"), 3747 h = this.get("height"), 3748 leftPaneWidth = 0, 3749 rightPaneWidth = 0, 3750 topPaneHeight = 0, 3751 bottomPaneHeight = 0, 3752 leftAxesCollection = this.get("leftAxesCollection"), 3753 rightAxesCollection = this.get("rightAxesCollection"), 3754 topAxesCollection = this.get("topAxesCollection"), 3755 bottomAxesCollection = this.get("bottomAxesCollection"), 3756 i = 0, 3757 l, 3758 axis, 3759 graphOverflow = "visible", 3760 graph = this.get("graph"), 3761 topOverflow, 3762 bottomOverflow, 3763 leftOverflow, 3764 rightOverflow, 3765 graphWidth, 3766 graphHeight, 3767 graphX, 3768 graphY, 3769 allowContentOverflow = this.get("allowContentOverflow"), 3770 diff, 3771 rightAxesXCoords, 3772 leftAxesXCoords, 3773 topAxesYCoords, 3774 bottomAxesYCoords, 3775 graphRect = {}; 3776 if(leftAxesCollection) 3777 { 3778 leftAxesXCoords = []; 3779 l = leftAxesCollection.length; 3780 for(i = l - 1; i > -1; --i) 3781 { 3782 leftAxesXCoords.unshift(leftPaneWidth); 3783 leftPaneWidth += leftAxesCollection[i].get("width"); 3784 } 3785 } 3786 if(rightAxesCollection) 3787 { 3788 rightAxesXCoords = []; 3789 l = rightAxesCollection.length; 3790 i = 0; 3791 for(i = l - 1; i > -1; --i) 3792 { 3793 rightPaneWidth += rightAxesCollection[i].get("width"); 3794 rightAxesXCoords.unshift(w - rightPaneWidth); 3795 } 3796 } 3797 if(topAxesCollection) 3798 { 3799 topAxesYCoords = []; 3800 l = topAxesCollection.length; 3801 for(i = l - 1; i > -1; --i) 3802 { 3803 topAxesYCoords.unshift(topPaneHeight); 3804 topPaneHeight += topAxesCollection[i].get("height"); 3805 } 3806 } 3807 if(bottomAxesCollection) 3808 { 3809 bottomAxesYCoords = []; 3810 l = bottomAxesCollection.length; 3811 for(i = l - 1; i > -1; --i) 3812 { 3813 bottomPaneHeight += bottomAxesCollection[i].get("height"); 3814 bottomAxesYCoords.unshift(h - bottomPaneHeight); 3815 } 3816 } 3817 3818 graphWidth = w - (leftPaneWidth + rightPaneWidth); 3819 graphHeight = h - (bottomPaneHeight + topPaneHeight); 3820 graphRect.left = leftPaneWidth; 3821 graphRect.top = topPaneHeight; 3822 graphRect.bottom = h - bottomPaneHeight; 3823 graphRect.right = w - rightPaneWidth; 3824 if(!allowContentOverflow) 3825 { 3826 topOverflow = this._getTopOverflow(leftAxesCollection, rightAxesCollection); 3827 bottomOverflow = this._getBottomOverflow(leftAxesCollection, rightAxesCollection); 3828 leftOverflow = this._getLeftOverflow(bottomAxesCollection, topAxesCollection); 3829 rightOverflow = this._getRightOverflow(bottomAxesCollection, topAxesCollection); 3830 3831 diff = topOverflow - topPaneHeight; 3832 if(diff > 0) 3833 { 3834 graphRect.top = topOverflow; 3835 if(topAxesYCoords) 3836 { 3837 i = 0; 3838 l = topAxesYCoords.length; 3839 for(; i < l; ++i) 3840 { 3841 topAxesYCoords[i] += diff; 3842 } 3843 } 3844 } 3845 3846 diff = bottomOverflow - bottomPaneHeight; 3847 if(diff > 0) 3848 { 3849 graphRect.bottom = h - bottomOverflow; 3850 if(bottomAxesYCoords) 3851 { 3852 i = 0; 3853 l = bottomAxesYCoords.length; 3854 for(; i < l; ++i) 3855 { 3856 bottomAxesYCoords[i] -= diff; 3857 } 3858 } 3859 } 3860 3861 diff = leftOverflow - leftPaneWidth; 3862 if(diff > 0) 3863 { 3864 graphRect.left = leftOverflow; 3865 if(leftAxesXCoords) 3866 { 3867 i = 0; 3868 l = leftAxesXCoords.length; 3869 for(; i < l; ++i) 3870 { 3871 leftAxesXCoords[i] += diff; 3872 } 3873 } 3874 } 3875 3876 diff = rightOverflow - rightPaneWidth; 3877 if(diff > 0) 3878 { 3879 graphRect.right = w - rightOverflow; 3880 if(rightAxesXCoords) 3881 { 3882 i = 0; 3883 l = rightAxesXCoords.length; 3884 for(; i < l; ++i) 3885 { 3886 rightAxesXCoords[i] -= diff; 3887 } 3888 } 3889 } 3890 } 3891 graphWidth = graphRect.right - graphRect.left; 3892 graphHeight = graphRect.bottom - graphRect.top; 3893 graphX = graphRect.left; 3894 graphY = graphRect.top; 3895 if(topAxesCollection) 3896 { 3897 l = topAxesCollection.length; 3898 i = 0; 3899 for(; i < l; i++) 3900 { 3901 axis = topAxesCollection[i]; 3902 if(axis.get("width") !== graphWidth) 3903 { 3904 axis.set("width", graphWidth); 3905 } 3906 axis.get("boundingBox").setStyle("left", graphX + "px"); 3907 axis.get("boundingBox").setStyle("top", topAxesYCoords[i] + "px"); 3908 } 3909 if(axis._hasDataOverflow()) 3910 { 3911 graphOverflow = "hidden"; 3912 } 3913 } 3914 if(bottomAxesCollection) 3915 { 3916 l = bottomAxesCollection.length; 3917 i = 0; 3918 for(; i < l; i++) 3919 { 3920 axis = bottomAxesCollection[i]; 3921 if(axis.get("width") !== graphWidth) 3922 { 3923 axis.set("width", graphWidth); 3924 } 3925 axis.get("boundingBox").setStyle("left", graphX + "px"); 3926 axis.get("boundingBox").setStyle("top", bottomAxesYCoords[i] + "px"); 3927 } 3928 if(axis._hasDataOverflow()) 3929 { 3930 graphOverflow = "hidden"; 3931 } 3932 } 3933 if(leftAxesCollection) 3934 { 3935 l = leftAxesCollection.length; 3936 i = 0; 3937 for(; i < l; ++i) 3938 { 3939 axis = leftAxesCollection[i]; 3940 axis.get("boundingBox").setStyle("top", graphY + "px"); 3941 axis.get("boundingBox").setStyle("left", leftAxesXCoords[i] + "px"); 3942 if(axis.get("height") !== graphHeight) 3943 { 3944 axis.set("height", graphHeight); 3945 } 3946 } 3947 if(axis._hasDataOverflow()) 3948 { 3949 graphOverflow = "hidden"; 3950 } 3951 } 3952 if(rightAxesCollection) 3953 { 3954 l = rightAxesCollection.length; 3955 i = 0; 3956 for(; i < l; ++i) 3957 { 3958 axis = rightAxesCollection[i]; 3959 axis.get("boundingBox").setStyle("top", graphY + "px"); 3960 axis.get("boundingBox").setStyle("left", rightAxesXCoords[i] + "px"); 3961 if(axis.get("height") !== graphHeight) 3962 { 3963 axis.set("height", graphHeight); 3964 } 3965 } 3966 if(axis._hasDataOverflow()) 3967 { 3968 graphOverflow = "hidden"; 3969 } 3970 } 3971 this._drawing = false; 3972 if(this._callLater) 3973 { 3974 this._redraw(); 3975 return; 3976 } 3977 if(graph) 3978 { 3979 graph.get("boundingBox").setStyle("left", graphX + "px"); 3980 graph.get("boundingBox").setStyle("top", graphY + "px"); 3981 graph.set("width", graphWidth); 3982 graph.set("height", graphHeight); 3983 graph.get("boundingBox").setStyle("overflow", graphOverflow); 3984 } 3985 3986 if(this._overlay) 3987 { 3988 this._overlay.setStyle("left", graphX + "px"); 3989 this._overlay.setStyle("top", graphY + "px"); 3990 this._overlay.setStyle("width", graphWidth + "px"); 3991 this._overlay.setStyle("height", graphHeight + "px"); 3992 } 3993 }, 3994 3995 /** 3996 * Destructor implementation for the CartesianChart class. Calls destroy on all axes, series and the Graph instance. 3997 * Removes the tooltip and overlay HTML elements. 3998 * 3999 * @method destructor 4000 * @protected 4001 */ 4002 destructor: function() 4003 { 4004 var graph = this.get("graph"), 4005 i = 0, 4006 len, 4007 seriesCollection = this.get("seriesCollection"), 4008 axesCollection = this._axesCollection, 4009 tooltip = this.get("tooltip").node; 4010 if(this._description) 4011 { 4012 this._description.empty(); 4013 this._description.remove(true); 4014 } 4015 if(this._liveRegion) 4016 { 4017 this._liveRegion.empty(); 4018 this._liveRegion.remove(true); 4019 } 4020 len = seriesCollection ? seriesCollection.length : 0; 4021 for(; i < len; ++i) 4022 { 4023 if(seriesCollection[i] instanceof Y.CartesianSeries) 4024 { 4025 seriesCollection[i].destroy(true); 4026 } 4027 } 4028 len = axesCollection ? axesCollection.length : 0; 4029 for(i = 0; i < len; ++i) 4030 { 4031 if(axesCollection[i] instanceof Y.Axis) 4032 { 4033 axesCollection[i].destroy(true); 4034 } 4035 } 4036 if(graph) 4037 { 4038 graph.destroy(true); 4039 } 4040 if(tooltip) 4041 { 4042 tooltip.empty(); 4043 tooltip.remove(true); 4044 } 4045 if(this._overlay) 4046 { 4047 this._overlay.empty(); 4048 this._overlay.remove(true); 4049 } 4050 }, 4051 4052 /** 4053 * Returns the appropriate message based on the key press. 4054 * 4055 * @method _getAriaMessage 4056 * @param {Number} key The keycode that was pressed. 4057 * @return String 4058 */ 4059 _getAriaMessage: function(key) 4060 { 4061 var msg = "", 4062 series, 4063 items, 4064 categoryItem, 4065 valueItem, 4066 seriesIndex = this._seriesIndex, 4067 itemIndex = this._itemIndex, 4068 seriesCollection = this.get("seriesCollection"), 4069 len = seriesCollection.length, 4070 dataLength; 4071 if(key % 2 === 0) 4072 { 4073 if(len > 1) 4074 { 4075 if(key === 38) 4076 { 4077 seriesIndex = seriesIndex < 1 ? len - 1 : seriesIndex - 1; 4078 } 4079 else if(key === 40) 4080 { 4081 seriesIndex = seriesIndex >= len - 1 ? 0 : seriesIndex + 1; 4082 } 4083 this._itemIndex = -1; 4084 } 4085 else 4086 { 4087 seriesIndex = 0; 4088 } 4089 this._seriesIndex = seriesIndex; 4090 series = this.getSeries(parseInt(seriesIndex, 10)); 4091 msg = series.get("valueDisplayName") + " series."; 4092 } 4093 else 4094 { 4095 if(seriesIndex > -1) 4096 { 4097 msg = ""; 4098 series = this.getSeries(parseInt(seriesIndex, 10)); 4099 } 4100 else 4101 { 4102 seriesIndex = 0; 4103 this._seriesIndex = seriesIndex; 4104 series = this.getSeries(parseInt(seriesIndex, 10)); 4105 msg = series.get("valueDisplayName") + " series."; 4106 } 4107 dataLength = series._dataLength ? series._dataLength : 0; 4108 if(key === 37) 4109 { 4110 itemIndex = itemIndex > 0 ? itemIndex - 1 : dataLength - 1; 4111 } 4112 else if(key === 39) 4113 { 4114 itemIndex = itemIndex >= dataLength - 1 ? 0 : itemIndex + 1; 4115 } 4116 this._itemIndex = itemIndex; 4117 items = this.getSeriesItems(series, itemIndex); 4118 categoryItem = items.category; 4119 valueItem = items.value; 4120 if(categoryItem && valueItem && categoryItem.value && valueItem.value) 4121 { 4122 msg += categoryItem.displayName + 4123 ": " + 4124 categoryItem.axis.formatLabel.apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) + 4125 ", "; 4126 msg += valueItem.displayName + 4127 ": " + 4128 valueItem.axis.formatLabel.apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]) + 4129 ", "; 4130 } 4131 else 4132 { 4133 msg += "No data available."; 4134 } 4135 msg += (itemIndex + 1) + " of " + dataLength + ". "; 4136 } 4137 return msg; 4138 } 4139 }, { 4140 ATTRS: { 4141 /** 4142 * Indicates whether axis labels are allowed to overflow beyond the bounds of the chart's content box. 4143 * 4144 * @attribute allowContentOverflow 4145 * @type Boolean 4146 */ 4147 allowContentOverflow: { 4148 value: false 4149 }, 4150 4151 /** 4152 * Style object for the axes. 4153 * 4154 * @attribute axesStyles 4155 * @type Object 4156 * @private 4157 */ 4158 axesStyles: { 4159 lazyAdd: false, 4160 4161 getter: function() 4162 { 4163 var axes = this.get("axes"), 4164 i, 4165 styles = this._axesStyles; 4166 if(axes) 4167 { 4168 for(i in axes) 4169 { 4170 if(axes.hasOwnProperty(i) && axes[i] instanceof Y.Axis) 4171 { 4172 if(!styles) 4173 { 4174 styles = {}; 4175 } 4176 styles[i] = axes[i].get("styles"); 4177 } 4178 } 4179 } 4180 return styles; 4181 }, 4182 4183 setter: function(val) 4184 { 4185 var axes = this.get("axes"), 4186 i; 4187 for(i in val) 4188 { 4189 if(val.hasOwnProperty(i) && axes.hasOwnProperty(i)) 4190 { 4191 this._setBaseAttribute(axes[i], "styles", val[i]); 4192 } 4193 } 4194 return val; 4195 } 4196 }, 4197 4198 /** 4199 * Style object for the series 4200 * 4201 * @attribute seriesStyles 4202 * @type Object 4203 * @private 4204 */ 4205 seriesStyles: { 4206 lazyAdd: false, 4207 4208 getter: function() 4209 { 4210 var styles = this._seriesStyles, 4211 graph = this.get("graph"), 4212 dict, 4213 i; 4214 if(graph) 4215 { 4216 dict = graph.get("seriesDictionary"); 4217 if(dict) 4218 { 4219 styles = {}; 4220 for(i in dict) 4221 { 4222 if(dict.hasOwnProperty(i)) 4223 { 4224 styles[i] = dict[i].get("styles"); 4225 } 4226 } 4227 } 4228 } 4229 return styles; 4230 }, 4231 4232 setter: function(val) 4233 { 4234 var i, 4235 l, 4236 s; 4237 4238 if(Y_Lang.isArray(val)) 4239 { 4240 s = this.get("seriesCollection"); 4241 i = 0; 4242 l = val.length; 4243 4244 for(; i < l; ++i) 4245 { 4246 this._setBaseAttribute(s[i], "styles", val[i]); 4247 } 4248 } 4249 else 4250 { 4251 for(i in val) 4252 { 4253 if(val.hasOwnProperty(i)) 4254 { 4255 s = this.getSeries(i); 4256 this._setBaseAttribute(s, "styles", val[i]); 4257 } 4258 } 4259 } 4260 return val; 4261 } 4262 }, 4263 4264 /** 4265 * Styles for the graph. 4266 * 4267 * @attribute graphStyles 4268 * @type Object 4269 * @private 4270 */ 4271 graphStyles: { 4272 lazyAdd: false, 4273 4274 getter: function() 4275 { 4276 var graph = this.get("graph"); 4277 if(graph) 4278 { 4279 return(graph.get("styles")); 4280 } 4281 return this._graphStyles; 4282 }, 4283 4284 setter: function(val) 4285 { 4286 var graph = this.get("graph"); 4287 this._setBaseAttribute(graph, "styles", val); 4288 return val; 4289 } 4290 4291 }, 4292 4293 /** 4294 * Style properties for the chart. Contains a key indexed hash of the following: 4295 * <dl> 4296 * <dt>series</dt><dd>A key indexed hash containing references to the `styles` attribute for each series in the chart. 4297 * Specific style attributes vary depending on the series: 4298 * <ul> 4299 * <li><a href="AreaSeries.html#attr_styles">AreaSeries</a></li> 4300 * <li><a href="BarSeries.html#attr_styles">BarSeries</a></li> 4301 * <li><a href="ColumnSeries.html#attr_styles">ColumnSeries</a></li> 4302 * <li><a href="ComboSeries.html#attr_styles">ComboSeries</a></li> 4303 * <li><a href="LineSeries.html#attr_styles">LineSeries</a></li> 4304 * <li><a href="MarkerSeries.html#attr_styles">MarkerSeries</a></li> 4305 * <li><a href="SplineSeries.html#attr_styles">SplineSeries</a></li> 4306 * </ul> 4307 * </dd> 4308 * <dt>axes</dt><dd>A key indexed hash containing references to the `styles` attribute for each axes in the chart. Specific 4309 * style attributes can be found in the <a href="Axis.html#attr_styles">Axis</a> class.</dd> 4310 * <dt>graph</dt><dd>A reference to the `styles` attribute in the chart. Specific style attributes can be found in the 4311 * <a href="Graph.html#attr_styles">Graph</a> class.</dd> 4312 * </dl> 4313 * 4314 * @attribute styles 4315 * @type Object 4316 */ 4317 styles: { 4318 lazyAdd: false, 4319 4320 getter: function() 4321 { 4322 var styles = { 4323 axes: this.get("axesStyles"), 4324 series: this.get("seriesStyles"), 4325 graph: this.get("graphStyles") 4326 }; 4327 return styles; 4328 }, 4329 setter: function(val) 4330 { 4331 if(val.hasOwnProperty("axes")) 4332 { 4333 if(this.get("axesStyles")) 4334 { 4335 this.set("axesStyles", val.axes); 4336 } 4337 else 4338 { 4339 this._axesStyles = val.axes; 4340 } 4341 } 4342 if(val.hasOwnProperty("series")) 4343 { 4344 if(this.get("seriesStyles")) 4345 { 4346 this.set("seriesStyles", val.series); 4347 } 4348 else 4349 { 4350 this._seriesStyles = val.series; 4351 } 4352 } 4353 if(val.hasOwnProperty("graph")) 4354 { 4355 this.set("graphStyles", val.graph); 4356 } 4357 } 4358 }, 4359 4360 /** 4361 * Axes to appear in the chart. This can be a key indexed hash of axis instances or object literals 4362 * used to construct the appropriate axes. 4363 * 4364 * @attribute axes 4365 * @type Object 4366 */ 4367 axes: { 4368 lazyAdd: false, 4369 4370 valueFn: "_getDefaultAxes", 4371 4372 setter: function(val) 4373 { 4374 if(this.get("dataProvider")) 4375 { 4376 val = this._setAxes(val); 4377 } 4378 return val; 4379 } 4380 }, 4381 4382 /** 4383 * Collection of series to appear on the chart. This can be an array of Series instances or object literals 4384 * used to construct the appropriate series. 4385 * 4386 * @attribute seriesCollection 4387 * @type Array 4388 */ 4389 seriesCollection: { 4390 lazyAdd: false, 4391 4392 valueFn: "_getDefaultSeriesCollection", 4393 4394 setter: function(val) 4395 { 4396 if(this.get("dataProvider")) 4397 { 4398 return this._parseSeriesCollection(val); 4399 } 4400 return val; 4401 } 4402 }, 4403 4404 /** 4405 * Reference to the left-aligned axes for the chart. 4406 * 4407 * @attribute leftAxesCollection 4408 * @type Array 4409 * @private 4410 */ 4411 leftAxesCollection: {}, 4412 4413 /** 4414 * Reference to the bottom-aligned axes for the chart. 4415 * 4416 * @attribute bottomAxesCollection 4417 * @type Array 4418 * @private 4419 */ 4420 bottomAxesCollection: {}, 4421 4422 /** 4423 * Reference to the right-aligned axes for the chart. 4424 * 4425 * @attribute rightAxesCollection 4426 * @type Array 4427 * @private 4428 */ 4429 rightAxesCollection: {}, 4430 4431 /** 4432 * Reference to the top-aligned axes for the chart. 4433 * 4434 * @attribute topAxesCollection 4435 * @type Array 4436 * @private 4437 */ 4438 topAxesCollection: {}, 4439 4440 /** 4441 * Indicates whether or not the chart is stacked. 4442 * 4443 * @attribute stacked 4444 * @type Boolean 4445 */ 4446 stacked: { 4447 value: false 4448 }, 4449 4450 /** 4451 * Direction of chart's category axis when there is no series collection specified. Charts can 4452 * be horizontal or vertical. When the chart type is column, the chart is horizontal. 4453 * When the chart type is bar, the chart is vertical. 4454 * 4455 * @attribute direction 4456 * @type String 4457 */ 4458 direction: { 4459 getter: function() 4460 { 4461 var type = this.get("type"); 4462 if(type === "bar") 4463 { 4464 return "vertical"; 4465 } 4466 else if(type === "column") 4467 { 4468 return "horizontal"; 4469 } 4470 return this._direction; 4471 }, 4472 4473 setter: function(val) 4474 { 4475 this._direction = val; 4476 return this._direction; 4477 } 4478 }, 4479 4480 /** 4481 * Indicates whether or not an area is filled in a combo chart. 4482 * 4483 * @attribute showAreaFill 4484 * @type Boolean 4485 */ 4486 showAreaFill: {}, 4487 4488 /** 4489 * Indicates whether to display markers in a combo chart. 4490 * 4491 * @attribute showMarkers 4492 * @type Boolean 4493 */ 4494 showMarkers:{}, 4495 4496 /** 4497 * Indicates whether to display lines in a combo chart. 4498 * 4499 * @attribute showLines 4500 * @type Boolean 4501 */ 4502 showLines:{}, 4503 4504 /** 4505 * Indicates the key value used to identify a category axis in the `axes` hash. If 4506 * not specified, the categoryKey attribute value will be used. 4507 * 4508 * @attribute categoryAxisName 4509 * @type String 4510 */ 4511 categoryAxisName: { 4512 }, 4513 4514 /** 4515 * Indicates the key value used to identify a the series axis when an axis not generated. 4516 * 4517 * @attribute valueAxisName 4518 * @type String 4519 */ 4520 valueAxisName: { 4521 value: "values" 4522 }, 4523 4524 /** 4525 * Reference to the horizontalGridlines for the chart. 4526 * 4527 * @attribute horizontalGridlines 4528 * @type Gridlines 4529 */ 4530 horizontalGridlines: { 4531 getter: function() 4532 { 4533 var graph = this.get("graph"); 4534 if(graph) 4535 { 4536 return graph.get("horizontalGridlines"); 4537 } 4538 return this._horizontalGridlines; 4539 }, 4540 setter: function(val) 4541 { 4542 var graph = this.get("graph"); 4543 if(val && !Y_Lang.isObject(val)) 4544 { 4545 val = {}; 4546 } 4547 if(graph) 4548 { 4549 graph.set("horizontalGridlines", val); 4550 } 4551 else 4552 { 4553 this._horizontalGridlines = val; 4554 } 4555 } 4556 }, 4557 4558 /** 4559 * Reference to the verticalGridlines for the chart. 4560 * 4561 * @attribute verticalGridlines 4562 * @type Gridlines 4563 */ 4564 verticalGridlines: { 4565 getter: function() 4566 { 4567 var graph = this.get("graph"); 4568 if(graph) 4569 { 4570 return graph.get("verticalGridlines"); 4571 } 4572 return this._verticalGridlines; 4573 }, 4574 setter: function(val) 4575 { 4576 var graph = this.get("graph"); 4577 if(val && !Y_Lang.isObject(val)) 4578 { 4579 val = {}; 4580 } 4581 if(graph) 4582 { 4583 graph.set("verticalGridlines", val); 4584 } 4585 else 4586 { 4587 this._verticalGridlines = val; 4588 } 4589 } 4590 }, 4591 4592 /** 4593 * Type of chart when there is no series collection specified. 4594 * 4595 * @attribute type 4596 * @type String 4597 */ 4598 type: { 4599 getter: function() 4600 { 4601 if(this.get("stacked")) 4602 { 4603 return "stacked" + this._type; 4604 } 4605 return this._type; 4606 }, 4607 4608 setter: function(val) 4609 { 4610 if(this._type === "bar") 4611 { 4612 if(val !== "bar") 4613 { 4614 this.set("direction", "horizontal"); 4615 } 4616 } 4617 else 4618 { 4619 if(val === "bar") 4620 { 4621 this.set("direction", "vertical"); 4622 } 4623 } 4624 this._type = val; 4625 return this._type; 4626 } 4627 }, 4628 4629 /** 4630 * Reference to the category axis used by the chart. 4631 * 4632 * @attribute categoryAxis 4633 * @type Axis 4634 */ 4635 categoryAxis:{} 4636 } 4637 }); 4638 /** 4639 * The PieChart class creates a pie chart 4640 * 4641 * @class PieChart 4642 * @extends ChartBase 4643 * @constructor 4644 * @submodule charts-base 4645 */ 4646 Y.PieChart = Y.Base.create("pieChart", Y.Widget, [Y.ChartBase], { 4647 /** 4648 * Calculates and returns a `seriesCollection`. 4649 * 4650 * @method _getSeriesCollection 4651 * @return Array 4652 * @private 4653 */ 4654 _getSeriesCollection: function() 4655 { 4656 if(this._seriesCollection) 4657 { 4658 return this._seriesCollection; 4659 } 4660 var axes = this.get("axes"), 4661 sc = [], 4662 seriesKeys, 4663 i = 0, 4664 l, 4665 type = this.get("type"), 4666 key, 4667 catAxis = "categoryAxis", 4668 catKey = "categoryKey", 4669 valAxis = "valueAxis", 4670 seriesKey = "valueKey"; 4671 if(axes) 4672 { 4673 seriesKeys = axes.values.get("keyCollection"); 4674 key = axes.category.get("keyCollection")[0]; 4675 l = seriesKeys.length; 4676 for(; i < l; ++i) 4677 { 4678 sc[i] = {type:type}; 4679 sc[i][catAxis] = "category"; 4680 sc[i][valAxis] = "values"; 4681 sc[i][catKey] = key; 4682 sc[i][seriesKey] = seriesKeys[i]; 4683 } 4684 } 4685 this._seriesCollection = sc; 4686 return sc; 4687 }, 4688 4689 /** 4690 * Creates `Axis` instances. 4691 * 4692 * @method _parseAxes 4693 * @param {Object} val Object containing `Axis` instances or objects in which to construct `Axis` instances. 4694 * @return Object 4695 * @private 4696 */ 4697 _parseAxes: function(hash) 4698 { 4699 if(!this._axes) 4700 { 4701 this._axes = {}; 4702 } 4703 var i, pos, axis, dh, config, AxisClass, 4704 type = this.get("type"), 4705 w = this.get("width"), 4706 h = this.get("height"), 4707 node = Y.Node.one(this._parentNode); 4708 if(!w) 4709 { 4710 this.set("width", node.get("offsetWidth")); 4711 w = this.get("width"); 4712 } 4713 if(!h) 4714 { 4715 this.set("height", node.get("offsetHeight")); 4716 h = this.get("height"); 4717 } 4718 for(i in hash) 4719 { 4720 if(hash.hasOwnProperty(i)) 4721 { 4722 dh = hash[i]; 4723 pos = type === "pie" ? "none" : dh.position; 4724 AxisClass = this._getAxisClass(dh.type); 4725 config = {dataProvider:this.get("dataProvider")}; 4726 if(dh.hasOwnProperty("roundingUnit")) 4727 { 4728 config.roundingUnit = dh.roundingUnit; 4729 } 4730 config.keys = dh.keys; 4731 config.width = w; 4732 config.height = h; 4733 config.position = pos; 4734 config.styles = dh.styles; 4735 axis = new AxisClass(config); 4736 axis.on("axisRendered", Y.bind(this._itemRendered, this)); 4737 this._axes[i] = axis; 4738 } 4739 } 4740 }, 4741 4742 /** 4743 * Adds axes to the chart. 4744 * 4745 * @method _addAxes 4746 * @private 4747 */ 4748 _addAxes: function() 4749 { 4750 var axes = this.get("axes"), 4751 i, 4752 axis, 4753 p; 4754 if(!axes) 4755 { 4756 this.set("axes", this._getDefaultAxes()); 4757 axes = this.get("axes"); 4758 } 4759 if(!this._axesCollection) 4760 { 4761 this._axesCollection = []; 4762 } 4763 for(i in axes) 4764 { 4765 if(axes.hasOwnProperty(i)) 4766 { 4767 axis = axes[i]; 4768 p = axis.get("position"); 4769 if(!this.get(p + "AxesCollection")) 4770 { 4771 this.set(p + "AxesCollection", [axis]); 4772 } 4773 else 4774 { 4775 this.get(p + "AxesCollection").push(axis); 4776 } 4777 this._axesCollection.push(axis); 4778 } 4779 } 4780 }, 4781 4782 /** 4783 * Renders the Graph. 4784 * 4785 * @method _addSeries 4786 * @private 4787 */ 4788 _addSeries: function() 4789 { 4790 var graph = this.get("graph"), 4791 seriesCollection = this.get("seriesCollection"); 4792 this._parseSeriesAxes(seriesCollection); 4793 graph.set("showBackground", false); 4794 graph.set("width", this.get("width")); 4795 graph.set("height", this.get("height")); 4796 graph.set("seriesCollection", seriesCollection); 4797 this._seriesCollection = graph.get("seriesCollection"); 4798 graph.render(this.get("contentBox")); 4799 }, 4800 4801 /** 4802 * Parse and sets the axes for the chart. 4803 * 4804 * @method _parseSeriesAxes 4805 * @param {Array} c A collection `PieSeries` instance. 4806 * @private 4807 */ 4808 _parseSeriesAxes: function(c) 4809 { 4810 var i = 0, 4811 len = c.length, 4812 s, 4813 axes = this.get("axes"), 4814 axis; 4815 for(; i < len; ++i) 4816 { 4817 s = c[i]; 4818 if(s) 4819 { 4820 //If series is an actual series instance, 4821 //replace axes attribute string ids with axes 4822 if(s instanceof Y.PieSeries) 4823 { 4824 axis = s.get("categoryAxis"); 4825 if(axis && !(axis instanceof Y.Axis)) 4826 { 4827 s.set("categoryAxis", axes[axis]); 4828 } 4829 axis = s.get("valueAxis"); 4830 if(axis && !(axis instanceof Y.Axis)) 4831 { 4832 s.set("valueAxis", axes[axis]); 4833 } 4834 continue; 4835 } 4836 s.categoryAxis = axes.category; 4837 s.valueAxis = axes.values; 4838 if(!s.type) 4839 { 4840 s.type = this.get("type"); 4841 } 4842 } 4843 } 4844 }, 4845 4846 /** 4847 * Generates and returns a key-indexed object containing `Axis` instances or objects used to create `Axis` instances. 4848 * 4849 * @method _getDefaultAxes 4850 * @return Object 4851 * @private 4852 */ 4853 _getDefaultAxes: function() 4854 { 4855 var catKey = this.get("categoryKey"), 4856 seriesKeys = this.get("seriesKeys").concat(), 4857 seriesAxis = "numeric"; 4858 return { 4859 values:{ 4860 keys:seriesKeys, 4861 type:seriesAxis 4862 }, 4863 category:{ 4864 keys:[catKey], 4865 type:this.get("categoryType") 4866 } 4867 }; 4868 }, 4869 4870 /** 4871 * Returns an object literal containing a categoryItem and a valueItem for a given series index. 4872 * 4873 * @method getSeriesItem 4874 * @param series Reference to a series. 4875 * @param index Index of the specified item within a series. 4876 * @return Object 4877 */ 4878 getSeriesItems: function(series, index) 4879 { 4880 var categoryItem = { 4881 axis: series.get("categoryAxis"), 4882 key: series.get("categoryKey"), 4883 displayName: series.get("categoryDisplayName") 4884 }, 4885 valueItem = { 4886 axis: series.get("valueAxis"), 4887 key: series.get("valueKey"), 4888 displayName: series.get("valueDisplayName") 4889 }; 4890 categoryItem.value = categoryItem.axis.getKeyValueAt(categoryItem.key, index); 4891 valueItem.value = valueItem.axis.getKeyValueAt(valueItem.key, index); 4892 return {category:categoryItem, value:valueItem}; 4893 }, 4894 4895 /** 4896 * Handler for sizeChanged event. 4897 * 4898 * @method _sizeChanged 4899 * @param {Object} e Event object. 4900 * @private 4901 */ 4902 _sizeChanged: function() 4903 { 4904 this._redraw(); 4905 }, 4906 4907 /** 4908 * Redraws the chart instance. 4909 * 4910 * @method _redraw 4911 * @private 4912 */ 4913 _redraw: function() 4914 { 4915 var graph = this.get("graph"), 4916 w = this.get("width"), 4917 h = this.get("height"), 4918 dimension; 4919 if(graph) 4920 { 4921 dimension = Math.min(w, h); 4922 graph.set("width", dimension); 4923 graph.set("height", dimension); 4924 } 4925 }, 4926 4927 /** 4928 * Formats tooltip text for a pie chart. 4929 * 4930 * @method _tooltipLabelFunction 4931 * @param {Object} categoryItem An object containing the following: 4932 * <dl> 4933 * <dt>axis</dt><dd>The axis to which the category is bound.</dd> 4934 * <dt>displayName</dt><dd>The display name set to the category (defaults to key if not provided)</dd> 4935 * <dt>key</dt><dd>The key of the category.</dd> 4936 * <dt>value</dt><dd>The value of the category</dd> 4937 * </dl> 4938 * @param {Object} valueItem An object containing the following: 4939 * <dl> 4940 * <dt>axis</dt><dd>The axis to which the item's series is bound.</dd> 4941 * <dt>displayName</dt><dd>The display name of the series. (defaults to key if not provided)</dd> 4942 * <dt>key</dt><dd>The key for the series.</dd> 4943 * <dt>value</dt><dd>The value for the series item.</dd> 4944 * </dl> 4945 * @param {Number} itemIndex The index of the item within the series. 4946 * @param {CartesianSeries} series The `PieSeries` instance of the item. 4947 * @return {HTMLElement} 4948 * @private 4949 */ 4950 _tooltipLabelFunction: function(categoryItem, valueItem, itemIndex, series) 4951 { 4952 var msg = DOCUMENT.createElement("div"), 4953 total = series.getTotalValues(), 4954 pct = Math.round((valueItem.value / total) * 10000)/100; 4955 msg.appendChild(DOCUMENT.createTextNode(categoryItem.displayName + 4956 ": " + categoryItem.axis.get("labelFunction").apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]))); 4957 msg.appendChild(DOCUMENT.createElement("br")); 4958 msg.appendChild(DOCUMENT.createTextNode(valueItem.displayName + 4959 ": " + valueItem.axis.get("labelFunction").apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]))); 4960 msg.appendChild(DOCUMENT.createElement("br")); 4961 msg.appendChild(DOCUMENT.createTextNode(pct + "%")); 4962 return msg; 4963 }, 4964 4965 /** 4966 * Returns the appropriate message based on the key press. 4967 * 4968 * @method _getAriaMessage 4969 * @param {Number} key The keycode that was pressed. 4970 * @return String 4971 */ 4972 _getAriaMessage: function(key) 4973 { 4974 var msg = "", 4975 categoryItem, 4976 items, 4977 series, 4978 valueItem, 4979 seriesIndex = 0, 4980 itemIndex = this._itemIndex, 4981 len, 4982 total, 4983 pct, 4984 markers; 4985 series = this.getSeries(parseInt(seriesIndex, 10)); 4986 markers = series.get("markers"); 4987 len = markers && markers.length ? markers.length : 0; 4988 if(key === 37) 4989 { 4990 itemIndex = itemIndex > 0 ? itemIndex - 1 : len - 1; 4991 } 4992 else if(key === 39) 4993 { 4994 itemIndex = itemIndex >= len - 1 ? 0 : itemIndex + 1; 4995 } 4996 this._itemIndex = itemIndex; 4997 items = this.getSeriesItems(series, itemIndex); 4998 categoryItem = items.category; 4999 valueItem = items.value; 5000 total = series.getTotalValues(); 5001 pct = Math.round((valueItem.value / total) * 10000)/100; 5002 if(categoryItem && valueItem) 5003 { 5004 msg += categoryItem.displayName + 5005 ": " + 5006 categoryItem.axis.formatLabel.apply(this, [categoryItem.value, categoryItem.axis.get("labelFormat")]) + 5007 ", "; 5008 msg += valueItem.displayName + 5009 ": " + valueItem.axis.formatLabel.apply(this, [valueItem.value, valueItem.axis.get("labelFormat")]) + 5010 ", "; 5011 msg += "Percent of total " + valueItem.displayName + ": " + pct + "%,"; 5012 } 5013 else 5014 { 5015 msg += "No data available,"; 5016 } 5017 msg += (itemIndex + 1) + " of " + len + ". "; 5018 return msg; 5019 }, 5020 5021 /** 5022 * Destructor implementation for the PieChart class. 5023 * 5024 * @method destructor 5025 * @protected 5026 */ 5027 destructor: function() 5028 { 5029 var series, 5030 axis, 5031 tooltip = this.get("tooltip"), 5032 tooltipNode = tooltip.node, 5033 graph = this.get("graph"), 5034 axesCollection = this._axesCollection, 5035 seriesCollection = this.get("seriesCollection"); 5036 while(seriesCollection.length > 0) 5037 { 5038 series = seriesCollection.shift(); 5039 series.destroy(true); 5040 } 5041 while(axesCollection.length > 0) 5042 { 5043 axis = axesCollection.shift(); 5044 if(axis instanceof Y.Axis) 5045 { 5046 axis.destroy(true); 5047 } 5048 } 5049 if(this._description) 5050 { 5051 this._description.empty(); 5052 this._description.remove(true); 5053 } 5054 if(this._liveRegion) 5055 { 5056 this._liveRegion.empty(); 5057 this._liveRegion.remove(true); 5058 } 5059 if(graph) 5060 { 5061 graph.destroy(true); 5062 } 5063 if(tooltipNode) 5064 { 5065 tooltipNode.empty(); 5066 tooltipNode.remove(true); 5067 } 5068 } 5069 }, { 5070 ATTRS: { 5071 /** 5072 * Sets the aria description for the chart. 5073 * 5074 * @attribute ariaDescription 5075 * @type String 5076 */ 5077 ariaDescription: { 5078 value: "Use the left and right keys to navigate through items.", 5079 5080 setter: function(val) 5081 { 5082 if(this._description) 5083 { 5084 this._description.set("text", val); 5085 } 5086 return val; 5087 } 5088 }, 5089 5090 /** 5091 * Axes to appear in the chart. 5092 * 5093 * @attribute axes 5094 * @type Object 5095 */ 5096 axes: { 5097 getter: function() 5098 { 5099 return this._axes; 5100 }, 5101 5102 setter: function(val) 5103 { 5104 this._parseAxes(val); 5105 } 5106 }, 5107 5108 /** 5109 * Collection of series to appear on the chart. This can be an array of Series instances or object literals 5110 * used to describe a Series instance. 5111 * 5112 * @attribute seriesCollection 5113 * @type Array 5114 */ 5115 seriesCollection: { 5116 lazyAdd: false, 5117 5118 getter: function() 5119 { 5120 return this._getSeriesCollection(); 5121 }, 5122 5123 setter: function(val) 5124 { 5125 return this._setSeriesCollection(val); 5126 } 5127 }, 5128 5129 /** 5130 * Type of chart when there is no series collection specified. 5131 * 5132 * @attribute type 5133 * @type String 5134 */ 5135 type: { 5136 value: "pie" 5137 } 5138 } 5139 }); 5140 /** 5141 * The Chart class is the basic application used to create a chart. 5142 * 5143 * @class Chart 5144 * @constructor 5145 * @submodule charts-base 5146 */ 5147 function Chart(cfg) 5148 { 5149 if(cfg.type !== "pie") 5150 { 5151 return new Y.CartesianChart(cfg); 5152 } 5153 else 5154 { 5155 return new Y.PieChart(cfg); 5156 } 5157 } 5158 Y.Chart = Chart; 5159 5160 5161 }, '3.17.2', { 5162 "requires": [ 5163 "dom", 5164 "event-mouseenter", 5165 "event-touch", 5166 "graphics-group", 5167 "axes", 5168 "series-pie", 5169 "series-line", 5170 "series-marker", 5171 "series-area", 5172 "series-spline", 5173 "series-column", 5174 "series-bar", 5175 "series-areaspline", 5176 "series-combo", 5177 "series-combospline", 5178 "series-line-stacked", 5179 "series-marker-stacked", 5180 "series-area-stacked", 5181 "series-spline-stacked", 5182 "series-column-stacked", 5183 "series-bar-stacked", 5184 "series-areaspline-stacked", 5185 "series-combo-stacked", 5186 "series-combospline-stacked" 5187 ] 5188 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |