Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
    /**
2
    * o-------------------------------------------------------------------------------o
3
    * | This file is part of the RGraph package. RGraph is Free software, licensed    |
4
    * | under the MIT license - so it's free to use for all purposes. Extended        |
5
    * | support is available if required and donations are always welcome! You can    |
6
    * | read more here:                                                               |
7
    * |                         http://www.rgraph.net/support                         |
8
    * o-------------------------------------------------------------------------------o
9
    */
10
 
11
    if (typeof(RGraph) == 'undefined') RGraph = {};
12
 
13
    /**
14
    * The rose chart constuctor
15
    *
16
    * @param object canvas
17
    * @param array data
18
    */
19
    RGraph.Rose = function (id, data)
20
    {
21
        this.id                = id;
22
        this.canvas            = document.getElementById(typeof id === 'object' ? id.id : id);
23
        this.context           = this.canvas.getContext('2d');
24
        this.data              = data;
25
        this.canvas.__object__ = this;
26
        this.type              = 'rose';
27
        this.isRGraph          = true;
28
        this.uid               = RGraph.CreateUID();
29
        this.canvas.uid        = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
30
        this.colorsParsed      = false;
31
        this.coordsText        = [];
32
        this.original_colors   = [];
33
 
34
 
35
        /**
36
        * Compatibility with older browsers
37
        */
38
        RGraph.OldBrowserCompat(this.context);
39
 
40
 
41
        this.centerx = 0;
42
        this.centery = 0;
43
        this.radius  = 0;
44
        this.max     = 0;
45
        this.angles  = [];
46
        this.angles2 = [];
47
 
48
        this.properties = {
49
            'chart.background.axes':        true,
50
            'chart.background.axes.color':  'black',
51
            'chart.background.grid':        true,
52
            'chart.background.grid.color':  '#ccc',
53
            'chart.background.grid.size':   null,
54
            'chart.background.grid.spokes': null,
55
            'chart.background.grid.count':  5,
56
            'chart.centerx':                null,
57
            'chart.centery':                null,
58
            'chart.radius':                 null,
59
            'chart.angles.start':           0,
60
            'chart.colors':                 ['rgba(255,0,0,0.5)', 'rgba(255,255,0,0.5)', 'rgba(0,255,255,0.5)', 'rgb(0,255,0)', 'gray', 'blue', 'rgb(255,128,255)','green', 'pink', 'gray', 'aqua'],
61
            'chart.colors.sequential':      false,
62
            'chart.colors.alpha':           null,
63
            'chart.margin':                 0,
64
            'chart.strokestyle':            '#aaa',
65
            'chart.gutter.left':            25,
66
            'chart.gutter.right':           25,
67
            'chart.gutter.top':             25,
68
            'chart.gutter.bottom':          25,
69
            'chart.title':                  '',
70
            'chart.title.background':       null,
71
            'chart.title.hpos':             null,
72
            'chart.title.vpos':             null,
73
            'chart.title.bold':             true,
74
            'chart.title.font':             null,
75
            'chart.title.x':                null,
76
            'chart.title.y':                null,
77
            'chart.title.halign':           null,
78
            'chart.title.valign':           null,
79
            'chart.labels':                 null,
80
            'chart.labels.position':       'center',
81
            'chart.labels.axes':            'nsew',
82
            'chart.labels.offset':          0,
83
            'chart.text.color':             'black',
84
            'chart.text.font':              'Arial',
85
            'chart.text.size':              10,
86
            'chart.key':                    null,
87
            'chart.key.background':         'white',
88
            'chart.key.position':           'graph',
89
            'chart.key.halign':             'right',
90
            'chart.key.shadow':             false,
91
            'chart.key.shadow.color':       '#666',
92
            'chart.key.shadow.blur':        3,
93
            'chart.key.shadow.offsetx':     2,
94
            'chart.key.shadow.offsety':     2,
95
            'chart.key.position.gutter.boxed': false,
96
            'chart.key.position.x':         null,
97
            'chart.key.position.y':         null,
98
            'chart.key.color.shape':        'square',
99
            'chart.key.rounded':            true,
100
            'chart.key.linewidth':          1,
101
            'chart.key.colors':             null,
102
            'chart.key.interactive':        false,
103
            'chart.key.interactive.highlight.chart.stroke': 'black',
104
            'chart.key.interactive.highlight.chart.fill': 'rgba(255,255,255,0.7)',
105
            'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
106
            'chart.key.text.color':         'black',
107
            'chart.contextmenu':            null,
108
            'chart.tooltips':               null,
109
            'chart.tooltips.event':         'onclick',
110
            'chart.tooltips.effect':        'fade',
111
            'chart.tooltips.css.class':     'RGraph_tooltip',
112
            'chart.tooltips.highlight':     true,
113
            'chart.highlight.stroke':       'rgba(0,0,0,0)',
114
            'chart.highlight.fill':         'rgba(255,255,255,0.7)',
115
            'chart.annotatable':            false,
116
            'chart.annotate.color':         'black',
117
            'chart.zoom.factor':            1.5,
118
            'chart.zoom.fade.in':           true,
119
            'chart.zoom.fade.out':          true,
120
            'chart.zoom.hdir':              'right',
121
            'chart.zoom.vdir':              'down',
122
            'chart.zoom.frames':            25,
123
            'chart.zoom.delay':             16.666,
124
            'chart.zoom.shadow':            true,
125
            'chart.zoom.background':        true,
126
            'chart.zoom.action':            'zoom',
127
            'chart.resizable':              false,
128
            'chart.resize.handle.adjust':   [0,0],
129
            'chart.resize.handle.background': null,
130
            'chart.adjustable':             false,
131
            'chart.ymax':                   null,
132
            'chart.ymin':                   0,
133
            'chart.scale.decimals':         null,
134
            'chart.scale.point':            '.',
135
            'chart.scale.thousand':         ',',
136
            'chart.variant':                'stacked',
137
            'chart.exploded':               0,
138
            'chart.events.mousemove':       null,
139
            'chart.events.click':           null,
140
            'chart.animation.roundrobin.factor':  1,
141
            'chart.animation.roundrobin.radius': true,
142
            'chart.animation.grow.multiplier': 1,
143
            'chart.labels.count':              5
144
        }
145
 
146
 
147
 
148
        /**
149
        * Create the $ objects. In the case of non-equi-angular rose charts it actually creates too many $ objects,
150
        * but it doesn't matter.
151
        */
152
        var linear_data = RGraph.array_linearize(this.data);
153
        for (var i=0; i<linear_data.length; ++i) {
154
            this["$" + i] = {}
155
        }
156
 
157
 
158
        /**
159
        * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
160
        * done already
161
        */
162
        if (!this.canvas.__rgraph_aa_translated__) {
163
            this.context.translate(0.5,0.5);
164
 
165
            this.canvas.__rgraph_aa_translated__ = true;
166
        }
167
 
168
 
169
 
170
 
171
        ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
172
 
173
 
174
 
175
 
176
        var RG   = RGraph;
177
        var ca   = this.canvas;
178
        var co   = ca.getContext('2d');
179
        var prop = this.properties;
180
        //var $jq  = jQuery;
181
 
182
 
183
 
184
 
185
        //////////////////////////////////// METHODS ///////////////////////////////////////
186
 
187
 
188
 
189
 
190
        /**
191
        * A simple setter
192
        *
193
        * @param string name  The name of the property to set
194
        * @param string value The value of the property
195
        */
196
        this.Set = function (name, value)
197
        {
198
            /**
199
            * This should be done first - prepend the propertyy name with "chart." if necessary
200
            */
201
            if (name.substr(0,6) != 'chart.') {
202
                name = 'chart.' + name;
203
            }
204
 
205
            prop[name.toLowerCase()] = value;
206
 
207
            return this;
208
        }
209
 
210
 
211
 
212
 
213
        /**
214
        * A simple getter
215
        *
216
        * @param string name The name of the property to get
217
        */
218
        this.Get = function (name)
219
        {
220
            /**
221
            * This should be done first - prepend the property name with "chart." if necessary
222
            */
223
            if (name.substr(0,6) != 'chart.') {
224
                name = 'chart.' + name;
225
            }
226
 
227
            return prop[name.toLowerCase()];
228
        }
229
 
230
 
231
 
232
 
233
        /**
234
        * This method draws the rose chart
235
        */
236
        this.Draw = function ()
237
        {
238
            /**
239
            * Fire the onbeforedraw event
240
            */
241
            RG.FireCustomEvent(this, 'onbeforedraw');
242
 
243
 
244
 
245
            /**
246
            * This doesn't affect the chart, but is used for compatibility
247
            */
248
            this.gutterLeft   = prop['chart.gutter.left'];
249
            this.gutterRight  = prop['chart.gutter.right'];
250
            this.gutterTop    = prop['chart.gutter.top'];
251
            this.gutterBottom = prop['chart.gutter.bottom'];
252
 
253
            // Calculate the radius
254
            this.radius       = (Math.min(ca.width - this.gutterLeft - this.gutterRight, ca.height - this.gutterTop - this.gutterBottom) / 2);
255
            this.centerx      = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
256
            this.centery      = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
257
            this.angles       = [];
258
            this.angles2      = [];
259
            this.total        = 0;
260
            this.startRadians = prop['chart.angles.start'];
261
 
262
            /**
263
            * Change the centerx marginally if the key is defined
264
            */
265
            if (prop['chart.key'] && prop['chart.key'].length > 0 && prop['chart.key'].length >= 3) {
266
                this.centerx = this.centerx - this.gutterRight + 5;
267
            }
268
 
269
 
270
 
271
            // User specified radius, centerx and centery
272
            if (typeof(prop['chart.centerx']) == 'number') this.centerx = prop['chart.centerx'];
273
            if (typeof(prop['chart.centery']) == 'number') this.centery = prop['chart.centery'];
274
            if (typeof(prop['chart.radius']) == 'number')  this.radius  = prop['chart.radius'];
275
 
276
            /**
277
            * Parse the colors for gradients. Its down here so that the center X/Y can be used
278
            */
279
            if (!this.colorsParsed) {
280
 
281
                this.parseColors();
282
 
283
                // Don't want to do this again
284
                this.colorsParsed = true;
285
            }
286
 
287
            this.DrawBackground();
288
            this.DrawRose();
289
            this.DrawLabels();
290
 
291
            /**
292
            * Setup the context menu if required
293
            */
294
            if (prop['chart.contextmenu']) {
295
                RG.ShowContext(this);
296
            }
297
 
298
 
299
            /**
300
            * This function enables resizing
301
            */
302
            if (prop['chart.resizable']) {
303
                RG.AllowResizing(this);
304
            }
305
 
306
 
307
            /**
308
            * This function enables adjusting
309
            */
310
            if (prop['chart.adjustable']) {
311
                RG.AllowAdjusting(this);
312
            }
313
 
314
 
315
            /**
316
            * This installs the event listeners
317
            */
318
            RG.InstallEventListeners(this);
319
 
320
 
321
            /**
322
            * Fire the RGraph ondraw event
323
            */
324
            RG.FireCustomEvent(this, 'ondraw');
325
 
326
            return this;
327
        }
328
 
329
 
330
 
331
 
332
        /**
333
        * This method draws the rose charts background
334
        */
335
        this.DrawBackground = function ()
336
        {
337
            co.lineWidth = 1;
338
 
339
 
340
            // Draw the background grey circles/spokes
341
            if (prop['chart.background.grid']) {
342
                if (typeof(prop['chart.background.grid.count']) == 'number') {
343
                    prop['chart.background.grid.size'] = this.radius / prop['chart.background.grid.count'];
344
                }
345
 
346
                co.beginPath();
347
                    co.strokeStyle = prop['chart.background.grid.color'];
348
 
349
                    // Radius must be greater than 0 for Opera to work
350
                    for (var i=prop['chart.background.grid.size']; i<=this.radius; i+=prop['chart.background.grid.size']) {
351
 
352
                        // Hmmm... This is questionable
353
                        co.moveTo(this.centerx + i, this.centery);
354
 
355
                        // Radius must be greater than 0 for Opera to work
356
                        co.arc(this.centerx,
357
                               this.centery,
358
                               i,
359
                               0,
360
                               TWOPI,
361
                               false);
362
                    }
363
                co.stroke();
364
 
365
 
366
 
367
 
368
 
369
 
370
                // Draw the background lines that go from the center outwards
371
                co.beginPath();
372
                    if (typeof(prop['chart.background.grid.spokes']) == 'number') {
373
 
374
                        var num = (360 / prop['chart.background.grid.spokes']);
375
 
376
                        for (var i=num; i<=360; i+=num) {
377
 
378
                            // Radius must be greater than 0 for Opera to work
379
                            co.arc(this.centerx,
380
                                   this.centery,
381
                                   this.radius,
382
                                   ((i / (180 / PI)) - HALFPI) + this.startRadians,
383
                                   (((i + 0.0001) / (180 / PI)) - HALFPI) + this.startRadians,
384
                                   false);
385
 
386
                            co.lineTo(this.centerx, this.centery);
387
                        }
388
                    } else {
389
                        for (var i=15; i<=360; i+=15) {
390
 
391
                            // Radius must be greater than 0 for Opera to work
392
                            co.arc(this.centerx,
393
                                   this.centery,
394
                                   this.radius,
395
                                   (i / (180/ PI)) - HALFPI,
396
                                   ((i + 0.0001) / (180/ PI)) - HALFPI,
397
                                   false);
398
 
399
                            co.lineTo(this.centerx, this.centery);
400
                        }
401
                    }
402
                co.stroke();
403
            }
404
 
405
 
406
 
407
            if (prop['chart.background.axes']) {
408
                co.beginPath();
409
                co.strokeStyle = prop['chart.background.axes.color'];
410
 
411
                // Draw the X axis
412
                co.moveTo(this.centerx - this.radius, Math.round(this.centery) );
413
                co.lineTo(this.centerx + this.radius, Math.round(this.centery) );
414
 
415
                // Draw the X ends
416
                co.moveTo(Math.round(this.centerx - this.radius), this.centery - 5);
417
                co.lineTo(Math.round(this.centerx - this.radius), this.centery + 5);
418
                co.moveTo(Math.round(this.centerx + this.radius), this.centery - 5);
419
                co.lineTo(Math.round(this.centerx + this.radius), this.centery + 5);
420
 
421
                // Draw the X check marks
422
                for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=(this.radius / 5)) {
423
                    co.moveTo(Math.round(i),  this.centery - 3);
424
                    co.lineTo(Math.round(i),  this.centery + 3.5);
425
                }
426
 
427
                // Draw the Y check marks
428
                for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=(this.radius / 5)) {
429
                    co.moveTo(this.centerx - 3, Math.round(i));
430
                    co.lineTo(this.centerx + 3, Math.round(i));
431
                }
432
 
433
                // Draw the Y axis
434
                co.moveTo(Math.round(this.centerx), this.centery - this.radius);
435
                co.lineTo(Math.round(this.centerx), this.centery + this.radius);
436
 
437
                // Draw the Y ends
438
                co.moveTo(this.centerx - 5, Math.round(this.centery - this.radius));
439
                co.lineTo(this.centerx + 5, Math.round(this.centery - this.radius));
440
 
441
                co.moveTo(this.centerx - 5, Math.round(this.centery + this.radius));
442
                co.lineTo(this.centerx + 5, Math.round(this.centery + this.radius));
443
 
444
                // Stroke it
445
                co.closePath();
446
                co.stroke();
447
            }
