Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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