rgraph-rails 4.62 → 4.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -4
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/vendor/assets/javascripts/RGraph.bar.js +240 -3742
  5. data/vendor/assets/javascripts/RGraph.bipolar.js +165 -2005
  6. data/vendor/assets/javascripts/RGraph.common.annotate.js +35 -395
  7. data/vendor/assets/javascripts/RGraph.common.context.js +30 -595
  8. data/vendor/assets/javascripts/RGraph.common.core.js +418 -5359
  9. data/vendor/assets/javascripts/RGraph.common.csv.js +20 -276
  10. data/vendor/assets/javascripts/RGraph.common.deprecated.js +35 -450
  11. data/vendor/assets/javascripts/RGraph.common.dynamic.js +88 -1395
  12. data/vendor/assets/javascripts/RGraph.common.effects.js +90 -1545
  13. data/vendor/assets/javascripts/RGraph.common.key.js +52 -753
  14. data/vendor/assets/javascripts/RGraph.common.resizing.js +37 -563
  15. data/vendor/assets/javascripts/RGraph.common.sheets.js +29 -352
  16. data/vendor/assets/javascripts/RGraph.common.tooltips.js +32 -450
  17. data/vendor/assets/javascripts/RGraph.common.zoom.js +14 -219
  18. data/vendor/assets/javascripts/RGraph.cornergauge.js +71 -0
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +34 -570
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +33 -544
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +51 -755
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +37 -645
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +36 -633
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +35 -514
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +37 -559
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +33 -548
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +36 -664
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +50 -812
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +51 -856
  30. data/vendor/assets/javascripts/RGraph.fuel.js +58 -964
  31. data/vendor/assets/javascripts/RGraph.funnel.js +55 -984
  32. data/vendor/assets/javascripts/RGraph.gantt.js +77 -1354
  33. data/vendor/assets/javascripts/RGraph.gauge.js +85 -1421
  34. data/vendor/assets/javascripts/RGraph.hbar.js +162 -2788
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +80 -1401
  36. data/vendor/assets/javascripts/RGraph.line.js +249 -4248
  37. data/vendor/assets/javascripts/RGraph.meter.js +74 -1280
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +19 -301
  39. data/vendor/assets/javascripts/RGraph.odo.js +71 -1264
  40. data/vendor/assets/javascripts/RGraph.pie.js +137 -2288
  41. data/vendor/assets/javascripts/RGraph.radar.js +110 -1847
  42. data/vendor/assets/javascripts/RGraph.rose.js +108 -1977
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +80 -1432
  44. data/vendor/assets/javascripts/RGraph.scatter.js +172 -3163
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +60 -1120
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +66 -1735
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +21 -246
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +255 -3937
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +20 -276
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +68 -1303
  51. data/vendor/assets/javascripts/RGraph.svg.common.key.js +19 -205
  52. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +29 -352
  53. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +22 -273
  54. data/vendor/assets/javascripts/RGraph.svg.funnel.js +32 -0
  55. data/vendor/assets/javascripts/RGraph.svg.hbar.js +59 -1400
  56. data/vendor/assets/javascripts/RGraph.svg.line.js +70 -1580
  57. data/vendor/assets/javascripts/RGraph.svg.pie.js +55 -1131
  58. data/vendor/assets/javascripts/RGraph.svg.radar.js +57 -1502
  59. data/vendor/assets/javascripts/RGraph.svg.rose.js +66 -1817
  60. data/vendor/assets/javascripts/RGraph.svg.scatter.js +58 -1261
  61. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +28 -865
  62. data/vendor/assets/javascripts/RGraph.svg.waterfall.js +45 -1252
  63. data/vendor/assets/javascripts/RGraph.thermometer.js +63 -1136
  64. data/vendor/assets/javascripts/RGraph.vprogress.js +83 -1470
  65. data/vendor/assets/javascripts/RGraph.waterfall.js +83 -1347
  66. metadata +5 -4
  67. data/vendor/assets/javascripts/financial-data.js +0 -1067
@@ -1,1503 +1,58 @@
1
- // version: 2017-05-08
2
- /**
3
- * o--------------------------------------------------------------------------------o
4
- * | This file is part of the RGraph package - you can learn more at: |
5
- * | |
6
- * | http://www.rgraph.net |
7
- * | |
8
- * | RGraph is licensed under the Open Source MIT license. That means that it's |
9
- * | totally free to use! |
10
- * o--------------------------------------------------------------------------------o
11
- */
12
1
 
