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 traditional radar chart constructor
15
    *
16
    * @param string id   The ID of the canvas
17
    * @param array  data An array of data to represent
18
    */
19
    RGraph.Radar = 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.canvas.__object__ = this;
25
        this.type              = 'radar';
26
        this.coords            = [];
27
        this.isRGraph          = true;
28
        this.data              = [];
29
        this.max               = 0;
30
        this.original_data     = [];
31
        this.uid               = RGraph.CreateUID();
32
        this.canvas.uid        = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
33
        this.colorsParsed      = false;
34
        this.coordsText        = [];
35
 
36
        /**
37
        * This allows for passing all of the arguments as one big array instead of as individual daatasets
38
        */
39
        if (typeof arguments[1] == 'object' && typeof arguments[1][0] == 'object') {
40
            for (var i=0,len=arguments[1].length; i<len; ++i) {
41
                this.original_data.push(RGraph.array_clone(arguments[1][i]));
42
                this.data.push(RGraph.array_clone(arguments[1][i]));
43
                this.max = Math.max(this.max, RGraph.array_max(arguments[1][i]));
44
            }
45
 
46
        } else {
47
 
48
            for (var i=1,len=arguments.length; i<len; ++i) {
49
                this.original_data.push(RGraph.array_clone(arguments[i]));
50
                this.data.push(RGraph.array_clone(arguments[i]));
51
                this.max = Math.max(this.max, RGraph.array_max(arguments[i]));
52
            }
53
        }
54
 
55
        /**
56
        * Compatibility with older browsers
57
        */
58
        RGraph.OldBrowserCompat(this.context);
59
 
60
 
61
        this.properties = {
62
            'chart.strokestyle':           '#aaa',
63
            'chart.gutter.left':           25,
64
            'chart.gutter.right':          25,
65
            'chart.gutter.top':            25,
66
            'chart.gutter.bottom':         25,
67
            'chart.linewidth':             1,
68
            'chart.colors':                ['rgba(255,255,0,0.25)','rgba(0,255,255,0.25)','rgba(255,0,0,0.5)', 'red', 'green', 'blue', 'pink', 'aqua','brown','orange','grey'],
69
            'chart.colors.alpha':          null,
70
            'chart.circle':                0,
71
            'chart.circle.fill':           'red',
72
            'chart.circle.stroke':         'black',
73
            'chart.labels':                [],
74
            'chart.labels.offset':         10,
75
            'chart.background.circles':    true,
76
            'chart.background.circles.count': null,
77
            'chart.background.circles.color': '#ddd',
78
            'chart.background.circles.poly':  true,
79
            'chart.text.size':             10,
80
            'chart.text.size.scale':       null,
81
            'chart.text.font':             'Arial',
82
            'chart.text.color':            'black',
83
            'chart.title':                 '',
84
            'chart.title.background':      null,
85
            'chart.title.hpos':            null,
86
            'chart.title.vpos':            null,
87
            'chart.title.color':           'black',
88
            'chart.title.bold':             true,
89
            'chart.title.font':             null,
90
            'chart.title.x':                null,
91
            'chart.title.y':                null,
92
            'chart.title.halign':           null,
93
            'chart.title.valign':           null,
94
            'chart.linewidth':             1,
95
            'chart.key':                   null,
96
            'chart.key.background':        'white',
97
            'chart.key.shadow':            false,
98
            'chart.key.shadow.color':       '#666',
99
            'chart.key.shadow.blur':        3,
100
            'chart.key.shadow.offsetx':     2,
101
            'chart.key.shadow.offsety':     2,
102
            'chart.key.position':          'graph',
103
            'chart.key.halign':             'right',
104
            'chart.key.position.gutter.boxed': false,
105
            'chart.key.position.x':         null,
106
            'chart.key.position.y':         null,
107
            'chart.key.color.shape':        'square',
108
            'chart.key.rounded':            true,
109
            'chart.key.linewidth':          1,
110
            'chart.key.colors':             null,
111
            'chart.key.interactive':        false,
112
            'chart.key.interactive.highlight.chart.stroke': 'rgba(255,0,0,0.3)',
113
            'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
114
            'chart.key.text.color':        'black',
115
            'chart.contextmenu':           null,
116
            'chart.annotatable':           false,
117
            'chart.annotate.color':        'black',
118
            'chart.zoom.factor':           1.5,
119
            'chart.zoom.fade.in':          true,
120
            'chart.zoom.fade.out':         true,
121
            'chart.zoom.hdir':             'right',
122
            'chart.zoom.vdir':             'down',
123
            'chart.zoom.frames':            25,
124
            'chart.zoom.delay':             16.666,
125
            'chart.zoom.shadow':           true,
126
            'chart.zoom.background':        true,
127
            'chart.zoom.action':            'zoom',
128
            'chart.tooltips.effect':        'fade',
129
            'chart.tooltips.event':         'onmousemove',
130
            'chart.tooltips.css.class':     'RGraph_tooltip',
131
            'chart.tooltips.highlight':     true,
132
            'chart.highlight.stroke':       'gray',
133
            'chart.highlight.fill':         'rgba(255,255,255,0.7)',
134
            'chart.highlight.point.radius': 2,
135
            'chart.resizable':              false,
136
            'chart.resize.handle.adjust':   [0,0],
137
            'chart.resize.handle.background': null,
138
            'chart.labels.axes':            '',
139
            'chart.labels.background.fill': 'white',
140
            'chart.labels.boxed':           false,
141
            'chart.labels.axes.bold':       [],
142
            'chart.labels.axes.boxed':      null, // This defaults to true - but that's set in the Draw() method
143
            'chart.labels.axes.boxed.zero': true,
144
            'chart.labels.specific':        [],
145
            'chart.labels.count':           5,
146
            'chart.ymax':                   null,
147
            'chart.accumulative':           false,
148
            'chart.radius':                 null,
149
            'chart.events.click':           null,
150
            'chart.events.mousemove':       null,
151
            'chart.scale.decimals':         0,
152
            'chart.scale.point':            '.',
153
            'chart.scale.thousand':         ',',
154
            'chart.units.pre':              '',
155
            'chart.units.post':             '',
156
            'chart.tooltips':             null,
157
            'chart.tooltips.event':       'onmousemove',
158
            'chart.centerx':              null,
159
            'chart.centery':              null,
160
            'chart.radius':               null,
161
            'chart.numxticks':            5,
162
            'chart.numyticks':            5,
163
            'chart.axes.color':           'rgba(0,0,0,0)',
164
            'chart.highlights':           false,
165
            'chart.highlights.stroke':    '#ddd',
166
            'chart.highlights.fill':      null,
167
            'chart.highlights.radius':    3,
168
            'chart.fill.click':           null,
169
            'chart.fill.mousemove':       null,
170
            'chart.fill.tooltips':        null,
171
            'chart.fill.highlight.fill':   'rgba(255,255,255,0.7)',
172
            'chart.fill.highlight.stroke': 'rgba(0,0,0,0)',
173
            'chart.fill.mousemove.redraw': false,
174
            'chart.animation.trace.clip': 1
175
        }
