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