13
- RGraph = window.RGraph || {isRGraph: true};
14
- RGraph.SVG = RGraph.SVG || {};
15
-
16
- // Module pattern
17
- (function (win, doc, undefined)
18
- {
19
- var RG = RGraph,
20
- ua = navigator.userAgent,
21
- ma = Math,
22
- win = window,
23
- doc = document;
24
-
25
-
26
-
27
- RG.SVG.Radar = function (conf)
28
- {
29
- //
30
- // A setter that the constructor uses (at the end)
31
- // to set all of the properties
32
- //
33
- // @param string name The name of the property to set
34
- // @param string value The value to set the property to
35
- //
36
- this.set = function (name, value)
37
- {
38
- if (arguments.length === 1 && typeof name === 'object') {
39
- for (i in arguments[0]) {
40
- if (typeof i === 'string') {
41
-
42
- var ret = RG.SVG.commonSetter({
43
- object: this,
44
- name: i,
45
- value: arguments[0][i]
46
- });
47
-
48
- name = ret.name;
49
- value = ret.value;
50
-
51
- this.set(name, value);
52
- }
53
- }
54
-
55
- } else {
56
-
57
- var ret = RG.SVG.commonSetter({
58
- object: this,
59
- name: name,
60
- value: value
61
- });
62
-
63
- name = ret.name;
64
- value = ret.value;
65
-
66
- this.properties[name] = value;
67
- }
68
-
69
- return this;
70
- };
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
- this.id = conf.id;
80
- this.uid = RG.SVG.createUID();
81
- this.container = document.getElementById(this.id);
82
- this.svg = RG.SVG.createSVG({container: this.container});
83
- this.isRGraph = true;
84
- this.width = Number(this.svg.getAttribute('width'));
85
- this.height = Number(this.svg.getAttribute('height'));
86
- this.data = RG.SVG.arrayClone(conf.data);
87
- this.originalData = RG.SVG.arrayClone(conf.data);
88
- this.type = 'radar';
89
- this.coords = [];
90
- this.coords2 = [];
91
- this.angles = [];
92
- this.angles2 = [];
93
- this.colorsParsed = false;
94
- this.originalColors = {};
95
- this.gradientCounter = 1;
96
- this.nodes = [];
97
- this.shadowNodes = [];
98
- this.max = 0;
99
- this.redraw = false;
100
- this.highlight_node = null;
101
-
102
- // Add this object to the ObjectRegistry
103
- RG.SVG.OR.add(this);
104
-
105
- // Set the DIV container to be inline-block
106
- this.container.style.display = 'inline-block';
107
-
108
-
109
-
110
-
111
-
112
- this.properties =
113
- {
114
- centerx: null,
115
- centery: null,
116
- radius: null,
117
-
118
- gutterLeft: 35,
119
- gutterRight: 35,
120
- gutterTop: 35,
121
- gutterBottom: 35,
122
-
123
- backgroundGrid: true,
124
- backgroundGridColor: '#ddd',
125
- backgroundGridRadialsCount: null,
126
- backgroundGridConcentricsCount: 5,
127
- backgroundGridLinewidth: 1,
128
- backgroundGridPoly: true,
129
-
130
- colors: [
131
- 'red', 'black', 'orange', 'green', '#6ff', '#ccc',
132
- 'pink', 'orange', 'cyan', 'maroon', 'olive', 'teal'
133
- ],
134
- filled: false,
135
- filledOpacity: 0.25,
136
- filledAccumulative: true,
137
-
138
- textColor: 'black',
139
- textFont: 'sans-serif',
140
- textSize: 12,
141
- textBold: false,
142
- textItalic: false,
143
-
144
- labels: [],
145
-
146
- scaleVisible: true,
147
- scaleUnitsPre: '',
148
- scaleUnitsPost: '',
149
- scaleMax: null,
150
- scaleMin: 0,
151
- scalePoint: '.',
152
- scaleThousand: ',',
153
- scaleRound: false,
154
- scaleDecimals: 0,
155
- scaleFormatter: null,
156
- scaleBold: null,
157
- scaleItalic: null,
158
- scaleColor: null,
159
- scaleSize: null,
160
- scaleFont: null,
161
- scaleLabelsCount: 5,
162
-
163
- linewidth: 1,
164
-
165
- tooltips: null,
166
- tooltipsOverride: null,
167
- tooltipsEffect: 'fade',
168
- tooltipsCssClass: 'RGraph_tooltip',
169
- tooltipsEvent: 'mousemove',
170
-
171
- highlightStroke: 'rgba(0,0,0,0)',
172
- highlightFill: 'rgba(255,255,255,0.7)',
173
- highlightLinewidth: 1,
174
-
175
- tickmarks: 'circle',
176
- tickmarksLinewidth: 1,
177
- tickmarksSize: 6,
178
- tickmarksFill: 'white',
179
-
180
- title: '',
181
- titleSize: 16,
182
- titleX: null,
183
- titleY: null,
184
- titleHalign: 'center',
185
- titleValign: null,
186
- titleColor: 'black',
187
- titleFont: null,
188
- titleBold: false,
189
- titleItalic: false,
190
-
191
- titleSubtitle: '',
192
- titleSubtitleSize: 10,
193
- titleSubtitleX: null,
194
- titleSubtitleY: null,
195
- titleSubtitleHalign: 'center',
196
- titleSubtitleValign: null,
197
- titleSubtitleColor: '#aaa',
198
- titleSubtitleFont: null,
199
- titleSubtitleBold: false,
200
- titleSubtitleItalic: false,
201
-
202
- grouping: 'normal', // Can also be stcked
203
-
204
- shadow: false,
205
- shadowOffsetx: 2,
206
- shadowOffsety: 2,
207
- shadowBlur: 2,
208
- shadowOpacity: 0.25,
209
-
210
-
211
-
212
- key: null,
213
- keyColors: null,
214
- keyOffsetx: 0,
215
- keyOffsety: 0,
216
- keyTextOffsetx: 0,
217
- keyTextOffsety: -1,
218
- keyTextSize: null,
219
- keyTextBold: null,
220
- keyTextItalic: null,
221
-
222
- attribution: true,
223
- attributionX: null,
224
- attributionY: null,
225
- attributionHref: null,// Default is set in RGraph.svg.common.core.js
226
- attributionHalign: 'right',
227
- attributionValign: 'bottom',
228
- attributionSize: 7,
229
- attributionColor: 'gray',
230
- attributionFont: 'sans-serif',
231
- attributionItalic: false,
232
- attributionBold: false
233
- };
234
-
235
-
236
-
237
-
238
-
239
-
240
- /**
241
- * "Decorate" the object with the generic effects if the effects library has been included
242
- */
243
- if (RG.SVG.FX && typeof RG.SVG.FX.decorate === 'function') {
244
- RG.SVG.FX.decorate(this);
245
- }
246
-
247
-
248
-
249
-
250
- var prop = this.properties;
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
- //
260
- // The draw method draws the Bar chart
261
- //
262
- this.draw = function ()
263
- {
264
- // Fire the beforedraw event
265
- RG.SVG.fireCustomEvent(this, 'onbeforedraw');
266
-
267
-
268
-
269
- // Reset the data back to the original values
270
- this.data = RG.SVG.arrayClone(this.originalData);
271
-
272
- //
273
- // The datasets have to have the same number of elements
274
- //
275
- if (this.data.length > 1) {
276
-
277
- var len = this.data[0].length;
278
-
279
- for (var i=1; i<this.data.length; ++i) {
280
- if (this.data[i].length !== len) {
281
- alert('[ERROR] The Radar chart datasets must have the same number of elements!');
282
- }
283
- }
284
- }
285
-
286
-
287
-
288
- // Reset the coords array to stop them growing
289
- this.angles = [];
290
- this.coords = [];
291
- this.coords2 = [];
292
-
293
-
294
-
295
-
296
-
297
- // Create the defs tag if necessary
298
- RG.SVG.createDefs(this);
299
-
300
-
301
-
302
-
303
- this.graphWidth = this.width - prop.gutterLeft - prop.gutterRight;
304
- this.graphHeight = this.height - prop.gutterTop - prop.gutterBottom;
305
-
306
-
307
-
308
- // Work out the center point
309
- this.centerx = (this.graphWidth / 2) + prop.gutterLeft;
310
- this.centery = (this.graphHeight / 2) + prop.gutterTop;
311
- this.radius = ma.min(this.graphWidth, this.graphHeight) / 2;
312
-
313
-
314
-
315
- // Allow the user to override the calculated centerx/y/radius
316
- this.centerx = typeof prop.centerx === 'number' ? prop.centerx : this.centerx;
317
- this.centery = typeof prop.centery === 'number' ? prop.centery : this.centery;
318
- this.radius = typeof prop.radius === 'number' ? prop.radius : this.radius;
319
-
320
- //
321
- // Allow the centerx/centery/radius to be a plus/minus
322
- //
323
- if (typeof prop.radius === 'string' && prop.radius.match(/^\+|-\d+$/) ) this.radius += parseFloat(prop.radius);
324
- if (typeof prop.centerx === 'string' && prop.centerx.match(/^\+|-\d+$/) ) this.centery += parseFloat(prop.centerx);
325
- if (typeof prop.centery === 'string' && prop.centery.match(/^\+|-\d+$/) ) this.centerx += parseFloat(prop.centery);
326
-
327
-
328
-
329
-
330
-
331
- /**
332
- * Add the data to the .originalData array and work out the max value
333
- *
334
- * 2/5/14 Now also use this loop to ensure that the data pieces
335
- * are numbers
336
- */
337
- if (RG.SVG.isArray(this.data) && (typeof this.data[0] === 'number' || typeof this.data[0] === 'string')) {
338
- this.data = [this.data];
339
- }
340
-
341
- // Convert strings to numbers
342
- for (var i=0; i<this.data.length; ++i) {
343
-
344
- for (var j=0; j<this.data[i].length; ++j) {
345
-
346
- if (typeof this.data[i][j] === 'string') {
347
- this.data[i][j] = RG.SVG.stringsToNumbers(this.data[i][j]);
348
- }
349
- }
350
- }
351
-
352
-
353
-
354
-
355
-
356
-
357
- // Modify the datasets to represent the stacked data
358
- // (if its stacked)
359
- if (prop.filled && prop.filledAccumulative) {
360
- for (var dataset=1; dataset<this.data.length; ++dataset) {
361
- for (var i=0; i<this.data[dataset].length; ++i) {
362
- this.data[dataset][i] += this.data[dataset - 1][i];
363
- }
364
- }
365
- }
366
-
367
-
368
-
369
-
370
-
371
- // Get the max value
372
- this.getMaxValue();
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
- // Parse the colors for gradients
381
- RG.SVG.resetColorsToOriginalValues({object:this});
382
- this.parseColors();
383
-
384
- //
385
- // Get the scale
386
- //
387
-
388
- this.scale = RG.SVG.getScale({
389
- object: this,
390
- numlabels: typeof prop.scaleLabelsCount === 'number' ? prop.scaleLabelsCount : prop.backgroundGridConcentricCount,
391
- unitsPre: prop.scaleUnitsPre,
392
- unitsPost: prop.scaleUnitsPost,
393
- max: typeof prop.scaleMax === 'number' ? prop.scaleMax : this.max,
394
- min: prop.scaleMin,
395
- point: prop.scalePoint,
396
- round: prop.scaleRound,
397
- thousand: prop.scaleThousand,
398
- decimals: prop.scaleDecimals,
399
- strict: typeof prop.scaleMax === 'number',
400
- formatter: prop.scaleFormatter
401
- });
402
-
403
- this.max = this.scale.max;
404
-
405
-
406
-
407
- // Draw the background 'grid'
408
- this.drawBackground();
409
-
410
-
411
-
412
- // Draw the chart
413
- this.drawRadar();
414
-
415
-
416
-
417
- // Draw the tickmarks for the chart
418
- this.drawTickmarks();
419
-
420
-
421
-
422
- // Draw the labels
423
- this.drawLabels();
424
-
425
-
426
-
427
- // Draw the title and subtitle
428
- RG.SVG.drawTitle(this);
429
-
430
-
431
-
432
- // Add the tooltip hotspots
433
- this.addTooltipHotspots();
434
-
435
-
436
-
437
-
438
-
439
-
440
- // Draw the key
441
- if (typeof prop.key !== null && RG.SVG.drawKey) {
442
- RG.SVG.drawKey(this);
443
- } else if (!RGraph.SVG.isNull(prop.key)) {
444
- alert('The drawKey() function does not exist - have you forgotten to include the key library?');
445
- }
446
-
447
-
448
-
449
-
450
-
451
- // Add the attribution link. If you're adding this elsewhere on your page/site
452
- // and you don't want it displayed then there are options available to not
453
- // show it.
454
- RG.SVG.attribution(this);
455
-
456
- // Create the shadow definition if needed
457
- if (prop.shadow) {
458
- RG.SVG.setShadow({
459
- object: this,
460
- offsetx: prop.shadowOffsetx,
461
- offsety: prop.shadowOffsety,
462
- blur: prop.shadowBlur,
463
- opacity: prop.shadowOpacity,
464
- id: 'dropShadow'
465
- });
466
- }
467
-
468
-
469
-
470
- // Add the event listener that clears the highlight if
471
- // there is any. Must be MOUSEDOWN (ie before the click event)
472
- var obj = this;
473
- doc.body.addEventListener('mousedown', function (e)
474
- {
475
- obj.hideHighlight(obj);
476
- }, false);
477
-
478
-
479
-
480
- // Fire the draw event
481
- RG.SVG.fireCustomEvent(this, 'ondraw');
482
-
483
-
484
-
485
- return this;
486
- };
487
-
488
-
489
-
490
-
491
-
492
-
493
-
494
-
495
- //
496
- // Draw the background grid
497
- //
498
- this.drawBackground = function ()
499
- {
500
- if (prop.backgroundGrid) {
501
-
502
- // Create the background grid group tag
503
- var grid = RG.SVG.create({
504
- svg: this.svg,
505
- parent: this.svg.all,
506
- type: 'g',
507
- attr: {
508
- className: 'rgraph_radar_grid',
509
- fill: 'rgba(0,0,0,0)',
510
- stroke: prop.backgroundGridColor
511
- }
512
- });
513
-
514
- // Draw the concentric "rings" grid lines that are
515
- // arranged around the centerx/centery along with
516
- // the radials that eminate from the center outwards
517
-
518
- var origin = 0 - (RG.SVG.TRIG.PI / 2),
519
- radials = (typeof prop.backgroundGridRadialsCount === 'number' ? prop.backgroundGridRadialsCount : this.data[0].length),
520
- concentrics = prop.backgroundGridConcentricsCount,
521
- step = RG.SVG.TRIG.TWOPI / radials;
522
-
523
-
524
-
525
-
526
-
527
- // First draw the radial lines that emanate from the
528
- // center outwards
529
- if (radials > 0) {
530
-
531
- for (var i=0,len=radials; i<len; ++i) {
532
-
533
- var coords = RG.SVG.TRIG.toCartesian({
534
- cx: this.centerx,
535
- cy: this.centery,
536
- r: this.radius,
537
- angle: origin + (i * step)
538
- });
539
-
540
- var str = 'M {1} {2} L {3} {4}'.format(
541
- this.centerx,
542
- this.centery,
543
- coords.x,
544
- coords.y
545
- );
546
-
547
- RG.SVG.create({
548
- svg: this.svg,
549
- type: 'path',
550
- parent: grid,
551
- attr: {
552
- d: str,
553
- stroke: prop.backgroundGridColor,
554
- 'stroke-width': prop.backgroundGridLinewidth
555
- }
556
- });
557
- }
558
- }
559
-
560
-
561
-
562
-
563
-
564
- // Draw the concentrics
565
- if (concentrics > 0) {
566
-
567
- if (prop.backgroundGridPoly) {
568
- for (var j=1; j<=concentrics; j++) {
569
- for (var i=0,len=radials,path=[]; i<len; ++i) {
570
-
571
- var coords = RG.SVG.TRIG.toCartesian({
572
- cx: this.centerx,
573
- cy: this.centery,
574
- r: this.radius * (j/concentrics),
575
- angle: origin + (i * step)
576
- });
577
-
578
- path.push('{1} {2} {3}'.format(
579
- i === 0 ? 'M' : 'L',
580
- coords.x,
581
- coords.y
582
- ));
583
-
584
- }
585
-
586
- // Now add the path to the scene
587
- RG.SVG.create({
588
- svg: this.svg,
589
- type: 'path',
590
- parent: grid,
591
- attr: {
592
- d: path.join(' ') + ' z',
593
- fill: 'transparent',
594
- stroke: prop.backgroundGridColor,
595
- 'stroke-width': prop.backgroundGridLinewidth
596
- }
597
- });
598
- }
599
-
600
-
601
-
602
-
603
-
604
- // Draw the background "grid" as concentric circles
605
- } else {
606
-
607
-
608
-
609
-
610
-
611
-
612
- for (var j=1; j<=concentrics; j++) {
613
-
614
- // Add circle to the scene
615
- RG.SVG.create({
616
- svg: this.svg,
617
- type: 'circle',
618
- parent: grid,
619
- attr: {
620
- cx: this.centerx,
621
- cy: this.centery,
622
- r: this.radius * (j/concentrics),
623
- fill: 'transparent',
624
- stroke: prop.backgroundGridColor,
625
- 'stroke-width': prop.backgroundGridLinewidth
626
- }
627
- });
628
- }
629
- }
630
- }
631
- }
632
- };
633
-
634
-
635
-
636
-
637
-
638
-
639
-
640
-
641
- //
642
- // Draws the radar
643
- //
644
- this.drawRadar = function (opt)
645
- {
646
- for (var dataset=0,len=this.data.length; dataset<len; ++dataset) {
647
-
648
- // Ensure these exist
649
- this.coords2[dataset] = [];
650
- this.angles2[dataset] = [];
651
-
652
- // Initialise the path
653
- var path = [];
654
-
655
- for (var i=0,len2=this.data[dataset].length; i<len2; ++i) {
656
-
657
- var value = this.data[dataset][i];
658
-
659
- var xy = RG.SVG.TRIG.toCartesian({
660
- cx: this.centerx,
661
- cy: this.centery,
662
- r: this.getRadius(this.data[dataset][i]),
663
- angle: (RG.SVG.TRIG.TWOPI / len2) * i - RG.SVG.TRIG.HALFPI
664
- });
665
-
666
- xy.r = (( (value - prop.scaleMin) / (this.max - prop.scaleMin) ) ) * this.radius;
667
- xy.angle = (RG.SVG.TRIG.TWOPI / len2) * i - RG.SVG.TRIG.HALFPI;
668
-
669
- path.push('{1}{2} {3}'.format(
670
- i === 0 ? 'M' : 'L',
671
- xy.x,
672
- xy.y
673
- ));
674
-
675
- // Save the coordinates and angle
676
- this.angles.push({
677
- cx: this.centerx,
678
- cy: this.centery,
679
- r: xy.r,
680
- angle: xy.angle
681
- });
682
- this.angles2[dataset].push({
683
- cx: this.centerx,
684
- cy: this.centery,
685
- r: xy.r,
686
- angle: xy.angle
687
- });
688
-
689
- this.coords.push([
690
- xy.x,
691
- xy.y
692
- ]);
693
- this.coords2[dataset].push([
694
- xy.x,
695
- xy.y
696
- ]);
697
- }
698
-
699
- // If a stacked filled charts then add the reverse path
700
- if (dataset > 0 && prop.filled && prop.filledAccumulative) {
701
-
702
- // Add a line completing the "circle"
703
- path.push('L {1} {2}'.format(
704
- this.coords2[dataset][0][0],
705
- this.coords2[dataset][0][1]
706
- ));
707
-
708
- // Move to the previous dataset
709
- path.push('M {1} {2}'.format(
710
- this.coords2[dataset - 1][0][0],
711
- this.coords2[dataset - 1][0][1]
712
- ));
713
-
714
- // Now backtrack over the previous dataset
715
- for (var i=this.coords2[dataset - 1].length - 1; i>=0; --i) {
716
- path.push('L {1} {2}'.format(
717
- this.coords2[dataset - 1][i][0],
718
- this.coords2[dataset - 1][i][1]
719
- ));
720
- }
721
-
722
- this.redraw = true;
723
-
724
- } else {
725
- // Add the closepath
726
- path.push('z');
727
- }
728
-
729
-
730
- var path = RG.SVG.create({
731
- svg: this.svg,
732
- type: 'path',
733
- parent: this.svg.all,
734
- attr: {
735
- d: path.join(" "),
736
- stroke: prop.colors[dataset],
737
- fill: prop.filled ? prop.colors[dataset] : 'transparent',
738
- 'fill-opacity': prop.filledOpacity,
739
- 'stroke-width': prop.linewidth,
740
- 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : '',
741
- filter: prop.shadow ? 'url(#dropShadow)' : '',
742
- }
743
- });
744
-
745
- path.setAttribute('data-dataset', dataset);
746
- }
747
-
748
-
749
- // Redraw the chart (this only runs if necessary
750
- this.redrawRadar();
751
- };
752
-
753
-
754
-
755
-
756
-
757
-
758
-
759
-
760
- //
761
- // Redraws the chart if required
762
- //
763
- this.redrawRadar = function ()
764
- {
765
- if (this.redraw) {
766
-
767
- this.redraw = false;
768
-
769
- // Loop through ths coordinates
770
- for (var dataset = 0; dataset<this.coords2.length; ++dataset) {
771
-
772
- var path = [];
773
-
774
- for (var i=0; i<this.coords2[dataset].length; ++i) {
775
- if (i === 0) {
776
- path.push('M {1} {2}'.format(
777
- this.coords2[dataset][i][0],
778
- this.coords2[dataset][i][1]
779
- ));
780
- } else {
781
- path.push('L {1} {2}'.format(
782
- this.coords2[dataset][i][0],
783
- this.coords2[dataset][i][1]
784
- ))
785
- }
786
- }
787
-
788
- path.push('z')
789
-
790
- RG.SVG.create({
791
- svg: this.svg,
792
- type: 'path',
793
- parent: this.svg.all,
794
- attr: {
795
- d: path.join(" "),
796
- stroke: prop.colors[dataset],
797
- fill: 'transparent',
798
- 'stroke-width': prop.linewidth
799
- }
800
- });
801
- }
802
- }
803
- };
804
-
805
-
806
-
807
-
808
-
809
-
810
-
811
-
812
- //
813
- // Draw the tickmarks
814
- //
815
- this.drawTickmarks = function ()
816
- {
817
- var group = RG.SVG.create({
818
- svg: this.svg,
819
- parent: this.svg.all,
820
- type: 'g',
821
- attr: {
822
- className: 'rgraph_radar_tickmarks'
823
- }
824
- });
825
-
826
- for (var i=0; i<this.coords2.length; ++i) {
827
- for (var j=0; j<this.coords2[i].length; ++j) {
828
- if (prop.tickmarks === 'circle' || prop.tickmarks === 'filledcircle' ) {
829
- var c = RG.SVG.create({
830
- svg: this.svg,
831
- type: 'circle',
832
- parent: group,
833
- attr: {
834
- cx: this.coords2[i][j][0],
835
- cy: this.coords2[i][j][1],
836
- r: prop.tickmarksSize,
837
- fill: prop.tickmarks === 'filledcircle' ? prop.colors[i] : prop.tickmarksFill,
838
- stroke: prop.colors[i],
839
- 'stroke-width': prop.tickmarksLinewidth,
840
- 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
841
- }
842
- });
843
-
844
- c.setAttribute('data-dataset', i);
845
- c.setAttribute('data-index', j);
846
-
847
-
848
- } else if (prop.tickmarks === 'rect' || prop.tickmarks === 'filledrect') {
849
-
850
- var halfTickmarkSize = prop.tickmarksSize / 2;
851
- var fill = typeof prop.tickmarksFill === 'object' && prop.tickmarksFill[i] ? prop.tickmarksFill[i] : prop.tickmarksFill;
852
-
853
- var s = RG.SVG.create({
854
- svg: this.svg,
855
- type: 'rect',
856
- parent: group,
857
- attr: {
858
- x: this.coords2[i][j][0] - halfTickmarkSize,
859
- y: this.coords2[i][j][1] - halfTickmarkSize,
860
- width: prop.tickmarksSize,
861
- height: prop.tickmarksSize,
862
- fill: prop.tickmarks === 'filledrect' ? prop.colors[i] : fill,
863
- stroke: prop.colors[i],
864
- 'stroke-width': prop.tickmarksLinewidth
865
- }
866
- });
867
-
868
- s.setAttribute('data-dataset', i);
869
- s.setAttribute('data-index', j);
870
- }
871
- }
872
- }
873
- };
874
-
875
-
876
-
877
-
878
-
879
-
880
-
881
-
882
- //
883
- // Draw the labels
884
- //
885
- this.drawLabels = function ()
886
- {
887
- var angles = this.angles2,
888
- prop = this.properties,
889
- labels = prop.labels;
890
-
891
- for (var i=0,len=labels.length; i<len; ++i) {
892
-
893
- if (!labels[i]) {
894
- continue;
895
- }
896
-
897
- var endpoint = RG.SVG.TRIG.getRadiusEndPoint({
898
- angle: RG.SVG.TRIG.TWOPI / labels.length * i - RG.SVG.TRIG.HALFPI,
899
- r: this.radius + 15
900
- });
901
-
902
- var x = endpoint[0] + this.centerx,
903
- y = endpoint[1] + this.centery;
904
-
905
- //
906
- // Horizontal alignment
907
-
908
- if ((i / len) < 0.5) {
909
- halign = 'left';
910
- } else {
911
- halign = 'right';
912
- }
913
-
914
- //
915
- // Vertical alignment
916
- //
917
- if ((i / len) < 0.25 || (i / len) > 0.75) {
918
- valign = 'bottom';
919
- } else {
920
- valign = 'top';
921
- }
922
-
923
- // Specify the alignment for labels which are on the axes
924
- if ( (i / len) === 0 ) {halign = 'center';}
925
- if ( (i / len) === 0.25 ) {valign = 'center';}
926
- if ( (i / len) === 0.5 ) {halign = 'center';}
927
- if ( (i / len) === 0.75 ) {valign = 'center';}
928
-
929
-
930
- RG.SVG.text({
931
- object: this,
932
- svg: this.svg,
933
- parent: this.svg.all,
934
- text: labels[i],
935
- size: typeof prop.labelsSize === 'number' ? prop.labelsSize : prop.textSize,
936
- x: x,
937
- y: y,
938
- halign: halign,
939
- valign: 'center',
940
- color: prop.labelsColor || prop.textColor,
941
- bold: typeof prop.labelsBold === 'boolean' ? prop.labelsBold : prop.textBold,
942
- italic: typeof prop.labelsItalic === 'boolean' ? prop.labelsItalic : prop.textItalic,
943
- font: prop.labelsFont || prop.textFont
944
- });
945
- }
946
-
947
-
948
-
949
-
950
-
951
-
952
-
953
-
954
-
955
-
956
-
957
-
958
- // Draw the scale if required
959
- if (prop.scaleVisible) {
960
- for (var i=0; i<this.scale.labels.length; ++i) {
961
-
962
- var x = this.centerx;
963
- var y = this.centery - (this.radius / this.scale.labels.length * (i+1) );
964
-
965
-
966
- RG.SVG.text({
967
- object: this,
968
- svg: this.svg,
969
- parent: this.svg.all,
970
- text: this.scale.labels[i],
971
- size: prop.scaleSize || prop.textSize - 2,
972
- x: x,
973
- y: y,
974
- halign: 'center',
975
- valign: 'center',
976
- background: 'rgba(255,255,255,0.7)',
977
- padding:2,
978
- color: prop.scaleColor || prop.textColor,
979
- bold: typeof prop.scaleBold === 'boolean' ? prop.scaleBold : prop.textBold,
980
- italic: typeof prop.scaleItalic === 'boolean' ? prop.scaleItalic : prop.textItalic,
981
- font: prop.scaleFont || prop.textFont
982
- });
983
- }
984
-
985
- // Draw the zero label
986
- var str = RG.SVG.numberFormat({
987
- object: this,
988
- num: this.scale.min.toFixed(prop.scaleDecimals),
989
- prepend: prop.scaleUnitsPre,
990
- append: prop.scaleUnitsPost,
991
- point: prop.scalePoint,
992
- thousand: prop.scaleThousand,
993
- formatter: prop.scaleFormatter
994
- });
995
-
996
-
997
- RG.SVG.text({
998
- object: this,
999
- svg: this.svg,
1000
- parent: this.svg.all,
1001
- text: str,
1002
- size: prop.scaleSize || prop.textSize - 2,
1003
- x: this.centerx,
1004
- y: this.centery,
1005
- halign: 'center',
1006
- valign: 'center',
1007
- background: 'rgba(255,255,255,0.7)',
1008
- padding:2,
1009
- color: prop.scaleColor || prop.textColor,
1010
- bold: typeof prop.scaleBold === 'boolean' ? prop.scaleBold : prop.textBold,
1011
- italic: typeof prop.scaleItalic === 'boolean' ? prop.scaleItalic : prop.textItalic,
1012
- font: prop.scaleFont || prop.textFont
1013
- });
1014
- }
1015
- };
1016
-
1017
-
1018
-
1019
-
1020
-
1021
-
1022
-
1023
-
1024
- /**
1025
- * This function can be used to highlight a segment on the chart
1026
- *
1027
- * @param object circle The circle to highlight
1028
- */
1029
- this.highlight = function (circle)
1030
- {
1031
- circle.setAttribute('fill', prop.highlightFill);
1032
- circle.setAttribute('stroke', prop.highlightStroke);
1033
- circle.setAttribute('stroke-width', prop.highlightLinewidth);
1034
-
1035
- this.highlight_node = circle;
1036
-
1037
- RG.SVG.REG.set('highlight', circle);
1038
- };
1039
-
1040
-
1041
-
1042
-
1043
-
1044
-
1045
-
1046
-
1047
- // Add the hide function
1048
- //this.hideHighlight = function ()
1049
- //{
1050
- // var highlight = RG.SVG.REG.get('highlight');
1051
-
1052
- // if (highlight) {
1053
- // highlight.setAttribute('fill', 'transparent');
1054
- // highlight.setAttribute('stroke', 'transparent');
1055
- // }
1056
- //};
1057
-
1058
-
1059
-
1060
-
1061
-
1062
-
1063
-
1064
-
1065
- /**
1066
- * This allows for easy specification of gradients
1067
- */
1068
- this.parseColors = function ()
1069
- {
1070
- // Save the original colors so that they can be restored when the canvas is reset
1071
- if (!Object.keys(this.originalColors).length) {
1072
- this.originalColors = {
1073
- colors: RG.SVG.arrayClone(prop.colors),
1074
- highlightFill: RG.SVG.arrayClone(prop.highlightFill)
1075
- }
1076
- }
1077
-
1078
-
1079
- // colors
1080
- var colors = prop.colors;
1081
-
1082
- if (colors) {
1083
- for (var i=0; i<colors.length; ++i) {
1084
- colors[i] = RG.SVG.parseColorRadial({
1085
- object: this,
1086
- color: colors[i]
1087
- });
1088
- }
1089
- }
1090
-
1091
- // Highlight fill
1092
- prop.highlightFill = RG.SVG.parseColorRadial({
1093
- object: this,
1094
- color: prop.highlightFill
1095
- });
1096
- };
1097
-
1098
-
1099
-
1100
-
1101
-
1102
-
1103
-
1104
-
1105
- //
1106
- // Get the maximum value
1107
- //
1108
- this.getMaxValue = function ()
1109
- {
1110
- var max = 0;
1111
-
1112
- if (prop.filled && prop.filledAccumulative) {
1113
- this.max = RG.SVG.arrayMax(this.data[this.data.length - 1]);
1114
- } else {
1115
- for (var dataset=0,max=0; dataset<this.data.length; ++dataset) {
1116
- this.max = ma.max(this.max, RG.SVG.arrayMax(this.data[dataset]));
1117
- }
1118
- }
1119
- };
1120
-
1121
-
1122
-
1123
-
1124
-
1125
-
1126
-
1127
-
1128
- //
1129
- // Gets the radius of a value
1130
- //
1131
- //@param number The value to get the radius for
1132
- //
1133
- this.getRadius = function (value)
1134
- {
1135
- return ( (value - prop.scaleMin) / (this.scale.max - prop.scaleMin) ) * this.radius;
1136
- };
1137
-
1138
-
1139
-
1140
-
1141
-
1142
-
1143
-
1144
-
1145
- //
1146
- // Adds the circular hotspot that facilitate tooltips
1147
- // (to a single point)
1148
- //
1149
- this.addTooltipHotspots = function ()
1150
- {
1151
- if (prop.tooltips && prop.tooltips.length > 0) {
1152
-
1153
- // Make the tooltipsEvent default to click
1154
- if (prop.tooltipsEvent !== 'mousemove') {
1155
- prop.tooltipsEvent = 'click';
1156
- }
1157
-
1158
- var group = RG.SVG.create({
1159
- svg: this.svg,
1160
- type: 'g',
1161
- parent: this.svg.all,
1162
- attr: {
1163
- className: 'rgraph-radar-tooltip-hotspots'
1164
- }
1165
- });
1166
-
1167
- for (var dataset=0,seq=0; dataset<this.coords2.length; ++dataset) {
1168
- for (var i=0; i<this.coords2[dataset].length; ++i) {
1169
-
1170
- var circle = RG.SVG.create({
1171
- svg: this.svg,
1172
- type: 'circle',
1173
- parent: group,
1174
- attr: {
1175
- cx: this.coords2[dataset][i][0],
1176
- cy: this.coords2[dataset][i][1],
1177
- r: prop.tickmarksSize,
1178
- fill: 'transparent',
1179
- stroke: 'transparent',
1180
- 'stroke-width': 0,
1181
- 'data-sequential-index': seq
1182
- },
1183
- style: {
1184
- cursor: prop['tooltips'][seq] ? 'pointer' : 'default'
1185
- }
1186
- });
1187
-
1188
- (function (dataset, index, seq, obj)
1189
- {
1190
- if (prop.tooltips[seq]) {
1191
- circle.addEventListener(prop.tooltipsEvent, function (e)
1192
- {
1193
- var tooltip = RG.SVG.REG.get('tooltip');
1194
-
1195
- //obj.hideHighlight();
1196
-
1197
- if (tooltip && tooltip.__sequentialIndex__ === seq) {
1198
- return;
1199
- }
1200
-
1201
- // Show the tooltip
1202
- RG.SVG.tooltip({
1203
- object: obj,
1204
- dataset: dataset,
1205
- index: index,
1206
- sequentialIndex: seq,
1207
- text: prop.tooltips[seq],
1208
- event: e
1209
- });
1210
-
1211
- // Highlight the shape that has been clicked on
1212
- obj.highlight(this);
1213
-
1214
- }, false);
1215
-
1216
- // Install the event listener that changes the
1217
- // cursor if necessary
1218
- if (prop.tooltipsEvent === 'click') {
1219
- circle.addEventListener('mousemove', function (e)
1220
- {
1221
- e.target.style.cursor = 'pointer';
1222
- }, false);
1223
- }
1224
- }
1225
-
1226
- }(dataset, i, seq++, this));
1227
- }
1228
- }
1229
- }
1230
- };
1231
-
1232
-
1233
-
1234
-
1235
-
1236
-
1237
-
1238
-
1239
- //
1240
- // A roundRobin effect for the Pie chart
1241
- //
1242
- // @param object Options for the effect
1243
- // @param function An optional callback function to call when
1244
- // the effect is complete
1245
- //
1246
- this.roundRobin = function ()
1247
- {
1248
- /*
1249
- var obj = this,
1250
- opt = arguments[0] || {},
1251
- data = RG.SVG.arrayClone(this.data),
1252
- prop = this.properties,
1253
- frame = 1,
1254
- frames = opt.frames || 30,
1255
- callback = typeof opt.callback === 'function' ? opt.callback : function () {},
1256
- dataSum = RG.SVG.arraySum(this.data),
1257
- textColor = prop.textColor;
1258
-
1259
- // Set the text color to transparent
1260
- this.properties.textColor = 'rgba(0,0,0,0)';
1261
-
1262
-
1263
- // Draw the chart first
1264
- obj.draw();
1265
-
1266
- // Now get the resulting angles
1267
- angles = RG.SVG.arrayClone(obj.angles);
1268
-
1269
-
1270
- function iterator ()
1271
- {
1272
- prop.roundRobinMultiplier = 1 / frames * frame++;
1273
-
1274
- for (var i=0; i<obj.angles.length; ++i) {
1275
-
1276
- var value = obj.data[i];
1277
-
1278
-
1279
-
1280
- // NB This was an absolute git to work out for some reason.
1281
-
1282
-
1283
-
1284
- obj.angles[i].start = angles[i].start * prop.roundRobinMultiplier;
1285
- obj.angles[i].end = angles[i].end * prop.roundRobinMultiplier;
1286
-
1287
- //var segment = (((value * prop.roundRobinMultiplier) / dataSum) * RG.SVG.TRIG.TWOPI);
1288
- var segment = ((obj.angles[i].end - obj.angles[i].start) / 2);
1289
- var explodedX = ma.cos(obj.angles[i].start + segment - RG.SVG.TRIG.HALFPI) * (prop.exploded[i] || 0);
1290
- var explodedY = ma.sin(obj.angles[i].start + segment - RG.SVG.TRIG.HALFPI) * (prop.exploded[i] || 0);
1291
-
1292
-
1293
-
1294
- var path = RG.SVG.TRIG.getArcPath({
1295
- cx: obj.centerx + explodedX,
1296
- cy: obj.centery + explodedY,
1297
- r: obj.radius,// * prop.roundRobinMultiplier,
1298
- start: obj.angles[i].start,
1299
- end: obj.angles[i].end
1300
- });
1301
-
1302
- path = path + " L {1} {2} Z".format(
1303
- obj.centerx + explodedX,
1304
- obj.centery + explodedY
1305
- );
1306
-
1307
- if (obj.shadowNodes && obj.shadowNodes[i]) {
1308
- obj.shadowNodes[i].setAttribute('d', path);
1309
- }
1310
- obj.nodes[i].setAttribute('d', path);
1311
- }
1312
-
1313
-
1314
- if (frame <= frames) {
1315
- RG.SVG.FX.update(iterator);
1316
- } else {
1317
- prop.textColor = textColor;
1318
-
1319
- RG.SVG.redraw(obj.svg);
1320
-
1321
- callback(obj);
1322
- }
1323
- }
1324
-
1325
- iterator();
1326
-
1327
- return this;
1328
- */
1329
- };
1330
-
1331
-
1332
-
1333
-
1334
-
1335
-
1336
-
1337
-
1338
- /**
1339
- * Using a function to add events makes it easier to facilitate method
1340
- * chaining
1341
- *
1342
- * @param string type The type of even to add
1343
- * @param function func
1344
- */
1345
- this.on = function (type, func)
1346
- {
1347
- if (type.substr(0,2) !== 'on') {
1348
- type = 'on' + type;
1349
- }
1350
-
1351
- RG.SVG.addCustomEventListener(this, type, func);
1352
-
1353
- return this;
1354
- };
1355
-
1356
-
1357
-
1358
-
1359
-
1360
-
1361
-
1362
-
1363
- //
1364
- // Used in chaining. Runs a function there and then - not waiting for
1365
- // the events to fire (eg the onbeforedraw event)
1366
- //
1367
- // @param function func The function to execute
1368
- //
1369
- this.exec = function (func)
1370
- {
1371
- func(this);
1372
-
1373
- return this;
1374
- };
1375
-
1376
-
1377
-
1378
-
1379
-
1380
-
1381
-
1382
-
1383
- //
1384
- // Removes the tooltip highlight from the chart
1385
- //
1386
- this.removeHighlight =
1387
- this.hideHighlight = function ()
1388
- {
1389
- var highlight = RG.SVG.REG.get('highlight');
1390
-
1391
- if (highlight && this.highlight_node) {
1392
- this.highlight_node.setAttribute('fill','transparent');
1393
- this.highlight_node.setAttribute('stroke','transparent');
1394
-
1395
- RG.SVG.REG.set('highlight', null);
1396
- }
1397
- };
1398
-
1399
-
1400
-
1401
-
1402
-
1403
-
1404
-
1405
-
1406
- //
1407
- // The trace effect
1408
- //
1409
- // @param ... object Options to the effect
1410
- // @param ... function A callback function to run when the effect finishes
1411
- //
1412
- this.trace = function ()
1413
- {
1414
- var opt = arguments[0] || {},
1415
- frame = 1,
1416
- frames = opt.frames || 120,
1417
- obj = this
1418
- step = 360 / frames;
1419
-
1420
- this.isTrace = true;
1421
-
1422
- this.draw();
1423
-
1424
- // Create the clip area
1425
- var clipPath = RG.SVG.create({
1426
- svg: this.svg,
1427
- parent: this.svg.defs,
1428
- type: 'clipPath',
1429
- attr: {
1430
- id: 'trace-effect-clip'
1431
- }
1432
- });
1433
-
1434
- clipPathArcPath = RG.SVG.TRIG.getArcPath2({
1435
- cx: this.angles[0].cx,
1436
- cy: this.angles[0].cy,
1437
- r: this.angles[0].r * 2,
1438
- start: 0,
1439
- end: 0
1440
- });
1441
-
1442
- var clipPathArc = RG.SVG.create({
1443
- svg: this.svg,
1444
- parent: clipPath,
1445
- type: 'path',
1446
- attr: {
1447
- d: clipPathArcPath
1448
- }
1449
- });
1450
-
1451
-
1452
- var iterator = function ()
1453
- {
1454
- var width = (frame++) / frames * obj.width;
1455
- var deg = (360 / frames) * frame++,
1456
- rad = (RG.SVG.TRIG.TWOPI / 360) * deg
1457
-
1458
- clipPathArc.setAttribute('d', RG.SVG.TRIG.getArcPath2({
1459
- cx: obj.angles[0].cx,
1460
- cy: obj.angles[0].cy,
1461
- r: obj.angles[0].r * 2,
1462
- start: 0,
1463
- end: rad
1464
- }));
1465
-
1466
- if (frame <= frames) {
1467
- RG.SVG.FX.update(iterator);
1468
- } else if (opt.callback) {
1469
- (opt.callback)(obj);
1470
- }
1471
- };
1472
-
1473
- iterator();
1474
-
1475
- return this;
1476
- };
1477
-
1478
-
1479
-
1480
-
1481
-
1482
-
1483
-
1484
-
1485
- //
1486
- // Set the options that the user has provided
1487
- //
1488
- for (i in conf.options) {
1489
- if (typeof i === 'string') {
1490
- this.set(i, conf.options[i]);
1491
- }
1492
- }
1493
- };
1494
-
1495
-
1496
-
1497
- return this;
1498
-
1499
-
1500
-
1501
-
1502
- // End module pattern
1503
- })(window, document);
2
+ RGraph=window.RGraph||{isRGraph:true};RGraph.SVG=RGraph.SVG||{};(function(win,doc,undefined)
3
+ {var RG=RGraph,ua=navigator.userAgent,ma=Math,win=window,doc=document;RG.SVG.Radar=function(conf)
4
+ {this.set=function(name,value)
5
+ {if(arguments.length===1&&typeof name==='object'){for(i in arguments[0]){if(typeof i==='string'){var ret=RG.SVG.commonSetter({object:this,name:i,value:arguments[0][i]});name=ret.name;value=ret.value;this.set(name,value);}}}else{var ret=RG.SVG.commonSetter({object:this,name:name,value:value});name=ret.name;value=ret.value;this.properties[name]=value;if(name==='colors'){this.originalColors=RG.SVG.arrayClone(value);this.colorsParsed=false;}}
6
+ return this;};this.id=conf.id;this.uid=RG.SVG.createUID();this.container=document.getElementById(this.id);this.layers={};this.svg=RG.SVG.createSVG({object:this,container:this.container});this.isRGraph=true;this.width=Number(this.svg.getAttribute('width'));this.height=Number(this.svg.getAttribute('height'));this.data=RG.SVG.arrayClone(conf.data);this.originalData=RG.SVG.arrayClone(conf.data);this.type='radar';this.coords=[];this.coords2=[];this.angles=[];this.angles2=[];this.colorsParsed=false;this.originalColors={};this.gradientCounter=1;this.nodes=[];this.shadowNodes=[];this.max=0;this.redraw=false;this.highlight_node=null;RG.SVG.OR.add(this);this.container.style.display='inline-block';this.properties={centerx:null,centery:null,radius:null,gutterLeft:35,gutterRight:35,gutterTop:35,gutterBottom:35,backgroundGrid:true,backgroundGridColor:'#ddd',backgroundGridRadialsCount:null,backgroundGridConcentricsCount:5,backgroundGridLinewidth:1,backgroundGridPoly:true,colors:['red','black','orange','green','#6ff','#ccc','pink','orange','cyan','maroon','olive','teal'],filled:false,filledOpacity:0.25,filledAccumulative:true,textColor:'black',textFont:'sans-serif',textSize:12,textBold:false,textItalic:false,labels:[],scaleVisible:true,scaleUnitsPre:'',scaleUnitsPost:'',scaleMax:null,scaleMin:0,scalePoint:'.',scaleThousand:',',scaleRound:false,scaleDecimals:0,scaleFormatter:null,scaleBold:null,scaleItalic:null,scaleColor:null,scaleSize:null,scaleFont:null,scaleLabelsCount:5,linewidth:1,tooltips:null,tooltipsOverride:null,tooltipsEffect:'fade',tooltipsCssClass:'RGraph_tooltip',tooltipsEvent:'mousemove',highlightStroke:'rgba(0,0,0,0)',highlightFill:'rgba(255,255,255,0.7)',highlightLinewidth:1,tickmarks:'circle',tickmarksLinewidth:1,tickmarksSize:6,tickmarksFill:'white',title:'',titleSize:16,titleX:null,titleY:null,titleHalign:'center',titleValign:null,titleColor:'black',titleFont:null,titleBold:false,titleItalic:false,titleSubtitle:'',titleSubtitleSize:10,titleSubtitleX:null,titleSubtitleY:null,titleSubtitleHalign:'center',titleSubtitleValign:null,titleSubtitleColor:'#aaa',titleSubtitleFont:null,titleSubtitleBold:false,titleSubtitleItalic:false,grouping:'normal',shadow:false,shadowOffsetx:2,shadowOffsety:2,shadowBlur:2,shadowOpacity:0.25,key:null,keyColors:null,keyOffsetx:0,keyOffsety:0,keyTextOffsetx:0,keyTextOffsety:-1,keyTextSize:null,keyTextBold:null,keyTextItalic:null};RG.SVG.getGlobals(this);if(RG.SVG.FX&&typeof RG.SVG.FX.decorate==='function'){RG.SVG.FX.decorate(this);}
7
+ var prop=this.properties;this.draw=function()
8
+ {RG.SVG.fireCustomEvent(this,'onbeforedraw');this.data=RG.SVG.arrayClone(this.originalData);if(this.data.length>1){var len=this.data[0].length;for(var i=1;i<this.data.length;++i){if(this.data[i].length!==len){alert('[ERROR] The Radar chart datasets must have the same number of elements!');}}}
9
+ this.angles=[];this.coords=[];this.coords2=[];RG.SVG.createDefs(this);this.graphWidth=this.width-prop.gutterLeft-prop.gutterRight;this.graphHeight=this.height-prop.gutterTop-prop.gutterBottom;this.centerx=(this.graphWidth/2)+prop.gutterLeft;this.centery=(this.graphHeight/2)+prop.gutterTop;this.radius=ma.min(this.graphWidth,this.graphHeight)/2;this.centerx=typeof prop.centerx==='number'?prop.centerx:this.centerx;this.centery=typeof prop.centery==='number'?prop.centery:this.centery;this.radius=typeof prop.radius==='number'?prop.radius:this.radius;if(typeof prop.radius==='string'&&prop.radius.match(/^\+|-\d+$/))this.radius+=parseFloat(prop.radius);if(typeof prop.centerx==='string'&&prop.centerx.match(/^\+|-\d+$/))this.centery+=parseFloat(prop.centerx);if(typeof prop.centery==='string'&&prop.centery.match(/^\+|-\d+$/))this.centerx+=parseFloat(prop.centery);if(RG.SVG.isArray(this.data)&&(typeof this.data[0]==='number'||typeof this.data[0]==='string')){this.data=[this.data];}
10
+ for(var i=0;i<this.data.length;++i){for(var j=0;j<this.data[i].length;++j){if(typeof this.data[i][j]==='string'){this.data[i][j]=RG.SVG.stringsToNumbers(this.data[i][j]);}}}
11
+ if(prop.filled&&prop.filledAccumulative){for(var dataset=1;dataset<this.data.length;++dataset){for(var i=0;i<this.data[dataset].length;++i){this.data[dataset][i]+=this.data[dataset-1][i];}}}
12
+ this.getMaxValue();RG.SVG.resetColorsToOriginalValues({object:this});this.parseColors();this.scale=RG.SVG.getScale({object:this,numlabels:typeof prop.scaleLabelsCount==='number'?prop.scaleLabelsCount:prop.backgroundGridConcentricCount,unitsPre:prop.scaleUnitsPre,unitsPost:prop.scaleUnitsPost,max:typeof prop.scaleMax==='number'?prop.scaleMax:this.max,min:prop.scaleMin,point:prop.scalePoint,round:prop.scaleRound,thousand:prop.scaleThousand,decimals:prop.scaleDecimals,strict:typeof prop.scaleMax==='number',formatter:prop.scaleFormatter});this.max=this.scale.max;this.drawBackground();this.drawRadar();this.drawTickmarks();this.drawLabels();RG.SVG.drawTitle(this);this.addTooltipHotspots();if(typeof prop.key!==null&&RG.SVG.drawKey){RG.SVG.drawKey(this);}else if(!RGraph.SVG.isNull(prop.key)){alert('The drawKey() function does not exist - have you forgotten to include the key library?');}
13
+ RG.SVG.attribution(this);if(prop.shadow){RG.SVG.setShadow({object:this,offsetx:prop.shadowOffsetx,offsety:prop.shadowOffsety,blur:prop.shadowBlur,opacity:prop.shadowOpacity,id:'dropShadow'});}
14
+ var obj=this;doc.body.addEventListener('mousedown',function(e)
15
+ {obj.hideHighlight(obj);},false);RG.SVG.fireCustomEvent(this,'ondraw');return this;};this.drawBackground=function()
16
+ {if(prop.backgroundGrid){var grid=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'g',attr:{className:'rgraph_radar_grid',fill:'rgba(0,0,0,0)',stroke:prop.backgroundGridColor}});var origin=0-(RG.SVG.TRIG.PI/2),radials=(typeof prop.backgroundGridRadialsCount==='number'?prop.backgroundGridRadialsCount:this.data[0].length),concentrics=prop.backgroundGridConcentricsCount,step=RG.SVG.TRIG.TWOPI/radials;if(radials>0){for(var i=0,len=radials;i<len;++i){var coords=RG.SVG.TRIG.toCartesian({cx:this.centerx,cy:this.centery,r:this.radius,angle:origin+(i*step)});var str='M {1} {2} L {3} {4}'.format(this.centerx,this.centery,coords.x,coords.y);RG.SVG.create({svg:this.svg,type:'path',parent:grid,attr:{d:str,stroke:prop.backgroundGridColor,'stroke-width':prop.backgroundGridLinewidth}});}}
17
+ if(concentrics>0){if(prop.backgroundGridPoly){for(var j=1;j<=concentrics;j++){for(var i=0,len=radials,path=[];i<len;++i){var coords=RG.SVG.TRIG.toCartesian({cx:this.centerx,cy:this.centery,r:this.radius*(j/concentrics),angle:origin+(i*step)});path.push('{1} {2} {3}'.format(i===0?'M':'L',coords.x,coords.y));}
18
+ RG.SVG.create({svg:this.svg,type:'path',parent:grid,attr:{d:path.join(' ')+' z',fill:'transparent',stroke:prop.backgroundGridColor,'stroke-width':prop.backgroundGridLinewidth}});}}else{for(var j=1;j<=concentrics;j++){RG.SVG.create({svg:this.svg,type:'circle',parent:grid,attr:{cx:this.centerx,cy:this.centery,r:this.radius*(j/concentrics),fill:'transparent',stroke:prop.backgroundGridColor,'stroke-width':prop.backgroundGridLinewidth}});}}}}};this.drawRadar=function(opt)
19
+ {for(var dataset=0,len=this.data.length;dataset<len;++dataset){this.coords2[dataset]=[];this.angles2[dataset]=[];var path=[];for(var i=0,len2=this.data[dataset].length;i<len2;++i){var value=this.data[dataset][i];var xy=RG.SVG.TRIG.toCartesian({cx:this.centerx,cy:this.centery,r:this.getRadius(this.data[dataset][i]),angle:(RG.SVG.TRIG.TWOPI/len2)*i-RG.SVG.TRIG.HALFPI});xy.r=(((value-prop.scaleMin)/(this.max-prop.scaleMin)))*this.radius;xy.angle=(RG.SVG.TRIG.TWOPI/len2)*i-RG.SVG.TRIG.HALFPI;path.push('{1}{2} {3}'.format(i===0?'M':'L',xy.x,xy.y));this.angles.push({cx:this.centerx,cy:this.centery,r:xy.r,angle:xy.angle});this.angles2[dataset].push({cx:this.centerx,cy:this.centery,r:xy.r,angle:xy.angle});this.coords.push([xy.x,xy.y]);this.coords2[dataset].push([xy.x,xy.y]);}
20
+ if(dataset>0&&prop.filled&&prop.filledAccumulative){path.push('L {1} {2}'.format(this.coords2[dataset][0][0],this.coords2[dataset][0][1]));path.push('M {1} {2}'.format(this.coords2[dataset-1][0][0],this.coords2[dataset-1][0][1]));for(var i=this.coords2[dataset-1].length-1;i>=0;--i){path.push('L {1} {2}'.format(this.coords2[dataset-1][i][0],this.coords2[dataset-1][i][1]));}
21
+ this.redraw=true;}else{path.push('z');}
22
+ var path=RG.SVG.create({svg:this.svg,type:'path',parent:this.svg.all,attr:{d:path.join(" "),stroke:prop.colors[dataset],fill:prop.filled?prop.colors[dataset]:'transparent','fill-opacity':prop.filledOpacity,'stroke-width':prop.linewidth,'clip-path':this.isTrace?'url(#trace-effect-clip)':'',filter:prop.shadow?'url(#dropShadow)':'',}});path.setAttribute('data-dataset',dataset);}
23
+ this.redrawRadar();};this.redrawRadar=function()
24
+ {if(this.redraw){this.redraw=false;for(var dataset=0;dataset<this.coords2.length;++dataset){var path=[];for(var i=0;i<this.coords2[dataset].length;++i){if(i===0){path.push('M {1} {2}'.format(this.coords2[dataset][i][0],this.coords2[dataset][i][1]));}else{path.push('L {1} {2}'.format(this.coords2[dataset][i][0],this.coords2[dataset][i][1]))}}
25
+ path.push('z')
26
+ RG.SVG.create({svg:this.svg,type:'path',parent:this.svg.all,attr:{d:path.join(" "),stroke:prop.colors[dataset],fill:'transparent','stroke-width':prop.linewidth}});}}};this.drawTickmarks=function()
27
+ {var group=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'g',attr:{className:'rgraph_radar_tickmarks'}});for(var i=0;i<this.coords2.length;++i){for(var j=0;j<this.coords2[i].length;++j){if(prop.tickmarks==='circle'||prop.tickmarks==='filledcircle'){var c=RG.SVG.create({svg:this.svg,type:'circle',parent:group,attr:{cx:this.coords2[i][j][0],cy:this.coords2[i][j][1],r:prop.tickmarksSize,fill:prop.tickmarks==='filledcircle'?prop.colors[i]:prop.tickmarksFill,stroke:prop.colors[i],'stroke-width':prop.tickmarksLinewidth,'clip-path':this.isTrace?'url(#trace-effect-clip)':''}});c.setAttribute('data-dataset',i);c.setAttribute('data-index',j);}else if(prop.tickmarks==='rect'||prop.tickmarks==='filledrect'){var halfTickmarkSize=prop.tickmarksSize/2;var fill=typeof prop.tickmarksFill==='object'&&prop.tickmarksFill[i]?prop.tickmarksFill[i]:prop.tickmarksFill;var s=RG.SVG.create({svg:this.svg,type:'rect',parent:group,attr:{x:this.coords2[i][j][0]-halfTickmarkSize,y:this.coords2[i][j][1]-halfTickmarkSize,width:prop.tickmarksSize,height:prop.tickmarksSize,fill:prop.tickmarks==='filledrect'?prop.colors[i]:fill,stroke:prop.colors[i],'stroke-width':prop.tickmarksLinewidth}});s.setAttribute('data-dataset',i);s.setAttribute('data-index',j);}}}};this.drawLabels=function()
28
+ {var angles=this.angles2,prop=this.properties,labels=prop.labels;for(var i=0,len=labels.length;i<len;++i){if(!labels[i]){continue;}
29
+ var endpoint=RG.SVG.TRIG.getRadiusEndPoint({angle:RG.SVG.TRIG.TWOPI/labels.length*i-RG.SVG.TRIG.HALFPI,r:this.radius+15});var x=endpoint[0]+this.centerx,y=endpoint[1]+this.centery;if((i/len)<0.5){halign='left';}else{halign='right';}
30
+ if((i/len)<0.25||(i/len)>0.75){valign='bottom';}else{valign='top';}
31
+ if((i/len)===0){halign='center';}
32
+ if((i/len)===0.25){valign='center';}
33
+ if((i/len)===0.5){halign='center';}
34
+ if((i/len)===0.75){valign='center';}
35
+ RG.SVG.text({object:this,svg:this.svg,parent:this.svg.all,text:labels[i],size:typeof prop.labelsSize==='number'?prop.labelsSize:prop.textSize,x:x,y:y,halign:halign,valign:'center',color:prop.labelsColor||prop.textColor,bold:typeof prop.labelsBold==='boolean'?prop.labelsBold:prop.textBold,italic:typeof prop.labelsItalic==='boolean'?prop.labelsItalic:prop.textItalic,font:prop.labelsFont||prop.textFont});}
36
+ if(prop.scaleVisible){for(var i=0;i<this.scale.labels.length;++i){var x=this.centerx;var y=this.centery-(this.radius/this.scale.labels.length*(i+1));RG.SVG.text({object:this,svg:this.svg,parent:this.svg.all,text:this.scale.labels[i],size:prop.scaleSize||prop.textSize-2,x:x,y:y,halign:'center',valign:'center',background:'rgba(255,255,255,0.7)',padding:2,color:prop.scaleColor||prop.textColor,bold:typeof prop.scaleBold==='boolean'?prop.scaleBold:prop.textBold,italic:typeof prop.scaleItalic==='boolean'?prop.scaleItalic:prop.textItalic,font:prop.scaleFont||prop.textFont});}
37
+ var str=RG.SVG.numberFormat({object:this,num:this.scale.min.toFixed(prop.scaleDecimals),prepend:prop.scaleUnitsPre,append:prop.scaleUnitsPost,point:prop.scalePoint,thousand:prop.scaleThousand,formatter:prop.scaleFormatter});RG.SVG.text({object:this,svg:this.svg,parent:this.svg.all,text:str,size:prop.scaleSize||prop.textSize-2,x:this.centerx,y:this.centery,halign:'center',valign:'center',background:'rgba(255,255,255,0.7)',padding:2,color:prop.scaleColor||prop.textColor,bold:typeof prop.scaleBold==='boolean'?prop.scaleBold:prop.textBold,italic:typeof prop.scaleItalic==='boolean'?prop.scaleItalic:prop.textItalic,font:prop.scaleFont||prop.textFont});}};this.highlight=function(circle)
38
+ {circle.setAttribute('fill',prop.highlightFill);circle.setAttribute('stroke',prop.highlightStroke);circle.setAttribute('stroke-width',prop.highlightLinewidth);this.highlight_node=circle;RG.SVG.REG.set('highlight',circle);};this.parseColors=function()
39
+ {if(!Object.keys(this.originalColors).length){this.originalColors={colors:RG.SVG.arrayClone(prop.colors),highlightFill:RG.SVG.arrayClone(prop.highlightFill)}}
40
+ var colors=prop.colors;if(colors){for(var i=0;i<colors.length;++i){colors[i]=RG.SVG.parseColorRadial({object:this,color:colors[i]});}}
41
+ prop.highlightFill=RG.SVG.parseColorRadial({object:this,color:prop.highlightFill});};this.getMaxValue=function()
42
+ {var max=0;if(prop.filled&&prop.filledAccumulative){this.max=RG.SVG.arrayMax(this.data[this.data.length-1]);}else{for(var dataset=0,max=0;dataset<this.data.length;++dataset){this.max=ma.max(this.max,RG.SVG.arrayMax(this.data[dataset]));}}};this.getRadius=function(value)
43
+ {return((value-prop.scaleMin)/(this.scale.max-prop.scaleMin))*this.radius;};this.addTooltipHotspots=function()
44
+ {if(prop.tooltips&&prop.tooltips.length>0){if(prop.tooltipsEvent!=='mousemove'){prop.tooltipsEvent='click';}
45
+ var group=RG.SVG.create({svg:this.svg,type:'g',parent:this.svg.all,attr:{className:'rgraph-radar-tooltip-hotspots'}});for(var dataset=0,seq=0;dataset<this.coords2.length;++dataset){for(var i=0;i<this.coords2[dataset].length;++i){var circle=RG.SVG.create({svg:this.svg,type:'circle',parent:group,attr:{cx:this.coords2[dataset][i][0],cy:this.coords2[dataset][i][1],r:prop.tickmarksSize,fill:'transparent',stroke:'transparent','stroke-width':0,'data-sequential-index':seq},style:{cursor:prop['tooltips'][seq]?'pointer':'default'}});(function(dataset,index,seq,obj)
46
+ {if(prop.tooltips[seq]){circle.addEventListener(prop.tooltipsEvent,function(e)
47
+ {var tooltip=RG.SVG.REG.get('tooltip');if(tooltip&&tooltip.__sequentialIndex__===seq){return;}
48
+ RG.SVG.tooltip({object:obj,dataset:dataset,index:index,sequentialIndex:seq,text:prop.tooltips[seq],event:e});obj.highlight(this);},false);if(prop.tooltipsEvent==='click'){circle.addEventListener('mousemove',function(e)
49
+ {e.target.style.cursor='pointer';},false);}}}(dataset,i,seq++,this));}}}};this.roundRobin=function()
50
+ {};this.on=function(type,func)
51
+ {if(type.substr(0,2)!=='on'){type='on'+type;}
52
+ RG.SVG.addCustomEventListener(this,type,func);return this;};this.exec=function(func)
53
+ {func(this);return this;};this.removeHighlight=this.hideHighlight=function()
54
+ {var highlight=RG.SVG.REG.get('highlight');if(highlight&&this.highlight_node){this.highlight_node.setAttribute('fill','transparent');this.highlight_node.setAttribute('stroke','transparent');RG.SVG.REG.set('highlight',null);}};this.trace=function()
55
+ {var opt=arguments[0]||{},frame=1,frames=opt.frames||120,obj=this
56
+ step=360/frames;this.isTrace=true;this.draw();var clipPath=RG.SVG.create({svg:this.svg,parent:this.svg.defs,type:'clipPath',attr:{id:'trace-effect-clip'}});clipPathArcPath=RG.SVG.TRIG.getArcPath2({cx:this.angles[0].cx,cy:this.angles[0].cy,r:this.angles[0].r*2,start:0,end:0});var clipPathArc=RG.SVG.create({svg:this.svg,parent:clipPath,type:'path',attr:{d:clipPathArcPath}});var iterator=function()
57
+ {var width=(frame++)/frames*obj.width;var deg=(360/frames)*frame++,rad=(RG.SVG.TRIG.TWOPI/360)*deg
58
+ clipPathArc.setAttribute('d',RG.SVG.TRIG.getArcPath2({cx:obj.angles[0].cx,cy:obj.angles[0].cy,r:obj.angles[0].r*2,start:0,end:rad}));if(frame<=frames){RG.SVG.FX.update(iterator);}else if(opt.callback){(opt.callback)(obj);}};iterator();return this;};for(i in conf.options){if(typeof i==='string'){this.set(i,conf.options[i]);}}};return this;})(window,document);