176
 
177
 
178
 
179
        // Must have at least 3 points
180
        for (var dataset=0; dataset<this.data.length; ++dataset) {
181
            if (this.data[dataset].length < 3) {
182
                alert('[RADAR] You must specify at least 3 data points');
183
                return;
184
            }
185
        }
186
 
187
 
188
        /**
189
        * Linearize the data and then create the $ objects
190
        */
191
        var idx = 0;
192
        for (var dataset=0; dataset<this.data.length; ++dataset) {
193
            for (var i=0,len=this.data[dataset].length; i<len; ++i) {
194
                this['$' + (idx++)] = {};
195
            }
196
        }
197
 
198
 
199
 
200
        /**
201
        * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
202
        * done already
203
        */
204
        if (!this.canvas.__rgraph_aa_translated__) {
205
            this.context.translate(0.5,0.5);
206
 
207
            this.canvas.__rgraph_aa_translated__ = true;
208
        }
209
 
210
 
211
 
212
 
213
        ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
214
 
215
 
216
 
217
 
218
        var RG   = RGraph;
219
        var ca   = this.canvas;
220
        var co   = ca.getContext('2d');
221
        var prop = this.properties;
222
        //var $jq  = jQuery;
223
 
224
 
225
 
226
 
227
        //////////////////////////////////// METHODS ///////////////////////////////////////
228
 
229
 
230
 
231
 
232
        /**
233
        * A simple setter
234
        *
235
        * @param string name  The name of the property to set
236
        * @param string value The value of the property
237
        */
238
        this.Set = function (name, value)
239
        {
240
            name = name.toLowerCase();
241
 
242
            /**
243
            * This should be done first - prepend the propertyy name with "chart." if necessary
244
            */
245
            if (name.substr(0,6) != 'chart.') {
246
                name = 'chart.' + name;
247
            }
248
 
249
            if (name == 'chart.text.diameter') {
250
                name = 'chart.text.size';
251
            }
252
 
253
            /**
254
            * If the name is chart.color, set chart.colors too
255
            */
256
            if (name == 'chart.color') {
257
                this.properties['chart.colors'] = [value];
258
            }
259
 
260
            prop[name] = value;
261
 
262
            return this;
263
        }
264
 
265
 
266
 
267
 
268
 
269
        /**
270
        * A simple hetter
271
        *
272
        * @param string name  The name of the property to get
273
        */
274
        this.Get = function (name)
275
        {
276
            /**
277
            * This should be done first - prepend the property name with "chart." if necessary
278
            */
279
            if (name.substr(0,6) != 'chart.') {
280
                name = 'chart.' + name;
281
            }
282
 
283
            if (name == 'chart.text.diameter') {
284
                name = 'chart.text.size';
285
            }
286
 
287
            return prop[name];
288
        }
289
 
290
 
291
 
292
 
293
        /**
294
        * The draw method which does all the brunt of the work
295
        */
296
        this.Draw = function ()
297
        {
298
            /**
299
            * Fire the onbeforedraw event
300
            */
301
            RG.FireCustomEvent(this, 'onbeforedraw');
302
 
303
            // NB: Colors are parsed further down
304
 
305
            // Reset the coords array to stop it growing
306
            this.coords  = [];
307
            this.coords2 = [];
308
 
309
            /**
310
            * Reset the data to the original_data
311
            */
312
            this.data = RG.array_clone(this.original_data);
313
 
314
            // Loop thru the data array if chart.accumulative is enable checking to see if all the
315
            // datasets have the same number of elements.
316
            if (prop['chart.accumulative']) {
317
                for (var i=0; i<this.data.length; ++i) {
318
                    if (this.data[i].length != this.data[0].length) {
319
                        alert('[RADAR] Error! When the radar has chart.accumulative set to true all the datasets must have the same number of elements');
320
                    }
321
                }
322
            }
323
 
324
 
325
            /**
326
            * This defaults to true, but needs to be an array with a size matching the number of
327
            * labels.
328
            */
329
            if (RG.is_null(prop['chart.labels.axes.boxed'])) {
330
                prop['chart.labels.axes.boxed'] = [];
331
                for (var i=0; i<(prop['chart.labels.specific'].length || prop['chart.labels.count'] || 5); ++i) {
332
                    prop['chart.labels.axes.boxed'][i] = true;
333
                }
334
            }
335
 
336
 
337
 
338
 
339
            /**
340
            * This is new in May 2011 and facilitates indiviual gutter settings,
341
            * eg chart.gutter.left
342
            */
343
            this.gutterLeft   = prop['chart.gutter.left'];
344
            this.gutterRight  = prop['chart.gutter.right'];
345
            this.gutterTop    = prop['chart.gutter.top'];
346
            this.gutterBottom = prop['chart.gutter.bottom'];
347
 
348
            this.centerx  = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
349
            this.centery  = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
350
            this.radius   = Math.min(ca.width - this.gutterLeft - this.gutterRight, ca.height - this.gutterTop - this.gutterBottom) / 2;
351
 
352
 
353
 
354
            /**
355
            * Allow these to be set by hand
356
            */
357
            if (typeof(prop['chart.centerx']) == 'number') this.centerx = 2 * prop['chart.centerx'];
358
            if (typeof(prop['chart.centery']) == 'number') this.centery = 2 * prop['chart.centery'];
359
            if (typeof(prop['chart.radius']) == 'number') this.radius   = prop['chart.radius'];
360
 
361
 
362
            /**
363
            * Parse the colors for gradients. Its down here so that the center X/Y can be used
364
            */
365
            if (!this.colorsParsed) {
366
 
367
                this.parseColors();
368
 
369
                // Don't want to do this again
370
                this.colorsParsed = true;
371
            }
372
 
373
 
374
 
375
            // Work out the maximum value and the sum
376
            if (!prop['chart.ymax']) {
377
 
378
                // this.max is calculated in the constructor
379
 
380
                // Work out this.max again if the chart is (now) set to be accumulative
381
                if (prop['chart.accumulative']) {
382
 
383
                    var accumulation = [];
384
                    var len = this.original_data[0].length
385
 
386
                    for (var i=1; i<this.original_data.length; ++i) {
387
                        if (this.original_data[i].length != len) {
388
                            alert('[RADAR] Error! Stacked Radar chart datasets must all be the same size!');
389
                        }
390
 
391
                        for (var j=0; j<this.original_data[i].length; ++j) {
392
                            this.data[i][j] += this.data[i - 1][j];
393
                            this.max = Math.max(this.max, this.data[i][j]);
394
                        }
395
                    }
396
                }
397
 
398
 
399
                this.scale2 = RG.getScale2(this, {'max':typeof(prop['chart.ymax']) == 'number' ? prop['chart.ymax'] : this.max,
400
                                                  'min':0,
401
                                                  'scale.decimals':Number(prop['chart.scale.decimals']),
402
                                                  'scale.point':prop['chart.scale.point'],
403
                                                  'scale.thousand':prop['chart.scale.thousand'],
404
                                                  'scale.round':prop['chart.scale.round'],
405
                                                  'units.pre':prop['chart.units.pre'],
406
                                                  'units.post':prop['chart.units.post'],
407
                                                  'ylabels.count':prop['chart.labels.count']
408
                                                 });
409
                this.max = this.scale2.max;
410
 
411
            } else {
412
                var ymax = prop['chart.ymax'];
413
 
414
                this.scale2 = RG.getScale2(this, {'max':ymax,
415
                                                  'min':0,
416
                                                  'strict':true,
417
                                                  'scale.decimals':Number(prop['chart.scale.decimals']),
418
                                                  'scale.point':prop['chart.scale.point'],
419
                                                  'scale.thousand':prop['chart.scale.thousand'],
420
                                                  'scale.round':prop['chart.scale.round'],
421
                                                  'units.pre':prop['chart.units.pre'],
422
                                                  'units.post':prop['chart.units.post'],
423
                                                  'ylabels.count':prop['chart.labels.count']
424
                                                 });
425
                this.max = this.scale2.max;
426
            }
427
 
428
            this.DrawBackground();
429
            this.DrawAxes();
430
            this.DrawCircle();
431
            this.DrawLabels();
432
            this.DrawAxisLabels();
433
 
434
            /**
435
            * Allow clipping
436
            */
437
            co.save();
438
                co.beginPath();
439
                    co.arc(this.centerx, this.centery, this.radius * 2, -HALFPI, (TWOPI * prop['chart.animation.trace.clip']) - HALFPI, false);
440
                    co.lineTo(this.centerx, this.centery);
441
                co.closePath();
442
                co.clip();
443
 
444
                this.DrawChart();
445
                this.DrawHighlights();
446
 
447
            co.restore();
448
 
449
            // Draw the title
450
            if (prop['chart.title']) {
451
                RG.DrawTitle(this, prop['chart.title'], this.gutterTop, null, prop['chart.title.diameter'] ? prop['chart.title.diameter'] : null)
452
            }
453
 
454
            // Draw the key if necessary
455
            // obj, key, colors
456
            if (prop['chart.key']) {
457
                RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
458
            }
459
 
460
            /**
461
            * Show the context menu
462
            */
463
            if (prop['chart.contextmenu']) {
464
                RG.ShowContext(this);
465
            }
466
 
467
 
468
            /**
469
            * This function enables resizing
470
            */
471
            if (prop['chart.resizable']) {
472
                RG.AllowResizing(this);
473
            }
474
 
475
 
476
            /**
477
            * This installs the event listeners
478
            */
479
            RG.InstallEventListeners(this);
480
 
481
            /**
482
            * This installs the Radar chart specific area listener
483
            */
484
            if ( (prop['chart.fill.click'] || prop['chart.fill.mousemove'] || !RG.is_null(prop['chart.fill.tooltips'])) && !this.__fill_click_listeners_installed__) {
485
                this.AddFillListeners();
486
                this.__fill_click_listeners_installed__ = true;
487
            }
488
 
489
            /**
490
            * Fire the RGraph ondraw event
491
            */
492
            RGraph.FireCustomEvent(this, 'ondraw');
493
 
494
            return this;
495
        }
