Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('series-pie', function (Y, NAME) {
2
 
3
/**
4
 * Provides functionality for creating a pie series.
5
 *
6
 * @module charts
7
 * @submodule series-pie
8
 */
9
/**
10
 * PieSeries visualizes data as a circular chart divided into wedges which represent data as a
11
 * percentage of a whole.
12
 *
13
 * @class PieSeries
14
 * @constructor
15
 * @extends SeriesBase
16
 * @uses Plots
17
 * @param {Object} config (optional) Configuration parameters.
18
 * @submodule series-pie
19
 */
20
var CONFIG = Y.config,
21
    DOCUMENT = CONFIG.doc,
22
    _getClassName = Y.ClassNameManager.getClassName,
23
    SERIES_MARKER = _getClassName("seriesmarker");
24
Y.PieSeries = Y.Base.create("pieSeries", Y.SeriesBase, [Y.Plots], {
25
    /**
26
     * Image map used for interactivity when rendered with canvas.
27
     *
28
     * @property _map
29
     * @type HTMLElement
30
     * @private
31
     */
32
    _map: null,
33
 
34
    /**
35
     * Image used for image map when rendered with canvas.
36
     *
37
     * @property _image
38
     * @type HTMLElement
39
     * @private
40
     */
41
    _image: null,
42
 
43
    /**
44
     * Creates or updates the image map when rendered with canvas.
45
     *
46
     * @method _setMap
47
     * @private
48
     */
49
    _setMap: function()
50
    {
51
        var id = "pieHotSpotMapi_" + Math.round(100000 * Math.random()),
52
            graph = this.get("graph"),
53
            graphic,
54
            cb,
55
            areaNode;
56
        if(graph)
57
        {
58
            cb = graph.get("contentBox");
59
        }
60
        else
61
        {
62
            graphic = this.get("graphic");
63
            cb = graphic.get("node");
64
        }
65
        if(this._image)
66
        {
67
            cb.removeChild(this._image);
68
            while(this._areaNodes && this._areaNodes.length > 0)
69
            {
70
                areaNode = this._areaNodes.shift();
71
                this._map.removeChild(areaNode);
72
            }
73
            cb.removeChild(this._map);
74
        }
75
        this._image = DOCUMENT.createElement("img");
76
        this._image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSB" +
77
                        "JbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==";
78
        cb.appendChild(this._image);
79
        this._image.style.position = "absolute";
80
        this._image.style.left = "0px";
81
        this._image.style.top = "0px";
82
        this._image.setAttribute("usemap", "#" + id);
83
        this._image.style.zIndex = 3;
84
        this._image.style.opacity = 0;
85
        this._image.setAttribute("alt", "imagemap");
86
        this._map = DOCUMENT.createElement("map");
87
        cb.appendChild(this._map);
88
        this._map.setAttribute("name", id);
89
        this._map.setAttribute("id", id);
90
        this._areaNodes = [];
91
    },
92
 
93
    /**
94
     * Storage for `categoryDisplayName` attribute.
95
     *
96
     * @property _categoryDisplayName
97
     * @private
98
     */
99
    _categoryDisplayName: null,
100
 
101
    /**
102
     * Storage for `valueDisplayName` attribute.
103
     *
104
     * @property _valueDisplayName
105
     * @private
106
     */
107
    _valueDisplayName: null,
108
 
109
    /**
110
     * Adds event listeners.
111
     *
112
     * @method addListeners
113
     * @private
114
     */
115
    addListeners: function()
116
    {
117
        var categoryAxis = this.get("categoryAxis"),
118
            valueAxis = this.get("valueAxis");
119
        if(categoryAxis)
120
        {
121
            categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
122
            categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
123
        }
124
        if(valueAxis)
125
        {
126
            valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
127
            valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
128
        }
129
        this.after("categoryAxisChange", this.categoryAxisChangeHandler);
130
        this.after("valueAxisChange", this.valueAxisChangeHandler);
131
        this._stylesChangeHandle = this.after("stylesChange", this._updateHandler);
132
        this._visibleChangeHandle = this.after("visibleChange", this._handleVisibleChange);
133
    },
134
 
135
    /**
136
     * Draws the series.
137
     *
138
     * @method validate
139
     * @private
140
     */
141
    validate: function()
142
    {
143
        this.draw();
144
        this._renderered = true;
145
    },
146
 
147
    /**
148
     * Event handler for the categoryAxisChange event.
149
     *
150
     * @method _categoryAxisChangeHandler
151
     * @param {Object} e Event object.
152
     * @private
153
     */
154
    _categoryAxisChangeHandler: function()
155
    {
156
        var categoryAxis = this.get("categoryAxis");
157
        categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
158
        categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
159
    },
160
 
161
    /**
162
     * Event handler for the valueAxisChange event.
163
     *
164
     * @method _valueAxisChangeHandler
165
     * @param {Object} e Event object.
166
     * @private
167
     */
168
    _valueAxisChangeHandler: function()
169
    {
170
        var valueAxis = this.get("valueAxis");
171
        valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
172
        valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
173
    },
174
 
175
    /**
176
     * Constant used to generate unique id.
177
     *
178
     * @property GUID
179
     * @type String
180
     * @private
181
     */
182
    GUID: "pieseries",
183
 
184
    /**
185
     * Event handler for categoryDataChange event.
186
     *
187
     * @method _categoryDataChangeHandler
188
     * @param {Object} event Event object.
189
     * @private
190
     */
191
    _categoryDataChangeHandler: function()
192
    {
193
       if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
194
        {
195
            this.draw();
196
        }
197
    },
198
 
199
    /**
200
     * Event handler for valueDataChange event.
201
     *
202
     * @method _valueDataChangeHandler
203
     * @param {Object} event Event object.
204
     * @private
205
     */
206
    _valueDataChangeHandler: function()
207
    {
208
        if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
209
        {
210
            this.draw();
211
        }
212
    },
213
 
214
    /**
215
     * Returns the sum of all values for the series.
216
     *
217
     * @method getTotalValues
218
     * @return Number
219
     */
220
    getTotalValues: function()
221
    {
222
        var total = this.get("valueAxis").getTotalByKey(this.get("valueKey"));
223
        return total;
224
    },
225
 
226
    /**
227
     * Draws the series. Overrides the base implementation.
228
     *
229
     * @method draw
230
     * @protected
231
     */
232
    draw: function()
233
    {
234
        var w = this.get("width"),
235
            h = this.get("height");
236
        if(isFinite(w) && isFinite(h) && w > 0 && h > 0)
237
        {
238
            this._rendered = true;
239
            if(this._drawing)
240
            {
241
                this._callLater = true;
242
                return;
243
            }
244
            this._drawing = true;
245
            this._callLater = false;
246
            this.drawSeries();
247
            this._drawing = false;
248
            if(this._callLater)
249
            {
250
                this.draw();
251
            }
252
            else
253
            {
254
                this.fire("drawingComplete");
255
            }
256
        }
257
    },
258
 
259
    /**
260
     * Draws the markers
261
     *
262
     * @method drawPlots
263
     * @protected
264
     */
265
    drawPlots: function()
266
    {
267
        var values = this.get("valueAxis").getDataByKey(this.get("valueKey")).concat(),
268
            totalValue = 0,
269
            itemCount = values.length,
270
            styles = this.get("styles").marker,
271
            fillColors = styles.fill.colors,
272
            fillAlphas = styles.fill.alphas || ["1"],
273
            borderColors = styles.border.colors,
274
            borderWeights = [styles.border.weight],
275
            borderAlphas = [styles.border.alpha],
276
            tbw = borderWeights.concat(),
277
            tbc = borderColors.concat(),
278
            tba = borderAlphas.concat(),
279
            tfc,
280
            tfa,
281
            padding = styles.padding,
282
            graphic = this.get("graphic"),
283
            minDimension = Math.min(graphic.get("width"), graphic.get("height")),
284
            w = minDimension - (padding.left + padding.right),
285
            h = minDimension - (padding.top + padding.bottom),
286
            startAngle = -90,
287
            halfWidth = w / 2,
288
            halfHeight = h / 2,
289
            radius = Math.min(halfWidth, halfHeight),
290
            i = 0,
291
            value,
292
            angle = 0,
293
            lc,
294
            la,
295
            lw,
296
            wedgeStyle,
297
            marker,
298
            graphOrder = this.get("graphOrder") || 0,
299
            isCanvas = Y.Graphic.NAME === "canvasGraphic";
300
        for(; i < itemCount; ++i)
301
        {
302
            value = parseFloat(values[i]);
303
 
304
            values.push(value);
305
            if(!isNaN(value))
306
            {
307
                totalValue += value;
308
            }
309
        }
310
 
311
        tfc = fillColors ? fillColors.concat() : null;
312
        tfa = fillAlphas ? fillAlphas.concat() : null;
313
        this._createMarkerCache();
314
        if(isCanvas)
315
        {
316
            this._setMap();
317
            this._image.width = w;
318
            this._image.height = h;
319
        }
320
        for(i = 0; i < itemCount; i++)
321
        {
322
            value = values[i];
323
            if(totalValue === 0)
324
            {
325
                angle = 360 / values.length;
326
            }
327
            else
328
            {
329
                angle = 360 * (value / totalValue);
330
            }
331
            if(tfc && tfc.length < 1)
332
            {
333
                tfc = fillColors.concat();
334
            }
335
            if(tfa && tfa.length < 1)
336
            {
337
                tfa = fillAlphas.concat();
338
            }
339
            if(tbw && tbw.length < 1)
340
            {
341
                tbw = borderWeights.concat();
342
            }
343
            if(tbw && tbc.length < 1)
344
            {
345
                tbc = borderColors.concat();
346
            }
347
            if(tba && tba.length < 1)
348
            {
349
                tba = borderAlphas.concat();
350
            }
351
            lw = tbw ? tbw.shift() : null;
352
            lc = tbc ? tbc.shift() : null;
353
            la = tba ? tba.shift() : null;
354
            startAngle += angle;
355
            wedgeStyle = {
356
                border: {
357
                    color:lc,
358
                    weight:lw,
359
                    alpha:la
360
                },
361
                fill: {
362
                    color:tfc ? tfc.shift() : this._getDefaultColor(i, "slice"),
363
                    alpha:tfa ? tfa.shift() : null
364
                },
365
                type: "pieslice",
366
                arc: angle,
367
                radius: radius,
368
                startAngle: startAngle,
369
                cx: halfWidth,
370
                cy: halfHeight,
371
                width: w,
372
                height: h
373
            };
374
            marker = this.getMarker(wedgeStyle, graphOrder, i);
375
            if(isCanvas)
376
            {
377
                this._addHotspot(wedgeStyle, graphOrder, i);
378
            }
379
        }
380
        this._clearMarkerCache();
381
    },
382
 
383
    /**
384
     * @protected
385
     *
386
     * Method used by `styles` setter. Overrides base implementation.
387
     *
388
     * @method _setStyles
389
     * @param {Object} newStyles Hash of properties to update.
390
     * @return Object
391
     */
392
    _setStyles: function(val)
393
    {
394
        if(!val.marker)
395
        {
396
            val = {marker:val};
397
        }
398
        val = this._parseMarkerStyles(val);
399
        return Y.PieSeries.superclass._mergeStyles.apply(this, [val, this._getDefaultStyles()]);
400
    },
401
 
402
    /**
403
     *  Adds an interactive map when rendering in canvas.
404
     *
405
     *  @method _addHotspot
406
     *  @param {Object} cfg Object containing data used to draw the hotspot
407
     *  @param {Number} seriesIndex Index of series in the `seriesCollection`.
408
     *  @param {Number} index Index of the marker using the hotspot.
409
     *  @private
410
     */
411
    _addHotspot: function(cfg, seriesIndex, index)
412
    {
413
        var areaNode = DOCUMENT.createElement("area"),
414
            i = 1,
415
            x = cfg.cx,
416
            y = cfg.cy,
417
            arc = cfg.arc,
418
            startAngle = cfg.startAngle - arc,
419
            endAngle = cfg.startAngle,
420
            radius = cfg.radius,
421
            ax = x + Math.cos(startAngle / 180 * Math.PI) * radius,
422
            ay = y + Math.sin(startAngle / 180 * Math.PI) * radius,
423
            bx = x + Math.cos(endAngle / 180 * Math.PI) * radius,
424
            by = y + Math.sin(endAngle / 180 * Math.PI) * radius,
425
            numPoints = Math.floor(arc/10) - 1,
426
            divAngle = (arc/(Math.floor(arc/10)) / 180) * Math.PI,
427
            angleCoord = Math.atan((ay - y)/(ax - x)),
428
            pts = x + ", " + y + ", " + ax + ", " + ay,
429
            cosAng,
430
            sinAng,
431
            multDivAng;
432
        for(i = 1; i <= numPoints; ++i)
433
        {
434
            multDivAng = divAngle * i;
435
            cosAng = Math.cos(angleCoord + multDivAng);
436
            sinAng = Math.sin(angleCoord + multDivAng);
437
            if(startAngle <= 90)
438
            {
439
                pts += ", " + (x + (radius * Math.cos(angleCoord + (divAngle * i))));
440
                pts += ", " + (y + (radius * Math.sin(angleCoord + (divAngle * i))));
441
            }
442
            else
443
            {
444
                pts += ", " + (x - (radius * Math.cos(angleCoord + (divAngle * i))));
445
                pts += ", " + (y - (radius * Math.sin(angleCoord + (divAngle * i))));
446
            }
447
        }
448
        pts += ", " + bx + ", " + by;
449
        pts += ", " + x + ", " + y;
450
        this._map.appendChild(areaNode);
451
        areaNode.setAttribute("class", SERIES_MARKER);
452
        areaNode.setAttribute("id", "hotSpot_" + seriesIndex + "_" + index);
453
        areaNode.setAttribute("shape", "polygon");
454
        areaNode.setAttribute("coords", pts);
455
        this._areaNodes.push(areaNode);
456
 
457
    },
458
 
459
    /**
460
     * Resizes and positions markers based on a mouse interaction.
461
     *
462
     * @method updateMarkerState
463
     * @param {String} type state of the marker
464
     * @param {Number} i index of the marker
465
     * @protected
466
     */
467
    updateMarkerState: function(type, i)
468
    {
469
        if(this._markers[i])
470
        {
471
            var state = this._getState(type),
472
                markerStyles,
473
                indexStyles,
474
                marker = this._markers[i],
475
                styles = this.get("styles").marker;
476
            markerStyles = state === "off" || !styles[state] ? styles : styles[state];
477
            indexStyles = this._mergeStyles(markerStyles, {});
478
            indexStyles.fill.color = indexStyles.fill.colors[i % indexStyles.fill.colors.length];
479
            indexStyles.fill.alpha = indexStyles.fill.alphas[i % indexStyles.fill.alphas.length];
480
            marker.set(indexStyles);
481
        }
482
    },
483
 
484
    /**
485
     * Creates a shape to be used as a marker.
486
     *
487
     * @method _createMarker
488
     * @param {Object} styles Hash of style properties.
489
     * @return Shape
490
     * @private
491
     */
492
    _createMarker: function(styles)
493
    {
494
        var graphic = this.get("graphic"),
495
            marker,
496
            cfg = this._copyObject(styles);
497
        marker = graphic.addShape(cfg);
498
        marker.addClass(SERIES_MARKER);
499
        return marker;
500
    },
501
 
502
    /**
503
     * Creates a cache of markers for reuse.
504
     *
505
     * @method _createMarkerCache
506
     * @private
507
     */
508
    _clearMarkerCache: function()
509
    {
510
        var len = this._markerCache.length,
511
            i = 0,
512
            marker;
513
        for(; i < len; ++i)
514
        {
515
            marker = this._markerCache[i];
516
            if(marker)
517
            {
518
                marker.destroy();
519
            }
520
        }
521
        this._markerCache = [];
522
    },
523
 
524
    /**
525
     * Gets the default style values for the markers.
526
     *
527
     * @method _getPlotDefaults
528
     * @return Object
529
     * @private
530
     */
531
    _getPlotDefaults: function()
532
    {
533
         var defs = {
534
            padding:{
535
                top: 0,
536
                left: 0,
537
                right: 0,
538
                bottom: 0
539
            },
540
            fill:{
541
                alphas:["1"]
542
            },
543
            border: {
544
                weight: 0,
545
                alpha: 1
546
            }
547
        };
548
        defs.fill.colors = this._defaultSliceColors;
549
        defs.border.colors = this._defaultBorderColors;
550
        return defs;
551
    }
552
}, {
553
    ATTRS: {
554
        /**
555
         * Read-only attribute indicating the type of series.
556
         *
557
         * @attribute type
558
         * @type String
559
         * @default pie
560
         */
561
        type: {
562
            value: "pie"
563
        },
564
 
565
        /**
566
         * Order of this instance of this `type`.
567
         *
568
         * @attribute order
569
         * @type Number
570
         */
571
        order: {},
572
 
573
        /**
574
         * Reference to the `Graph` in which the series is drawn into.
575
         *
576
         * @attribute graph
577
         * @type Graph
578
         */
579
        graph: {},
580
 
581
        /**
582
         * Reference to the `Axis` instance used for assigning
583
         * category values to the graph.
584
         *
585
         * @attribute categoryAxis
586
         * @type Axis
587
         */
588
        categoryAxis: {
589
            value: null,
590
 
591
            validator: function(value)
592
            {
593
                return value !== this.get("categoryAxis");
594
            }
595
        },
596
 
597
        /**
598
         * Reference to the `Axis` instance used for assigning
599
         * series values to the graph.
600
         *
601
         * @attribute categoryAxis
602
         * @type Axis
603
         */
604
        valueAxis: {
605
            value: null,
606
 
607
            validator: function(value)
608
            {
609
                return value !== this.get("valueAxis");
610
            }
611
        },
612
 
613
        /**
614
         * Indicates which array to from the hash of value arrays in
615
         * the category `Axis` instance.
616
         *
617
         * @attribute categoryKey
618
         * @type String
619
         */
620
        categoryKey: {
621
            value: null,
622
 
623
            validator: function(value)
624
            {
625
                return value !== this.get("categoryKey");
626
            }
627
        },
628
        /**
629
         * Indicates which array to from the hash of value arrays in
630
         * the value `Axis` instance.
631
         *
632
         * @attribute valueKey
633
         * @type String
634
         */
635
        valueKey: {
636
            value: null,
637
 
638
            validator: function(value)
639
            {
640
                return value !== this.get("valueKey");
641
            }
642
        },
643
 
644
        /**
645
         * Name used for for displaying category data
646
         *
647
         * @attribute categoryDisplayName
648
         * @type String
649
         */
650
        categoryDisplayName: {
651
            setter: function(val)
652
            {
653
                this._categoryDisplayName = val;
654
                return val;
655
            },
656
 
657
            getter: function()
658
            {
659
                return this._categoryDisplayName || this.get("categoryKey");
660
            }
661
        },
662
 
663
        /**
664
         * Name used for for displaying value data
665
         *
666
         * @attribute valueDisplayName
667
         * @type String
668
         */
669
        valueDisplayName: {
670
            setter: function(val)
671
            {
672
                this._valueDisplayName = val;
673
                return val;
674
            },
675
 
676
            getter: function()
677
            {
678
                return this._valueDisplayName || this.get("valueKey");
679
            }
680
        },
681
 
682
        /**
683
         * @attribute slices
684
         * @type Array
685
         * @private
686
         */
687
        slices: null
688
 
689
        /**
690
         * Style properties used for drawing markers. This attribute is inherited from `MarkerSeries`. Below are  the default
691
         * values:
692
         *  <dl>
693
         *      <dt>fill</dt><dd>A hash containing the following values:
694
         *          <dl>
695
         *              <dt>colors</dt><dd>An array of colors to be used for the marker fills. The color for each marker  is
696
         *              retrieved from the array below:<br/>
697
         *              `["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]`
698
         *              </dd>
699
         *              <dt>alphas</dt><dd>An array of alpha references (Number from 0 to 1) indicating the opacity of each marker
700
         *              fill. The default value is [1].</dd>
701
         *          </dl>
702
         *      </dd>
703
         *      <dt>border</dt><dd>A hash containing the following values:
704
         *          <dl>
705
         *              <dt>color</dt><dd>An array of colors to be used for the marker borders. The color for each marker is
706
         *              retrieved from the array below:<br/>
707
         *              `["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]`
708
         *              <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
709
         *              <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
710
         *          </dl>
711
         *      </dd>
712
         *      <dt>over</dt><dd>hash containing styles for markers when highlighted by a `mouseover` event. The default
713
         *      values for each style is null. When an over style is not set, the non-over value will be used. For example,
714
         *      the default value for `marker.over.fill.color` is equivalent to `marker.fill.color`.</dd>
715
         *  </dl>
716
         *
717
         * @attribute styles
718
         * @type Object
719
         */
720
    }
721
});
722
 
723
 
724
}, '3.18.1', {"requires": ["series-base", "series-plot-util"]});