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