496
 
497
 
498
 
499
 
500
        /**
501
        * Draws the background circles
502
        */
503
        this.DrawBackground = function ()
504
        {
505
            var color   = prop['chart.background.circles.color'];
506
            var poly    = prop['chart.background.circles.poly'];
507
            var spacing = prop['chart.background.circles.spacing'];
508
 
509
 
510
 
511
 
512
 
513
            // Set the linewidth for the grid (so that repeated redrawing works OK)
514
            co.lineWidth = 1;
515
 
516
 
517
 
518
 
519
            /**
520
            * Draws the background circles
521
            */
522
            if (prop['chart.background.circles'] && poly == false) {
523
 
524
 
525
 
526
 
527
 
528
                // Draw the concentric circles
529
                co.strokeStyle = color;
530
               co.beginPath();
531
 
532
                    var numrings = typeof(prop['chart.background.circles.count']) == 'number' ? prop['chart.background.circles.count'] : prop['chart.labels.count'];
533
 
534
                    // TODO Currently set to 5 - needs changing
535
                   for (var r=0; r<=this.radius; r+=(this.radius / numrings)) {
536
                        co.moveTo(this.centerx, this.centery);
537
                        co.arc(this.centerx, this.centery,r, 0, TWOPI, false);
538
                    }
539
                co.stroke();
540
 
541
 
542
 
543
 
544
 
545
                /**
546
                * Draw the diagonals/spokes
547
                */
548
                co.strokeStyle = color;
549
 
550
                for (var i=0; i<360; i+=15) {
551
                    co.beginPath();
552
                        co.arc(this.centerx,
553
                               this.centery,
554
                               this.radius,
555
                               (i / 360) * TWOPI,
556
                               ((i+0.001) / 360) * TWOPI,
557
                               false); // The 0.01 avoids a bug in Chrome 6
558
                        co.lineTo(this.centerx, this.centery);
559
                    co.stroke();
560
                }
561
 
562
 
563
 
564
 
565
 
566
 
567
            /**
568
            * The background"circles" are actually drawn as a poly based on how many points there are
569
            * (ie hexagons if there are 6 points, squares if the are four etc)
570
            */
571
            } else if (prop['chart.background.circles'] && poly == true) {
572
 
573
                /**
574
                * Draw the diagonals/spokes
575
                */
576
                co.strokeStyle = color;
577
                var increment = 360 / this.data[0].length
578
 
579
                for (var i=0; i<360; i+=increment) {
580
                    co.beginPath();
581
                        co.arc(this.centerx,
582
                               this.centery,
583
                               this.radius,
584
                               ((i / 360) * TWOPI) - HALFPI,
585
                               (((i + 0.001) / 360) * TWOPI) - HALFPI,
586
                               false); // The 0.001 avoids a bug in Chrome 6
587
                        co.lineTo(this.centerx, this.centery);
588
                    co.stroke();
589
                }
590
 
591
 
592
                /**
593
                * Draw the lines that go around the Radar chart
594
                */
595
                co.strokeStyle = color;
596
 
597
                    var numrings = typeof(prop['chart.background.circles.count']) == 'number' ? prop['chart.background.circles.count'] : prop['chart.labels.count'];
598
 
599
                    for (var r=0; r<=this.radius; r+=(this.radius / numrings)) {
600
                        co.beginPath();
601
                            for (var a=0; a<=360; a+=(360 / this.data[0].length)) {
602
                                co.arc(this.centerx,
603
                                       this.centery,
604
                                       r,
605
                                       RG.degrees2Radians(a) - HALFPI,
606
                                       RG.degrees2Radians(a) + 0.001 - HALFPI,
607
                                       false);
608
                            }
609
                        co.closePath();
610
                        co.stroke();
611
                    }
612
            }
613
        }