448
        }
449
 
450
 
451
 
452
 
453
        /**
454
        * This method draws the data on the graph
455
        */
456
        this.DrawRose = function ()
457
        {
458
            var max    = 0;
459
            var data   = this.data;
460
            var margin = RGraph.degrees2Radians(prop['chart.margin']);
461
 
462
            // Must be at least two data points
463
            //if (data.length < 2) {
464
            //    alert('[ROSE] Must be at least two data points! [' + data + ']');
465
            //    return;
466
            //}
467
 
468
            // Work out the maximum value and the sum
469
            if (RG.is_null(prop['chart.ymax'])) {
470
 
471
                // Work out the max
472
                for (var i=0; i<data.length; ++i) {
473
                    if (typeof(data[i]) == 'number') {
474
                        max = Math.max(max, data[i]);
475
                    } else if (typeof(data[i]) == 'object' && prop['chart.variant'] == 'non-equi-angular') {
476
                        max = Math.max(max, data[i][0]);
477
 
478
                    // Fallback is stacked
479
                    } else {
480
                        max = Math.max(max, RG.array_sum(data[i]));
481
                    }
482
                }
483
 
484
                this.scale2 = RG.getScale2(this, {
485
                                                  'max':max,
486
                                                  'min':0,
487
                                                  'scale.thousand':prop['chart.scale.thousand'],
488
                                                  'scale.point':prop['chart.scale.point'],
489
                                                  'scale.decimals':prop['chart.scale.decimals'],
490
                                                  'ylabels.count':prop['chart.labels.count'],
491
                                                  'scale.round':prop['chart.scale.round'],
492
                                                  'units.pre': prop['chart.units.pre'],
493
                                                  'units.post': prop['chart.units.post']
494
                                                 });
495
                this.max = this.scale2.max;
496
 
497
            } else {
498
 
499
                var ymax = prop['chart.ymax'];
500
 
501
 
502
 
503
                this.scale2 = RG.getScale2(this, {
504
                                                  'max':ymax,
505
                                                  'strict':true,
506
                                                  'scale.thousand':prop['chart.scale.thousand'],
507
                                                  'scale.point':prop['chart.scale.point'],
508
                                                  'scale.decimals':prop['chart.scale.decimals'],
509
                                                  'ylabels.count':prop['chart.labels.count'],
510
                                                  'scale.round':prop['chart.scale.round'],
511
                                                  'units.pre': prop['chart.units.pre'],
512
                                                  'units.post': prop['chart.units.post']
513
                                                 });
514
                this.max = this.scale2.max
515
            }
516
 
517
            this.sum = RG.array_sum(data);
518
 
519
            // Move to the centre
520
            co.moveTo(this.centerx, this.centery);
521
 
522
            co.stroke(); // Stroke the background so it stays grey
523
 
524
            // Transparency
525
            if (prop['chart.colors.alpha']) {
526
                co.globalAlpha = prop['chart.colors.alpha'];
527
            }
528
 
529
            /*******************************************************
530
            * A non-equi-angular Rose chart
531
            *******************************************************/
532
            if (typeof(prop['chart.variant']) == 'string' && prop['chart.variant'] == 'non-equi-angular') {
533
                /*******************************************************
534
                * NON-EQUI-ANGULAR GOES HERE
535
                *******************************************************/
536
                var total=0;
537
                for (var i=0; i<data.length; ++i) {
538
                    total += data[i][1];
539
                }
540
 
541
 
542
                for (var i=0; i<this.data.length; ++i) {
543
 
544
                    var segmentRadians = ((this.data[i][1] / total) * TWOPI);
545
                    var radius         = ((this.data[i][0] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius;
546
                        radius = radius * prop['chart.animation.grow.multiplier'];
547
 
548
                    co.strokeStyle = prop['chart.strokestyle'];
549
                    co.fillStyle   = prop['chart.colors'][0];
550
 
551
                    if (prop['chart.colors.sequential']) {
552
                        co.fillStyle = prop['chart.colors'][i];
553
                    }
554
 
555
                    co.beginPath(); // Begin the segment
556
 
557
                        var startAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) - HALFPI + margin;
558
                        var endAngle   = ((this.startRadians + segmentRadians) * prop['chart.animation.roundrobin.factor']) - HALFPI - margin;
559
 
560
                        var exploded  = this.getexploded(i, startAngle, endAngle, prop['chart.exploded']);
561
                        var explodedX = exploded[0];
562
                        var explodedY = exploded[1];
563
 
564
 
565
                        co.arc(this.centerx + explodedX,
566
                               this.centery + explodedY,
567
                               prop['chart.animation.roundrobin.radius'] ? radius * prop['chart.animation.roundrobin.factor'] : radius,
568
                               startAngle,
569
                               endAngle,
570
                               0);
571
                        co.lineTo(this.centerx + explodedX, this.centery + explodedY);
572
                    co.closePath(); // End the segment
573
 
574
                    co.stroke();
575
                    co.fill();
576
 
577
                    // Store the start and end angles
578
 
579
                    this.angles.push(gg = [
580
                                      startAngle,
581
                                      endAngle,
582
                                      0,
583
                                      radius,
584
                                      this.centerx + explodedX,
585
                                      this.centery + explodedY
586
                                     ]);
587
 
588
                    this.startRadians += segmentRadians;
589
                }
590
            } else {
591
 
592
                var sequentialColorIndex = 0;
593
 
594
                /*******************************************************
595
                * Draw regular segments here
596
                *******************************************************/
597
                for (var i=0; i<this.data.length; ++i) {
598
 
599
                    co.strokeStyle = prop['chart.strokestyle'];
600
                    co.fillStyle = prop['chart.colors'][0];
601
 
602
                    /*******************************************************
603
                    * This allows sequential colors
604
                    *******************************************************/
605
                    if (prop['chart.colors.sequential']) {
606
                        co.fillStyle = prop['chart.colors'][i];
607
                    }
608
 
609
                    var segmentRadians = (1 / this.data.length) * TWOPI;
610
 
611
                    if (typeof(this.data[i]) == 'number') {
612
                        co.beginPath(); // Begin the segment
613
 
614
                            var radius = ((this.data[i] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius;
615
                                radius = radius * prop['chart.animation.grow.multiplier'];
616
 
617
                            var startAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) - HALFPI + margin;
618
                            var endAngle   = (this.startRadians * prop['chart.animation.roundrobin.factor']) + (segmentRadians * prop['chart.animation.roundrobin.factor']) - HALFPI - margin;
619
 
620
                            var exploded  = this.getexploded(i, startAngle, endAngle, prop['chart.exploded']);
621
                            var explodedX = exploded[0];
622
                            var explodedY = exploded[1];
623
 
624
                            co.arc(this.centerx + explodedX,
625
                                   this.centery + explodedY,
626
                                   prop['chart.animation.roundrobin.radius'] ? radius * prop['chart.animation.roundrobin.factor'] : radius,
627
                                   startAngle,
628
                                   endAngle,
629
                                   0);
630
                            co.lineTo(this.centerx + explodedX, this.centery + explodedY);
631
                        co.closePath(); // End the segment
632
                        co.stroke();
633
                        co.fill();
634
 
635
                        if (endAngle == 0) {
636
                            //endAngle = TWOPI;
637
                        }
638
 
639
                        // Store the start and end angles
640
                        this.angles.push([
641
                                          startAngle,
642
                                          endAngle,
643
                                          0,
644
                                          radius * prop['chart.animation.roundrobin.factor'],
645
                                          this.centerx + explodedX,
646
                                          this.centery + explodedY
647
                                         ]);
648
 
649
                    /*******************************************************
650
                    * Draw a stacked segment
651
                    *******************************************************/
652
                    } else if (typeof(this.data[i]) == 'object') {
653
 
654
                        var margin = prop['chart.margin'] / (180 / PI);
655
 
656
                        // Initialise the angles2 array so there's no undefined error
657
                        if (!this.angles2[i]) {
658
                            this.angles2[i] = [];
659
                        }
660
 
661
 
662
                        for (var j=0; j<this.data[i].length; ++j) {
663
 
664
                            var startAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) - HALFPI + margin;
665
                            var endAngle  = (this.startRadians * prop['chart.animation.roundrobin.factor'])+ (segmentRadians * prop['chart.animation.roundrobin.factor']) - HALFPI - margin;
666
 
667
                            var exploded  = this.getexploded(i, startAngle, endAngle, prop['chart.exploded']);
668
                            var explodedX = exploded[0];
669
                            var explodedY = exploded[1];
670
 
671
                            co.fillStyle = prop['chart.colors'][j];
672
 
673
                            // This facilitates sequential color support
674
                            if (prop['chart.colors.sequential']) {
675
                                co.fillStyle = prop['chart.colors'][sequentialColorIndex++];
676
                            }
677
 
678
                            if (j == 0) {
679
                                co.beginPath(); // Begin the segment
680
                                    var startRadius = 0;
681
                                    var endRadius = ((this.data[i][j] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius;
682
                                        endRadius = endRadius * prop['chart.animation.grow.multiplier'];
683
 
684
                                    co.arc(this.centerx + explodedX,
685
                                           this.centery + explodedY,
686
                                           prop['chart.animation.roundrobin.radius'] ? endRadius * prop['chart.animation.roundrobin.factor'] : endRadius,
687
                                           startAngle,
688
                                           endAngle,
689
                                           0);
690
                                    co.lineTo(this.centerx + explodedX, this.centery + explodedY);
691
                                co.closePath(); // End the segment
692
                                co.stroke();
693
                                co.fill();
694
 
695
                                this.angles.push([
696
                                                  startAngle,
697
                                                  endAngle,
698
                                                  0,
699
                                                  endRadius * prop['chart.animation.roundrobin.factor'],
700
                                                  this.centerx + explodedX,
701
                                                  this.centery + explodedY
702
                                                 ]);
703
 
704
                                this.angles2[i].push([
705
                                                      startAngle,
706
                                                      endAngle,
707
                                                      0,
708
                                                      endRadius * prop['chart.animation.roundrobin.factor'],
709
                                                      this.centerx + explodedX,
710
                                                      this.centery + explodedY
711
                                                     ]);
712
 
713
                            } else {
714
 
715
                                co.beginPath(); // Begin the segment
716
 
717
                                    var startRadius = endRadius; // This comes from the prior iteration of this loop
718
                                    var endRadius = (((this.data[i][j] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius) + startRadius;
719
                                        endRadius = endRadius * prop['chart.animation.grow.multiplier'];
720
 
721
                                    co.arc(this.centerx + explodedX,
722
                                           this.centery + explodedY,
723
                                           startRadius  * prop['chart.animation.roundrobin.factor'],
724
                                           startAngle,
725
                                           endAngle,
726
                                           0);
727
 
728
                                    co.arc(this.centerx + explodedX,
729
                                           this.centery + explodedY,
730
                                           endRadius  * prop['chart.animation.roundrobin.factor'],
731
                                           endAngle,
732
                                           startAngle,
733
                                           true);
734
 
735
                                co.closePath(); // End the segment
736
                                co.stroke();
737
                                co.fill();
738
 
739
                                this.angles.push([
740
                                                  startAngle,
741
                                                  endAngle,
742
                                                  startRadius * prop['chart.animation.roundrobin.factor'],
743
                                                  endRadius * prop['chart.animation.roundrobin.factor'],
744
                                                  this.centerx + explodedX,
745
                                                  this.centery + explodedY
746
                                                 ]);
747
 
748
                                this.angles2[i].push([
749
                                                      startAngle,
750
                                                      endAngle,
751
                                                      startRadius * prop['chart.animation.roundrobin.factor'],
752
                                                      endRadius * prop['chart.animation.roundrobin.factor'],
753
                                                      this.centerx + explodedX,
754
                                                      this.centery + explodedY
755
                                                     ]);
756
                            }
757
                        }
758
                    }
759
 
760
                    this.startRadians += segmentRadians;
761
                }
762
            }
763
 
764
            // Turn off the transparency
765
            if (prop['chart.colors.alpha']) {
766
                co.globalAlpha = 1;
767
            }
768
 
769
            // Draw the title if any has been set
770
            if (prop['chart.title']) {
771
                RG.DrawTitle(this,
772
                             prop['chart.title'],
773
                             (ca.height / 2) - this.radius,
774
                             this.centerx,
775
                             prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2);
776
            }
777
        }
778
 
779
 
780
 
781
 
782
        /**
783
        * Unsuprisingly, draws the labels
784
        */
785
        this.DrawLabels = function ()
786
        {
787
            co.lineWidth = 1;
788
            var key = prop['chart.key'];
789
 
790
            if (key && key.length) {
791
                RG.DrawKey(this, key, prop['chart.colors']);
792
            }
793
 
794
            // Set the color to black
795
            co.fillStyle = prop['chart.text.color'];
796
            co.strokeStyle = 'black';
797
 
798
            var radius     = this.radius;
799
            var font       = prop['chart.text.font'];
800
            var size       = prop['chart.text.size'];
801
            var axes       = prop['chart.labels.axes'].toLowerCase();
802
            var decimals   = prop['chart.scale.decimals'];
803
            var units_pre  = prop['chart.units.pre'];
804
            var units_post = prop['chart.units.post'];
805
            var centerx    = this.centerx;
806
            var centery    = this.centery;
807
 
808
            // Draw any circular labels
809
            if (typeof(prop['chart.labels']) == 'object' && prop['chart.labels']) {
810
                this.DrawCircularLabels(co, prop['chart.labels'], font, size, radius + 10);
811
            }
812
 
813
 
814
            // Size can be specified seperately for the scale now
815
            if (typeof(prop['chart.text.size.scale']) == 'number') {
816
                size = prop['chart.text.size.scale'];
817
            }
818
 
819
 
820
            var color = 'rgba(255,255,255,0.8)';
821
 
822
            // The "North" axis labels
823
            if (axes.indexOf('n') > -1) {
824
                for (var i=0; i<prop['chart.labels.count']; ++i) {
825
                    RG.Text2(this, {'font':font,
826
                                    'size':size,
827
                                    'x':centerx,
828
                                    'y':centery - (radius * ((i+1) / prop['chart.labels.count'])),
829
                                    'text':this.scale2.labels[i],
830
                                    'valign':'center',
831
                                    'halign':'center',
832
                                    'bounding':true,
833
                                    'boundingFill':color,
834
                                    'tag': 'scale'
835
                                   });
836
                }
837
            }
838
 
839
            // The "South" axis labels
840
            if (axes.indexOf('s') > -1) {
841
                for (var i=0; i<prop['chart.labels.count']; ++i) {
842
                    RG.Text2(this, {'font':font,
843
                                    'size':size,
844
                                    'x':centerx,
845
                                    'y':centery + (radius * ((i+1) / prop['chart.labels.count'])),
846
                                    'text':this.scale2.labels[i],
847
                                    'valign':'center',
848
                                    'halign':'center',
849
                                    'bounding':true,
850
                                    'boundingFill':color,
851
                                    'tag': 'scale'
852
                                   });
853
                }
854
            }
855
 
856
            // The "East" axis labels
857
            if (axes.indexOf('e') > -1) {
858
                for (var i=0; i<prop['chart.labels.count']; ++i) {
859
                    RG.Text2(this, {'font':font,
860
                                    'size':size,
861
                                    'x':centerx + (radius * ((i+1) / prop['chart.labels.count'])),
862
                                    'y':centery,
863
                                    'text':this.scale2.labels[i],
864
                                    'valign':'center',
865
                                    'halign':'center',
866
                                    'bounding':true,
867
                                    'boundingFill':color,
868
                                    'tag': 'scale'
869
                                   });
870
                }
871
            }
872
 
873
            // The "West" axis labels
874
            if (axes.indexOf('w') > -1) {
875
                for (var i=0; i<prop['chart.labels.count']; ++i) {
876
                    RG.Text2(this, {'font':font,
877
                                    'size':size,
878
                                    'x':centerx - (radius * ((i+1) / prop['chart.labels.count'])),
879
                                    'y':centery,
880
                                    'text':this.scale2.labels[i],
881
                                    'valign':'center',
882
                                    'halign':'center',
883
                                    'bounding':true,
884
                                    'boundingFill':color,
885
                                    'tag': 'scale'
886
                                   });
887
                }
888
            }
889
 
890
            if (axes.length > 0) {
891
                RG.Text2(this, {'font':font,
892
                                'size':size,
893
                                'x':centerx,
894
                                'y':centery,
895
                                'text':typeof(prop['chart.ymin']) == 'number' ? RG.number_format(this, Number(prop['chart.ymin']).toFixed(prop['chart.scale.decimals']), units_pre, units_post) : '0',
896
                                'valign':'center',
897
                                'halign':'center',
898
                                'bounding':true,
899
                                'boundingFill':color,
900
                                'tag': 'scale'
901
                               });
902
            }
903
        }
904
 
905
 
906
 
907
 
908
        /**
909
        * Draws the circular labels that go around the charts
910
        *
911
        * @param labels array The labels that go around the chart
912
        */
913
        this.DrawCircularLabels = function (co, labels, font, size, radius)
914
        {
915
            var variant = prop['chart.variant'];
916
            var position = prop['chart.labels.position'];
917
            var radius   = radius + 5 + prop['chart.labels.offset'];
918
            var centerx  = this.centerx;
919
            var centery  = this.centery;
920
 
921
            for (var i=0; i<labels.length; ++i) {
922
                if (typeof(variant) == 'string' && variant == 'non-equi-angular') {
923
                    var a = Number(this.angles[i][0]) + ((this.angles[i][1] - this.angles[i][0]) / 2);
924
                } else {
925
                    var a = (TWOPI / labels.length) * (i + 1) - (TWOPI / (labels.length * 2));
926
                    var a = a - HALFPI + (prop['chart.labels.position'] == 'edge' ? ((TWOPI / labels.length) / 2) : 0);
927
                }
928
 
929
                var x = centerx + (Math.cos(a) * radius);
930
                var y = centery + (Math.sin(a) * radius);
931
 
932
                // Horizontal alignment
933
                if (x > centerx) {
934
                    halign = 'left';
935
                } else if (Math.round(x) == centerx) {
936
                    halign = 'center';
937
                } else {
938
                    halign = 'right';
939
                }
940
 
941
                RG.Text2(this, {'font':font,
942
                                'size':size,
943
                                'x':x,
944
                                'y':y,
945
                                'text':String(labels[i]),
946
                                'halign':halign,
947
                                'valign':'center',
948
                                    'tag': 'labels'
949
                               });
950
            }
951
        }
952
 
953
 
954
 
955
 
956
 
957
 
958
 
959
 
960
 
961
 
962
 
963
 
964
 
965
 
966
 
967
 
968
 
969
 
970
 
971
 
972
 
973
 
974
 
975
 
976
 
977
 
978
        /**
979
        * This function is for use with circular graph types, eg the Pie or Rose. Pass it your event object
980
        * and it will pass you back the corresponding segment details as an array:
981
        *
982
        * [x, y, r, startAngle, endAngle]
983
        *
984
        * Angles are measured in degrees, and are measured from the "east" axis (just like the canvas).
985
        *
986
        * @param object e   Your event object
987
        */
988
        this.getShape =
989
        this.getSegment = function (e)
990
        {
991
            RG.FixEventObject(e);
992
 
993
            var angles  = this.angles;
994
            var ret     = [];
995
 
996
            /**
997
            * Go through all of the angles checking each one
998
            */
999
            for (var i=0; i<angles.length ; ++i) {
1000
 
1001
                var angleStart  = angles[i][0];
1002
                var angleEnd    = angles[i][1];
1003
                var radiusStart = angles[i][2];
1004
                var radiusEnd   = angles[i][3];
1005
                var centerX     = angles[i][4];
1006
                var centerY     = angles[i][5];
1007
                var mouseXY     = RG.getMouseXY(e);
1008
                var mouseX      = mouseXY[0] - centerX;
1009
                var mouseY      = mouseXY[1] - centerY;
1010
 
1011
                // New click testing (the 0.01 is there because Opera doesn't like 0 as the radius)
1012
                co.beginPath();
1013
                    co.arc(centerX, centerY, radiusStart ? radiusStart : 0.01, angleStart, angleEnd, false);
1014
                    co.arc(centerX, centerY, radiusEnd, angleEnd, angleStart, true);
1015
                co.closePath();
1016
 
1017
                // No stroke() or fill()
1018
 
1019
 
1020
                if (co.isPointInPath(mouseXY[0], mouseXY[1])) {
1021
 
1022
                    angles[i][6] = i;
1023
 
1024
                    if (RG.parseTooltipText) {
1025
                        var tooltip = RG.parseTooltipText(prop['chart.tooltips'], angles[i][6]);
1026
                    }
1027
 
1028
                    // Add the textual keys
1029
                    angles[i]['object']       = this;
1030
                    angles[i]['x']            = angles[i][4];
1031
                    angles[i]['y']            = angles[i][5];
1032
                    angles[i]['angle.start']  = angles[i][0];
1033
                    angles[i]['angle.end']    = angles[i][1];
1034
                    angles[i]['radius.start'] = angles[i][2];
1035
                    angles[i]['radius.end']   = angles[i][3];
1036
                    angles[i]['index']        = angles[i][6];
1037
                    angles[i]['tooltip']      = tooltip ? tooltip : null;
1038
 
1039
                    return angles[i];
1040
                }
1041
            }
1042
 
1043
            return null;
1044
        }
1045
 
1046
 
1047
 
1048
 
1049
 
1050
 
1051
 
1052
 
1053
 
1054
 
1055
 
1056
 
1057
 
1058
 
1059
 
1060
 
1061
 
1062
 
1063
 
1064
 
1065
 
1066
 
1067
 
1068
 
1069
        /**
1070
        * Returns any exploded for a particular segment
1071
        */
1072
        this.getexploded = function (index, startAngle, endAngle, exploded)
1073
        {
1074
            var explodedx, explodedy;
1075
 
1076
            /**
1077
            * Retrieve any exploded - the exploded can be an array of numbers or a single number
1078
            * (which is applied to all segments)
1079
            */
1080
            if (typeof(exploded) == 'object' && typeof(exploded[index]) == 'number') {
1081
                explodedx = Math.cos(((endAngle - startAngle) / 2) + startAngle) * exploded[index];
1082
                explodedy = Math.sin(((endAngle - startAngle) / 2) + startAngle) * exploded[index];
1083
 
1084
            } else if (typeof(exploded) == 'number') {
1085
                explodedx = Math.cos(((endAngle - startAngle) / 2) + startAngle) * exploded;
1086
                explodedy = Math.sin(((endAngle - startAngle) / 2) + startAngle) * exploded;
1087
 
1088
            } else {
1089
                explodedx = 0;
1090
                explodedy = 0;
1091
            }
1092
 
1093
            return [explodedx, explodedy];
1094
        }
1095
 
1096
 
1097
 
1098
 
1099
        /**
1100
        * This function facilitates the installation of tooltip event listeners if
1101
        * tooltips are defined.
1102
        */
1103
        this.AllowTooltips = function ()
1104
        {
1105
            // Preload any tooltip images that are used in the tooltips
1106
            RG.PreLoadTooltipImages(this);
1107
 
1108
 
1109
            /**
1110
            * This installs the window mousedown event listener that lears any
1111
            * highlight that may be visible.
1112
            */
1113
            RG.InstallWindowMousedownTooltipListener(this);
1114
 
1115
 
1116
            /**
1117
            * This installs the canvas mousemove event listener. This function
1118
            * controls the pointer shape.
1119
            */
1120
            RG.InstallCanvasMousemoveTooltipListener(this);
1121
 
1122
 
1123
            /**
1124
            * This installs the canvas mouseup event listener. This is the
1125
            * function that actually shows the appropriate tooltip (if any).
1126
            */
1127
            RG.InstallCanvasMouseupTooltipListener(this);
1128
        }
1129
 
1130
 
1131
 
1132
 
1133
        /**
1134
        * Each object type has its own Highlight() function which highlights the appropriate shape
1135
        *
1136
        * @param object shape The shape to highlight
1137
        */
1138
        this.Highlight = function (shape)
1139
        {
1140
            if (prop['chart.tooltips.highlight']) {
1141
                // Add the new segment highlight
1142
                co.beginPath();
1143
 
1144
                    co.strokeStyle = prop['chart.highlight.stroke'];
1145
                    co.fillStyle = prop['chart.highlight.fill'];
1146
 
1147
                    co.arc(shape['x'], shape['y'], shape['radius.end'], shape['angle.start'], shape['angle.end'], false);
1148
 
1149
                    if (shape['radius.start'] > 0) {
1150
                        co.arc(shape['x'], shape['y'], shape['radius.start'], shape['angle.end'], shape['angle.start'], true);
1151
                    } else {
1152
                        co.lineTo(shape['x'], shape['y']);
1153
                    }
1154
                co.closePath();
1155
 
1156
                co.stroke();
1157
                co.fill();
1158
            }
1159
        }
1160
 
1161
 
1162
 
1163
 
1164
        /**
1165
        * The getObjectByXY() worker method. Don't call this call:
1166
        *
1167
        * RGraph.ObjectRegistry.getObjectByXY(e)
1168
        *
1169
        * @param object e The event object
1170
        */
1171
        this.getObjectByXY = function (e)
1172
        {
1173
            var mouseXY = RGraph.getMouseXY(e);
1174
 
1175
            // Work out the radius
1176
            var radius = RG.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
1177
 
1178
            if (
1179
                   mouseXY[0] > (this.centerx - this.radius)
1180
                && mouseXY[0] < (this.centerx + this.radius)
1181
                && mouseXY[1] > (this.centery - this.radius)
1182
                && mouseXY[1] < (this.centery + this.radius)
1183
                && radius <= this.radius
1184
                ) {
1185
 
1186
                return this;
1187
            }
1188
        }
1189
 
1190
 
1191
 
1192
 
1193
        /**
1194
        * This function positions a tooltip when it is displayed
1195
        *
1196
        * @param obj object    The chart object
1197
        * @param int x         The X coordinate specified for the tooltip
1198
        * @param int y         The Y coordinate specified for the tooltip
1199
        * @param objec tooltip The tooltips DIV element
1200
        */
1201
        this.positionTooltip = function (obj, x, y, tooltip, idx)
1202
        {
1203
            var coordX      = obj.angles[idx][4];
1204
            var coordY      = obj.angles[idx][5];
1205
            var angleStart  = obj.angles[idx][0];
1206
            var angleEnd    = obj.angles[idx][1];
1207
            var radius      = ((obj.angles[idx][3] - obj.angles[idx][2]) / 2) + obj.angles[idx][2];
1208
 
1209
            var angleCenter = ((angleEnd - angleStart) / 2) + angleStart;
1210
            var canvasXY    = RG.getCanvasXY(obj.canvas);
1211
            var gutterLeft  = this.gutterLeft;
1212
            var gutterTop   = this.gutterTop;
1213
            var width       = tooltip.offsetWidth;
1214
            var height      = tooltip.offsetHeight;
1215
 
1216
 
1217
            // By default any overflow is hidden
1218
            tooltip.style.overflow = '';
1219
 
1220
            // The arrow
1221
            var img = new Image();
1222
                img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
1223
                img.style.position = 'absolute';
1224
                img.id = '__rgraph_tooltip_pointer__';
1225
                img.style.top = (tooltip.offsetHeight - 2) + 'px';
1226
            tooltip.appendChild(img);
1227
 
1228
            // Reposition the tooltip if at the edges:
1229
 
1230
            // LEFT edge
1231
            if ((canvasXY[0] + coordX + (Math.cos(angleCenter) * radius) - (width / 2)) < 10) {
1232
                tooltip.style.left = (canvasXY[0] + coordX + (Math.cos(angleCenter) * radius)- (width * 0.1)) + 'px';
1233
                tooltip.style.top = (canvasXY[1] + coordY + (Math.sin(angleCenter) * radius)- height - 5) + 'px';
1234
                img.style.left = ((width * 0.1) - 8.5) + 'px';
1235
 
1236
            // RIGHT edge
1237
            } else if ((canvasXY[0] + coordX + (Math.cos(angleCenter) * radius) + (width / 2)) > (document.body.offsetWidth - 10) ) {
1238
                tooltip.style.left = (canvasXY[0] + coordX + (Math.cos(angleCenter) * radius) - (width * 0.9)) + 'px';
1239
                tooltip.style.top = (canvasXY[1] + coordY + (Math.sin(angleCenter) * radius)- height - 5) + 'px';
1240
                img.style.left = ((width * 0.9) - 8.5) + 'px';
1241
 
1242
            // Default positioning - CENTERED
1243
            } else {
1244
                tooltip.style.left = (canvasXY[0] + coordX + (Math.cos(angleCenter) * radius)- (width / 2)) + 'px';
1245
                tooltip.style.top = (canvasXY[1] + coordY + (Math.sin(angleCenter) * radius)- height - 5) + 'px';
1246
                img.style.left = ((width * 0.5) - 8.5) + 'px';
1247
            }
1248
        }
1249
 
1250
 
1251
 
1252
 
1253
        /**
1254
        * This method gives you the relevant radius for a particular value
1255
        *
1256
        * @param number value The relevant value to get the radius for
1257
        */
1258
        this.getRadius = function (value)
1259
        {
1260
            // Range checking (the Rose minimum is always 0)
1261
            if (value < 0 || value > this.max) {
1262
                return null;
1263
            }
1264
 
1265
            var r = (value / this.max) * this.radius;
1266
 
1267
            return r;
1268
        }
1269
 
1270
 
1271
 
1272
 
1273
        /**
1274
        * This allows for easy specification of gradients
1275
        */
1276
        this.parseColors = function ()
1277
        {
1278
            for (var i=0; i<prop['chart.colors'].length; ++i) {
1279
                prop['chart.colors'][i] = this.parseSingleColorForGradient(prop['chart.colors'][i]);
1280
            }
1281
 
1282
            /**
1283
            * Key colors
1284
            */
1285
            if (!RG.is_null(prop['chart.key.colors'])) {
1286
                for (var i=0; i<prop['chart.key.colors'].length; ++i) {
1287
                    prop['chart.key.colors'][i] = this.parseSingleColorForGradient(prop['chart.key.colors'][i]);
1288
                }
1289
            }
1290
 
1291
            prop['chart.text.color']       = this.parseSingleColorForGradient(prop['chart.text.color']);
1292
            prop['chart.title.color']      = this.parseSingleColorForGradient(prop['chart.title.color']);
1293
            prop['chart.highlight.fill']   = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
1294
            prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
1295
        }
1296
 
1297
 
1298
 
1299
 
1300
        /**
1301
        * This parses a single color value
1302
        */
1303
        this.parseSingleColorForGradient = function (color)
1304
        {
1305
            if (!color || typeof(color) != 'string') {
1306
                return color;
1307
            }
1308
 
1309
            if (color.match(/^gradient\((.*)\)$/i)) {
1310
 
1311
                var parts = RegExp.$1.split(':');
1312
 
1313
                // Create the gradient
1314
                //var grad = context.createLinearGradient(0,0,canvas.width,0);
1315
                var grad = co.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
1316
 
1317
                var diff = 1 / (parts.length - 1);
1318
 
1319
                grad.addColorStop(0, RG.trim(parts[0]));
1320
 
1321
                for (var j=1; j<parts.length; ++j) {
1322
                    grad.addColorStop(j * diff, RG.trim(parts[j]));
1323
                }
1324
            }
1325
 
1326
            return grad ? grad : color;
1327
        }
1328
 
1329
 
1330
 
1331
 
1332
        /**
1333
        * This function handles highlighting an entire data-series for the interactive
1334
        * key
1335
        *
1336
        * @param int index The index of the data series to be highlighted
1337
        */
1338
        this.interactiveKeyHighlight = function (index)
1339
        {
1340
            this.angles2.forEach(function (val, idx, arr)
1341
            {
1342
                var segment = val[index];
1343
 
1344
                if (segment) {
1345
                    co.beginPath();
1346
                        co.lineWidth = 2;
1347
                        co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
1348
                        co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
1349
                        co.arc(segment[4], segment[5], segment[2], segment[0], segment[1], false);
1350
                        co.arc(segment[4], segment[5], segment[3], segment[1], segment[0], true);
1351
                    co.closePath();
1352
                    co.fill();
1353
                    co.stroke();
1354
                }
1355
            });
1356
 
1357
        }
1358
 
1359
 
1360
 
1361
 
1362
        /**
1363
        * Register this object
1364
        */
1365
        RG.Register(this);
1366
    }