614
 
615
 
616
 
617
 
618
        /**
619
        * Draws the axes
620
        */
621
        this.DrawAxes = function ()
622
        {
623
            co.strokeStyle = prop['chart.axes.color'];
624
 
625
            var halfsize = this.radius;
626
 
627
            co.beginPath();
628
                /**
629
                * The Y axis
630
                */
631
                co.moveTo(Math.round(this.centerx), this.centery + this.radius);
632
                co.lineTo(Math.round(this.centerx), this.centery - this.radius);
633
 
634
 
635
                // Draw the bits at either end of the Y axis
636
                co.moveTo(this.centerx - 5, Math.round(this.centery + this.radius));
637
                co.lineTo(this.centerx + 5, Math.round(this.centery + this.radius));
638
                co.moveTo(this.centerx - 5, Math.round(this.centery - this.radius));
639
                co.lineTo(this.centerx + 5, Math.round(this.centery - this.radius));
640
 
641
                // Draw Y axis tick marks
642
                for (var y=(this.centery - this.radius); y<(this.centery + this.radius); y+=(this.radius/prop['chart.numyticks'])) {
643
                    co.moveTo(this.centerx - 3, Math.round(y));
644
                    co.lineTo(this.centerx + 3, Math.round(y));
645
                }
646
 
647
                /**
648
                * The X axis
649
                */
650
                co.moveTo(this.centerx - this.radius, Math.round(this.centery));
651
                co.lineTo(this.centerx + this.radius, Math.round(this.centery));
652
 
653
                // Draw the bits at the end of the X axis
654
                co.moveTo(Math.round(this.centerx - this.radius), this.centery - 5);
655
                co.lineTo(Math.round(this.centerx - this.radius), this.centery + 5);
656
                co.moveTo(Math.round(this.centerx + this.radius), this.centery - 5);
657
                co.lineTo(Math.round(this.centerx + this.radius), this.centery + 5);
658
 
659
                // Draw X axis tick marks
660
                for (var x=(this.centerx - this.radius); x<(this.centerx + this.radius); x+=(this.radius/prop['chart.numxticks'])) {
661
                    co.moveTo(Math.round(x), this.centery - 3);
662
                    co.lineTo(Math.round(x), this.centery + 3);
663
                }
664
 
665
            // Stroke it
666
            co.stroke();
667
        }
668
 
669
 
670
 
671
 
672
        /**
673
        * The function which actually draws the radar chart
674
        */
675
        this.DrawChart = function ()
676
        {
677
            var alpha = prop['chart.colors.alpha'];
678
 
679
            if (typeof(alpha) == 'number') {
680
                var oldAlpha = co.globalAlpha;
681
                co.globalAlpha = alpha;
682
            }
683
 
684
            var numDatasets = this.data.length;
685
 
686
            for (var dataset=0; dataset<this.data.length; ++dataset) {
687
 
688
                co.beginPath();
689
 
690
                    var coords_dataset = [];
691
 
692
                    for (var i=0; i<this.data[dataset].length; ++i) {
693
 
694
                        var coords = this.GetCoordinates(dataset, i);
695
 
696
                        if (coords_dataset == null) {
697
                            coords_dataset = [];
698
                        }
699
 
700
                        coords_dataset.push(coords);
701
                        this.coords.push(coords);
702
                    }
703
 
704
                    this.coords2[dataset] = coords_dataset;
705
 
706
 
707
                    /**
708
                    * Now go through the coords and draw the chart itself
709
                    *
710
                    * 18/5/2012 - chart.strokestyle can now be an array of colors as well as a single color
711
                    */
712
 
713
                    co.strokeStyle = (typeof(prop['chart.strokestyle']) == 'object' && prop['chart.strokestyle'][dataset]) ? prop['chart.strokestyle'][dataset] : prop['chart.strokestyle'];
714
                    co.fillStyle   = prop['chart.colors'][dataset];
715
                    co.lineWidth   = prop['chart.linewidth'];
716
 
717
                    for (i=0; i<coords_dataset.length; ++i) {
718
                        if (i == 0) {
719
                            co.moveTo(coords_dataset[i][0], coords_dataset[i][1]);
720
                        } else {
721
                            co.lineTo(coords_dataset[i][0], coords_dataset[i][1]);
722
                        }
723
                    }
724
 
725
 
726
                    // If on the second or greater dataset, backtrack
727
                    if (prop['chart.accumulative'] && dataset > 0) {
728
 
729
                        // This goes back to the start coords of this particular dataset
730
                        co.lineTo(coords_dataset[0][0], coords_dataset[0][1]);
731
 
732
                        //Now move down to the end point of the previous dataset
733
                        co.moveTo(last_coords[0][0], last_coords[0][1]);
734
 
735
                        for (var i=coords_dataset.length - 1; i>=0; --i) {
736
                            co.lineTo(last_coords[i][0], last_coords[i][1]);
737
                        }
738
                    }
739
 
740
                // This is used by the next iteration of the loop
741
                var last_coords = coords_dataset;
742
 
743
                co.closePath();
744
 
745
                co.stroke();
746
                co.fill();
747
            }
748
 
749
            // Reset the globalAlpha
750
            if (typeof(alpha) == 'number') {
751
                co.globalAlpha = oldAlpha;
752
            }
753
        }
754
 
755
 
756
 
757
 
758
        /**
759
        * Gets the coordinates for a particular mark
760
        *
761
        * @param  number i The index of the data (ie which one it is)
762
        * @return array    A two element array of the coordinates
763
        */
764
        this.GetCoordinates = function (dataset, index)
765
        {
766
            // The number  of data points
767
            var len = this.data[dataset].length;
768
 
769
            // The magnitude of the data (NOT the x/y coords)
770
            var mag = (this.data[dataset][index] / this.max) * this.radius;
771
 
772
            /**
773
            * Get the angle
774
            */
775
            var angle = (TWOPI / len) * index; // In radians
776
angle -= HALFPI;
777
 
778
 
779
            /**
780
            * Work out the X/Y coordinates
781
            */
782
            var x = Math.cos(angle) * mag;
783
            var y = Math.sin(angle) * mag;
784
 
785
            /**
786
            * Put the coordinate in the right quadrant
787
            */
788
            x = this.centerx + x;
789
            y = this.centery + y;
790
 
791
            return [x,y];
792
        }
793
 
794
 
795
 
796
 
797
        /**
798
        * This function adds the labels to the chart
799
        */
800
        this.DrawLabels = function ()
801
        {
802
            var labels = prop['chart.labels'];
803
 
804
            if (labels && labels.length > 0) {
805
 
806
                co.lineWidth = 1;
807
                co.strokeStyle = 'gray';
808
                co.fillStyle = prop['chart.text.color'];
809
 
810
                var bgFill  = prop['chart.labels.background.fill'];
811
                var bold    = prop['chart.labels.bold'];
812
                var bgBoxed = prop['chart.labels.boxed'];
813
                var offset  = prop['chart.labels.offset'];
814
                var font    = prop['chart.text.font'];
815
                var size    = prop['chart.text.size'];
816
                var radius  = this.radius;
817
 
818
                for (var i=0; i<labels.length; ++i) {
819
 
820
                    var angle  = (TWOPI / prop['chart.labels'].length) * i;
821
                        angle -= HALFPI;
822
 
823
                    var x = this.centerx + (Math.cos(angle) * (radius + offset));
824
                    var y = this.centery + (Math.sin(angle) * (radius + offset));
825
 
826
                    /**
827
                    * Horizontal alignment
828
                    */
829
                    var halign = x < this.centerx ? 'right' : 'left' ;
830
                    if (i == 0 || (i / labels.length) == 0.5) halign = 'center';
831
 
832
                    if (labels[i] && labels[i].length) {
833
                        RG.Text2(this, {'font':font,
834
                                        'size':size,
835
                                        'x':x,
836
                                        'y':y,
837
                                        'text':labels[i],
838
                                        'valign':'center',
839
                                        'halign':halign,
840
                                        'bounding':bgBoxed,
841
                                        'boundingFill':bgFill,
842
                                        'bold':bold,
843
                                        'tag': 'labels'
844
                                       });
845
                    }
846
                }
847
            }
848
        }
849
 
850
 
851
 
852
 
853
        /**
854
        * Draws the circle. No arguments as it gets the information from the object properties.
855
        */
856
        this.DrawCircle = function ()
857
        {
858
            var circle     = {};
859
            circle.limit   = prop['chart.circle'];
860
            circle.fill    = prop['chart.circle.fill'];
861
            circle.stroke  = prop['chart.circle.stroke'];
862
 
863
            if (circle.limit) {
864
 
865
                var r = (circle.limit / this.max) * this.radius;
866
 
867
                co.fillStyle = circle.fill;
868
                co.strokeStyle = circle.stroke;
869
 
870
                co.beginPath();
871
                co.arc(this.centerx, this.centery, r, 0, TWOPI, 0);
872
                co.fill();
873
                co.stroke();
874
            }
875
        }
876
 
877
 
878
 
879
 
880
        /**
881
        * Unsuprisingly, draws the labels
882
        */
883
        this.DrawAxisLabels = function ()
884
        {
885
            /**
886
            * Draw specific axis labels
887
            */
888
            if (RG.is_array(prop['chart.labels.specific']) && prop['chart.labels.specific'].length) {
889
                this.DrawSpecificAxisLabels();
890
                return;
891
            }
892
 
893
            co.lineWidth = 1;
894
 
895
            // Set the color to black
896
            co.fillStyle = 'black';
897
            co.strokeStyle = 'black';
898
 
899
            var r          = this.radius;
900
            var font       = prop['chart.text.font'];
901
            var size       = typeof(prop['chart.text.size.scale']) == 'number' ? prop['chart.text.size.scale'] : prop['chart.text.size'];
902
            var axes       = prop['chart.labels.axes'].toLowerCase();
903
            var color      = 'white';
904
            var drawzero   = false;
905
            var units_pre  = prop['chart.units.pre'];
906
            var units_post = prop['chart.units.post'];
907
            var decimals   = prop['chart.scale.decimals'];
908
            var bold       = prop['chart.labels.axes.bold'];
909
            var boxed      = prop['chart.labels.axes.boxed'];
910
            var centerx    = this.centerx;
911
            var centery    = this.centery;
912
            var scale      = this.scale;
913
 
914
            co.fillStyle = prop['chart.text.color'];
915
 
916
            // The "North" axis labels
917
            if (axes.indexOf('n') > -1) {
918
                for (var i=0; i<this.scale2.labels.length; ++i) {
919
                    RG.Text2(this, {'bold':bold[i],
920
                                    'font':font,
921
                                    'size':size,
922
                                    'x':centerx,
923
                                    'y':centery - (r * ((i+1)/this.scale2.labels.length)),
924
                                    'text':this.scale2.labels[i],
925
                                    'valign':'center',
926
                                    'halign':'center',
927
                                    'bounding':boxed[i],
928
                                    'boundingFill':color,
929
                                    'tag': 'scale'
930
                                   });
931
                }
932
 
933
                drawzero = true;
934
            }
935
 
936
            // The "South" axis labels
937
            if (axes.indexOf('s') > -1) {
938
                for (var i=0; i<this.scale2.labels.length; ++i) {
939
                    RG.Text2(this, {'bold':bold[i],
940
                                    'font':font,
941
                                    'size':size,
942
                                    'x':centerx,
943
                                    'y':centery + (r * ((i+1)/this.scale2.labels.length)),
944
                                    'text':this.scale2.labels[i],
945
                                    'valign':'center',
946
                                    'halign':'center',
947
                                    'bounding':boxed[i],
948
                                    'boundingFill':color,
949
                                    'tag': 'scale'
950
                                   });
951
                }
952
 
953
                drawzero = true;
954
            }
955
 
956
            // The "East" axis labels
957
            if (axes.indexOf('e') > -1) {
958
 
959
                for (var i=0; i<this.scale2.labels.length; ++i) {
960
                    RG.Text2(this, {'bold':bold[i],
961
                                    'font':font,
962
                                    'size':size,
963
                                    'x':centerx + (r * ((i+1)/this.scale2.labels.length)),
964
                                    'y':centery,
965
                                    'text':this.scale2.labels[i],
966
                                    'valign':'center',
967
                                    'halign':'center',
968
                                    'bounding':boxed[i],
969
                                    'boundingFill':color,
970
                                    'tag': 'scale'
971
                                   });
972
                }
973
 
974
                drawzero = true;
975
            }
976
 
977
            // The "West" axis labels
978
            if (axes.indexOf('w') > -1) {
979
 
980
                for (var i=0; i<this.scale2.labels.length; ++i) {
981
                    RG.Text2(this, {'bold':bold[i],
982
                                    'font':font,
983
                                    'size':size,
984
                                    'x':centerx - (r * ((i+1)/this.scale2.labels.length)),
985
                                    'y':centery,
986
                                    'text':this.scale2.labels[i],
987
                                    'valign':'center',
988
                                    'halign':'center',
989
                                    'bounding':boxed[i],
990
                                    'boundingFill':color,
991
                                    'tag': 'scale'
992
                                   });
993
                }
994
 
995
                drawzero = true;
996
            }
997
 
998
            if (drawzero) {
999
                RG.Text2(this, {'font':font,
1000
                                'size':size,
1001
                                'x':centerx,
1002
                                'y':centery,
1003
                                'text':RG.number_format(this, Number(0).toFixed(decimals), units_pre, units_post),
1004
                                'valign':'center',
1005
                                'halign':'center',
1006
                                'bounding':prop['chart.labels.axes.boxed.zero'],
1007
                                'boundingFill':color,
1008
                                'bold':prop['chart.labels.axes.bold.zero'],
1009
                                'tag': 'scale'
1010
                               });
1011
            }
1012
        }
1013
 
1014
 
1015
 
1016
 
1017
        /**
1018
        * Draws specific axis labels
1019
        */
1020
        this.DrawSpecificAxisLabels = function ()
1021
        {
1022
            /**
1023
            * Specific axis labels
1024
            */
1025
            var labels          = prop['chart.labels.specific'];
1026
            var bold            = RG.array_pad(prop['chart.labels.axes.bold'],labels.length);
1027
            var boxed           = RG.array_pad(prop['chart.labels.axes.boxed'],labels.length);
1028
            var reversed_labels = RG.array_reverse(labels);
1029
            var reversed_bold   = RG.array_reverse(bold);
1030
            var reversed_boxed  = RG.array_reverse(boxed);
1031
            var font            = prop['chart.text.font'];
1032
            var size            = typeof(prop['chart.text.size.scale']) == 'number' ? prop['chart.text.size.scale'] : prop['chart.text.size'];
1033
            var axes            = prop['chart.labels.axes'].toLowerCase();
1034
 
1035
            co.fillStyle = prop['chart.text.color'];
1036
 
1037
            for (var i=0; i<labels.length; ++i) {
1038
 
1039
                if (axes.indexOf('n') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':reversed_bold[i],'font':font,'size':size,'x':this.centerx,'y':this.centery - this.radius + ((this.radius / labels.length) * i),'text':reversed_labels[i],'valign':'center','halign':'center','bounding':reversed_boxed[i],'boundingFill':'white'});
1040
                if (axes.indexOf('s') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':bold[i],'font':font,'size':size,'x':this.centerx,'y':this.centery + ((this.radius / labels.length) * (i+1)),'text':labels[i],'valign':'center','halign':'center','bounding':boxed[i],'boundingFill':'white'});
1041
 
1042
                if (axes.indexOf('w') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':reversed_bold[i],'font':font,'size':size,'x':this.centerx - this.radius + ((this.radius / labels.length) * i),'y':this.centery,'text':reversed_labels[i],'valign':'center','halign':'center','bounding':reversed_boxed[i],'boundingFill':'white'});
1043
                if (axes.indexOf('e') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':bold[i],'font':font,'size':size,'x':this.centerx + ((this.radius / labels.length) * (i+1)),'y':this.centery,'text':labels[i],'valign':'center','halign':'center','bounding':boxed[i],'boundingFill':'white'});
1044
            }
1045
        }
1046
 
1047
 
1048
 
1049
 
1050
        /**
1051
        * This method eases getting the focussed point (if any)
1052
        *
1053
        * @param event e The event object
1054
        */
1055
        this.getShape =
1056
        this.getPoint = function (e)
1057
        {
1058
            for (var i=0; i<this.coords.length; ++i) {
1059
 
1060
                var x        = this.coords[i][0];
1061
                var y        = this.coords[i][1];
1062
                var tooltips = prop['chart.tooltips'];
1063
                var index    = Number(i);
1064
                var mouseXY  = RG.getMouseXY(e);
1065
                var mouseX   = mouseXY[0];
1066
                var mouseY   = mouseXY[1];
1067
 
1068
                if (   mouseX < (x + 5)
1069
                    && mouseX > (x - 5)
1070
                    && mouseY > (y - 5)
1071
                    && mouseY < (y + 5)
1072
                   ) {
1073
 
1074
                    var tooltip = RG.parseTooltipText(prop['chart.tooltips'], index);
1075
 
1076
                    return {0: this,    'object':  this,
1077
                            1: x,       'x':       x,
1078
                            2: y,       'y':       y,
1079
                            3: null, 'dataset': null,
1080
                            4: index,       'index':   i,
1081
                                        'tooltip': tooltip
1082
                           }
1083
                }
1084
            }
1085
        }
1086
 
1087
 
1088
 
1089
 
1090
        /**
1091
        * Each object type has its own Highlight() function which highlights the appropriate shape
1092
        *
1093
        * @param object shape The shape to highlight
1094
        */
1095
        this.Highlight = function (shape)
1096
        {
1097
            // Add the new highlight
1098
            RG.Highlight.Point(this, shape);
1099
        }
1100
 
1101
 
1102
 
1103
 
1104
        /**
1105
        * The getObjectByXY() worker method. Don't call this call:
1106
        *
1107
        * RGraph.ObjectRegistry.getObjectByXY(e)
1108
        *
1109
        * @param object e The event object
1110
        */
1111
        this.getObjectByXY = function (e)
1112
        {
1113
            var mouseXY = RG.getMouseXY(e);
1114
 
1115
            if (
1116
                   mouseXY[0] > (this.centerx - this.radius)
1117
                && mouseXY[0] < (this.centerx + this.radius)
1118
                && mouseXY[1] > (this.centery - this.radius)
1119
                && mouseXY[1] < (this.centery + this.radius)
1120
                ) {
1121
 
1122
                return this;
1123
            }
1124
        }
1125
 
1126
 
1127
 
1128
 
1129
        /**
1130
        * This function positions a tooltip when it is displayed
1131
        *
1132
        * @param obj object    The chart object
1133
        * @param int x         The X coordinate specified for the tooltip
1134
        * @param int y         The Y coordinate specified for the tooltip
1135
        * @param objec tooltip The tooltips DIV element
1136
        */
1137
        this.positionTooltip = function (obj, x, y, tooltip, idx)
1138
        {
1139
            var dataset    = tooltip.__dataset__;
1140
            var index      = tooltip.__index__;
1141
            var coordX     = this.coords[index][0];
1142
            var coordY     = this.coords[index][1];
1143
            var canvasXY   = RG.getCanvasXY(obj.canvas);
1144
            var gutterLeft = this.gutterLeft;
1145
            var gutterTop  = this.gutterTop;
1146
            var width      = tooltip.offsetWidth;
1147
 
1148
            // Set the top position
1149
            tooltip.style.left = 0;
1150
            tooltip.style.top  = parseInt(tooltip.style.top) - 9 + 'px';
1151
 
1152
            // By default any overflow is hidden
1153
            tooltip.style.overflow = '';
1154
 
1155
            // The arrow
1156
            var img = new Image();
1157
                img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
1158
                img.style.position = 'absolute';
1159
                img.id = '__rgraph_tooltip_pointer__';
1160
                img.style.top = (tooltip.offsetHeight - 2) + 'px';
1161
            tooltip.appendChild(img);
1162
 
1163
            // Reposition the tooltip if at the edges:
1164
 
1165
            // LEFT edge
1166
            if ((canvasXY[0] + coordX - (width / 2)) < 10) {
1167
                tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + 'px';
1168
                img.style.left = ((width * 0.1) - 8.5) + 'px';
1169
 
1170
            // RIGHT edge
1171
            } else if ((canvasXY[0] + coordX + (width / 2)) > document.body.offsetWidth) {
1172
                tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + 'px';
1173
                img.style.left = ((width * 0.9) - 8.5) + 'px';
1174
 
1175
            // Default positioning - CENTERED
1176
            } else {
1177
                tooltip.style.left = (canvasXY[0] + coordX - (width * 0.5)) + 'px';
1178
                img.style.left = ((width * 0.5) - 8.5) + 'px';
1179
            }
1180
        }
1181
 
1182
 
1183
 
1184
 
1185
        /**
1186
        * This draws highlights on the points
1187
        */
1188
        this.DrawHighlights = function ()
1189
        {
1190
            if (prop['chart.highlights']) {
1191
 
1192
                var sequentialIdx = 0;
1193
                var dataset       = 0;
1194
                var index         = 0;
1195
                var radius        = prop['chart.highlights.radius'];
1196
 
1197
 
1198
 
1199
                for (var dataset=0; dataset <this.data.length; ++dataset) {
1200
                    for (var index=0; index<this.data[dataset].length; ++index) {
1201
                        co.beginPath();
1202
                            co.strokeStyle = prop['chart.highlights.stroke'];
1203
                            co.fillStyle = prop['chart.highlights.fill'] ? prop['chart.highlights.fill'] : ((typeof(prop['chart.strokestyle']) == 'object' && prop['chart.strokestyle'][dataset]) ? prop['chart.strokestyle'][dataset] : prop['chart.strokestyle']);
1204
                            co.arc(this.coords[sequentialIdx][0], this.coords[sequentialIdx][1], radius, 0, TWOPI, false);
1205
                        co.stroke();
1206
                        co.fill();
1207
                        ++sequentialIdx;
1208
                    }
1209
                }
1210
 
1211
            }
1212
        }
1213
 
1214
 
1215
 
1216
 
1217
        /**
1218
        * This function returns the radius (ie the distance from the center) for a particular
1219
        * value. Note that if you want the angle for a point you can use getAngle(index)
1220
        *
1221
        * @param number value The value you want the radius for
1222
        */
1223
        this.getRadius = function (value)
1224
        {
1225
            if (value < 0 || value > this.max) {
1226
                return null;
1227
            }
1228
 
1229
            // Radar doesn't support minimum value
1230
            var radius = (value / this.max) * this.radius;
1231
 
1232
            return radius;
1233
        }
1234
 
1235
 
1236
 
1237
 
1238
        /**
1239
        * This function returns the angle (in radians) for a particular index.
1240
        *
1241
        * @param number numitems The total number of items
1242
        * @param number index    The zero index number of the item to get the angle for
1243
        */
1244
        this.getAngle = function (numitems, index)
1245
        {
1246
            var angle = (TWOPI / numitems) * index;
1247
                angle -= HALFPI;
1248
 
1249
            return angle;
1250
        }
1251
 
1252
 
1253
 
1254
 
1255
        /**
1256
        * This allows for easy specification of gradients
1257
        */
1258
        this.parseColors = function ()
1259
        {
1260
            for (var i=0; i<prop['chart.colors'].length; ++i) {
1261
                prop['chart.colors'][i] = this.parseSingleColorForGradient(prop['chart.colors'][i]);
1262
            }
1263
 
1264
            var keyColors = prop['chart.key.colors'];
1265
 
1266
            if (typeof(keyColors) != 'null' && keyColors && keyColors.length) {
1267
                for (var i=0; i<prop['chart.key.colors'].length; ++i) {
1268
                    prop['chart.key.colors'][i] = this.parseSingleColorForGradient(prop['chart.key.colors'][i]);
1269
                }
1270
            }
1271
 
1272
            prop['chart.title.color']      = this.parseSingleColorForGradient(prop['chart.title.color']);
1273
            prop['chart.text.color']       = this.parseSingleColorForGradient(prop['chart.text.color']);
1274
            prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
1275
            prop['chart.highlight.fill']   = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
1276
            prop['chart.circle.fill']      = this.parseSingleColorForGradient(prop['chart.circle.fill']);
1277
            prop['chart.circle.stroke']    = this.parseSingleColorForGradient(prop['chart.circle.stroke']);
1278
        }
1279
 
1280
 
1281
 
1282
 
1283
        /**
1284
        * This parses a single color value
1285
        */
1286
        this.parseSingleColorForGradient = function (color)
1287
        {
1288
            if (!color || typeof(color) != 'string') {
1289
                return color;
1290
            }
1291
 
1292
            if (color.match(/^gradient\((.*)\)$/i)) {
1293
 
1294
                var parts = RegExp.$1.split(':');
1295
 
1296
                // Create the gradient
1297
                var grad = co.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
1298
 
1299
                var diff = 1 / (parts.length - 1);
1300
 
1301
                grad.addColorStop(0, RG.trim(parts[0]));
1302
 
1303
                for (var j=1; j<parts.length; ++j) {
1304
                    grad.addColorStop(j * diff, RG.trim(parts[j]));
1305
                }
1306
            }
1307
 
1308
            return grad ? grad : color;
1309
        }
1310
 
1311
 
1312
 
1313
 
1314
        this.AddFillListeners = function (e)
1315
        {
1316
            var obj = this;
1317
 
1318
            var func = function (e)
1319
            {
1320
                //var canvas  = e.target;
1321
                //var context = canvas.getContext('2d');
1322
                var coords  = this.coords;
1323
                var coords2 = this.coords2;
1324
                var mouseXY = RG.getMouseXY(e);
1325
                var dataset = 0;
1326
 
1327
                if (e.type == 'mousemove' && prop['chart.fill.mousemove.redraw']) {
1328
                    RG.RedrawCanvas(ca);
1329
                }
1330
 
1331
                for (var dataset=(obj.coords2.length-1); dataset>=0; --dataset) {
1332
 
1333
                    // Draw the path again so that it can be checked
1334
                    co.beginPath();
1335
                        co.moveTo(obj.coords2[dataset][0][0], obj.coords2[dataset][0][1]);
1336
                        for (var j=0; j<obj.coords2[dataset].length; ++j) {
1337
                            co.lineTo(obj.coords2[dataset][j][0], obj.coords2[dataset][j][1]);
1338
                        }
1339
 
1340
                    // Draw a line back to the starting point
1341
                    co.lineTo(obj.coords2[dataset][0][0], obj.coords2[dataset][0][1]);
1342
 
1343
                    // Go thru the previous datasets coords in reverse order
1344
                    if (prop['chart.accumulative'] && dataset > 0) {
1345
                        co.lineTo(obj.coords2[dataset - 1][0][0], obj.coords2[dataset - 1][0][1]);
1346
                        for (var j=(obj.coords2[dataset - 1].length - 1); j>=0; --j) {
1347
                            co.lineTo(obj.coords2[dataset - 1][j][0], obj.coords2[dataset - 1][j][1]);
1348
                        }
1349
                    }
1350
 
1351
                    co.closePath();
1352
 
1353
                    if (co.isPointInPath(mouseXY[0], mouseXY[1])) {
1354
                        var inPath = true;
1355
                        break;
1356
                    }
1357
                }
1358
 
1359
                // Call the events
1360
                if (inPath) {
1361
 
1362
                    var fillTooltips = prop['chart.fill.tooltips'];
1363
 
1364
                    /**
1365
                    * Click event
1366
                    */
1367
                    if (e.type == 'click') {
1368
                        if (prop['chart.fill.click']) {
1369
                            prop['chart.fill.click'](e, dataset);
1370
                        }
1371
 
1372
                        if (prop['chart.fill.tooltips'] && prop['chart.fill.tooltips'][dataset]) {
1373
                            obj.DatasetTooltip(e, dataset);
1374
                        }
1375
                    }
1376
 
1377
 
1378
 
1379
                    /**
1380
                    * Mousemove event
1381
                    */
1382
                    if (e.type == 'mousemove') {
1383
 
1384
                        if (prop['chart.fill.mousemove']) {
1385
                            prop['chart.fill.mousemove'](e, dataset);
1386
                        }
1387
 
1388
                        if (!RG.is_null(fillTooltips)) {
1389
                            e.target.style.cursor = 'pointer';
1390
                        }
1391
 
1392
                        if (prop['chart.fill.tooltips'] && prop['chart.fill.tooltips'][dataset]) {
1393
                            e.target.style.cursor = 'pointer';
1394
                        }
1395
                    }
1396
 
1397
                    e.stopPropagation();
1398
 
1399
                } else if (e.type == 'mousemove') {
1400
                    ca.style.cursor = 'default';
1401
                }
1402
            }
1403
 
1404
            /**
1405
            * Add the click listener
1406
            */
1407
            if (prop['chart.fill.click'] || !RG.is_null(prop['chart.fill.tooltips'])) {
1408
                ca.addEventListener('click', func, false);
1409
            }
1410
 
1411
            /**
1412
            * Add the mousemove listener
1413
            */
1414
            if (prop['chart.fill.mousemove'] || !RG.is_null(prop['chart.fill.tooltips'])) {
1415
                ca.addEventListener('mousemove', func, false);
1416
            }
1417
        }
1418
 
1419
 
1420
 
1421
 
1422
        /**
1423
        * This highlights a specific dataset on the chart
1424
        *
1425
        * @param number dataset The index of the dataset (which starts at zero)
1426
        */
1427
        this.HighlightDataset = function (dataset)
1428
        {
1429
            co.beginPath();
1430
            for (var j=0; j<this.coords2[dataset].length; ++j) {
1431
                if (j == 0) {
1432
                    co.moveTo(this.coords2[dataset][0][0], this.coords2[dataset][0][1]);
1433
                } else {
1434
                    co.lineTo(this.coords2[dataset][j][0], this.coords2[dataset][j][1]);
1435
                }
1436
            }
1437
 
1438
            co.lineTo(this.coords2[dataset][0][0], this.coords2[dataset][0][1]);
1439
 
1440
            if (prop['chart.accumulative'] && dataset > 0) {
1441
                co.lineTo(this.coords2[dataset - 1][0][0], this.coords2[dataset - 1][0][1]);
1442
                for (var j=(this.coords2[dataset - 1].length - 1); j>=0; --j) {
1443
                    co.lineTo(this.coords2[dataset - 1][j][0], this.coords2[dataset - 1][j][1]);
1444
                }
1445
            }
1446
 
1447
            co.strokeStyle = prop['chart.fill.highlight.stroke'];
1448
            co.fillStyle   = prop['chart.fill.highlight.fill'];
1449
 
1450
            co.stroke();
1451
            co.fill();
1452
        }
1453
 
1454
 
1455
 
1456
 
1457
        /**
1458
        * Shows a tooltip for a dataset (a "fill" tooltip), You can pecify these
1459
        * with chart.fill.tooltips
1460
        */
1461
        this.DatasetTooltip = function (e, dataset)
1462
        {
1463
            // Highlight the dataset
1464
            this.HighlightDataset(dataset);
1465
 
1466
            // Use the First datapoints coords for the Y position of the tooltip NOTE The X position is changed in the
1467
            // obj.positionTooltip() method so set the index to be the first one
1468
            var text = prop['chart.fill.tooltips'][dataset];
1469
            var x    = 0;
1470
            var y    = this.coords2[dataset][0][1] + RG.getCanvasXY(ca)[1];
1471
 
1472
 
1473
            // Show a tooltip
1474
            RG.Tooltip(this, text, x, y, 0, e);
1475
        }
1476
 
1477
 
1478
 
1479
 
1480
        /**
1481
        * This function handles highlighting an entire data-series for the interactive
1482
        * key
1483
        *
1484
        * @param int index The index of the data series to be highlighted
1485
        */
1486
        this.interactiveKeyHighlight = function (index)
1487
        {
1488
            var coords = this.coords2[index];
1489
 
1490
            if (coords) {
1491
 
1492
                var pre_linewidth = co.lineWidth;
1493
                var pre_linecap   = co.lineCap;
1494
 
1495
 
1496
 
1497
 
1498
                // ------------------------------------------ //
1499
 
1500
                co.lineWidth   = prop['chart.linewidth'] + 10;
1501
                co.lineCap     = 'round';
1502
                co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
1503
 
1504
 
1505
                co.beginPath();
1506
                for (var i=0,len=coords.length; i<len; i+=1) {
1507
                    if (i == 0) {
1508
                        co.moveTo(coords[i][0], coords[i][1]);
1509
                    } else {
1510
                        co.lineTo(coords[i][0], coords[i][1]);
1511
                    }
1512
                }
1513
                co.closePath();
1514
                co.stroke();
1515
 
1516
                // ------------------------------------------ //
1517
 
1518
 
1519
 
1520
 
1521
                // Reset the lineCap and lineWidth
1522
                co.lineWidth = pre_linewidth;
1523
                co.lineCap = pre_linecap;
1524
            }
1525
        }
1526
 
1527
 
1528
 
1529
 
1530
        /**
1531
        * Always register the object
1532
        */
1533
        RG.Register(this);
1534
    }