rgraph-rails 1.0.1

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +4 -0
  7. data/README.md +73 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/lib/rgraph-rails/version.rb +3 -0
  12. data/lib/rgraph-rails.rb +8 -0
  13. data/license.txt +19 -0
  14. data/rgraph-rails.gemspec +26 -0
  15. data/vendor/assets/images/bg.png +0 -0
  16. data/vendor/assets/images/bullet.png +0 -0
  17. data/vendor/assets/images/facebook-large.png +0 -0
  18. data/vendor/assets/images/google-plus-large.png +0 -0
  19. data/vendor/assets/images/logo.png +0 -0
  20. data/vendor/assets/images/meter-image-sd-needle.png +0 -0
  21. data/vendor/assets/images/meter-image-sd.png +0 -0
  22. data/vendor/assets/images/meter-sketch-needle.png +0 -0
  23. data/vendor/assets/images/meter-sketch.png +0 -0
  24. data/vendor/assets/images/odometer-background.png +0 -0
  25. data/vendor/assets/images/rgraph.jpg +0 -0
  26. data/vendor/assets/images/title.png +0 -0
  27. data/vendor/assets/images/twitter-large.png +0 -0
  28. data/vendor/assets/javascripts/RGraph.bar.js +3246 -0
  29. data/vendor/assets/javascripts/RGraph.bipolar.js +2003 -0
  30. data/vendor/assets/javascripts/RGraph.common.annotate.js +399 -0
  31. data/vendor/assets/javascripts/RGraph.common.context.js +600 -0
  32. data/vendor/assets/javascripts/RGraph.common.core.js +4751 -0
  33. data/vendor/assets/javascripts/RGraph.common.csv.js +275 -0
  34. data/vendor/assets/javascripts/RGraph.common.deprecated.js +454 -0
  35. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1194 -0
  36. data/vendor/assets/javascripts/RGraph.common.effects.js +1524 -0
  37. data/vendor/assets/javascripts/RGraph.common.key.js +735 -0
  38. data/vendor/assets/javascripts/RGraph.common.resizing.js +550 -0
  39. data/vendor/assets/javascripts/RGraph.common.tooltips.js +605 -0
  40. data/vendor/assets/javascripts/RGraph.common.zoom.js +223 -0
  41. data/vendor/assets/javascripts/RGraph.drawing.background.js +636 -0
  42. data/vendor/assets/javascripts/RGraph.drawing.circle.js +579 -0
  43. data/vendor/assets/javascripts/RGraph.drawing.image.js +810 -0
  44. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +710 -0
  45. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +672 -0
  46. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +568 -0
  47. data/vendor/assets/javascripts/RGraph.drawing.poly.js +623 -0
  48. data/vendor/assets/javascripts/RGraph.drawing.rect.js +603 -0
  49. data/vendor/assets/javascripts/RGraph.drawing.text.js +648 -0
  50. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +815 -0
  51. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +860 -0
  52. data/vendor/assets/javascripts/RGraph.fuel.js +965 -0
  53. data/vendor/assets/javascripts/RGraph.funnel.js +988 -0
  54. data/vendor/assets/javascripts/RGraph.gantt.js +1242 -0
  55. data/vendor/assets/javascripts/RGraph.gauge.js +1391 -0
  56. data/vendor/assets/javascripts/RGraph.hbar.js +1794 -0
  57. data/vendor/assets/javascripts/RGraph.hprogress.js +1307 -0
  58. data/vendor/assets/javascripts/RGraph.line.js +3940 -0
  59. data/vendor/assets/javascripts/RGraph.meter.js +1242 -0
  60. data/vendor/assets/javascripts/RGraph.modaldialog.js +292 -0
  61. data/vendor/assets/javascripts/RGraph.odo.js +1265 -0
  62. data/vendor/assets/javascripts/RGraph.pie.js +1979 -0
  63. data/vendor/assets/javascripts/RGraph.radar.js +1840 -0
  64. data/vendor/assets/javascripts/RGraph.rose.js +1860 -0
  65. data/vendor/assets/javascripts/RGraph.rscatter.js +1332 -0
  66. data/vendor/assets/javascripts/RGraph.scatter.js +3029 -0
  67. data/vendor/assets/javascripts/RGraph.thermometer.js +1131 -0
  68. data/vendor/assets/javascripts/RGraph.vprogress.js +1326 -0
  69. data/vendor/assets/javascripts/RGraph.waterfall.js +1252 -0
  70. data/vendor/assets/javascripts/financial-data.js +1067 -0
  71. data/vendor/assets/stylesheets/ModalDialog.css +90 -0
  72. data/vendor/assets/stylesheets/animations.css +3347 -0
  73. data/vendor/assets/stylesheets/website.css +402 -0
  74. metadata +175 -0
@@ -0,0 +1,1860 @@
1
+ // version: 2015-11-02
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 dual licensed under the Open Source GPL (General Public License) |
9
+ * | v2.0 license and a commercial license which means that you're not bound by |
10
+ * | the terms of the GPL. The commercial license is just �99 (GBP) and you can |
11
+ * | read about it here: |
12
+ * | http://www.rgraph.net/license |
13
+ * o--------------------------------------------------------------------------------o
14
+ */
15
+
16
+ RGraph = window.RGraph || {isRGraph: true};
17
+ RGraph.Effects = RGraph.Effects || {};
18
+ RGraph.Effects.Rose = RGraph.Effects.Rose || {};
19
+
20
+ /**
21
+ * The rose chart constuctor
22
+ *
23
+ * @param object canvas
24
+ * @param array data
25
+ */
26
+ RGraph.Rose = function (conf)
27
+ {
28
+ /**
29
+ * Allow for object config style
30
+ */
31
+ if ( typeof conf === 'object'
32
+ && typeof conf.data === 'object'
33
+ && typeof conf.id === 'string') {
34
+
35
+ var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
36
+
37
+ } else {
38
+
39
+ var conf = {id: conf};
40
+ conf.data = arguments[1];
41
+ }
42
+
43
+
44
+
45
+
46
+ this.id = conf.id;
47
+ this.canvas = document.getElementById(this.id);
48
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
49
+ this.data = conf.data;
50
+ this.canvas.__object__ = this;
51
+ this.type = 'rose';
52
+ this.isRGraph = true;
53
+ this.uid = RGraph.CreateUID();
54
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
55
+ this.colorsParsed = false;
56
+ this.coordsText = [];
57
+ this.original_colors = [];
58
+ this.firstDraw = true; // After the first draw this will be false
59
+
60
+ this.centerx = 0;
61
+ this.centery = 0;
62
+ this.radius = 0;
63
+ this.max = 0;
64
+ this.angles = [];
65
+ this.angles2 = [];
66
+
67
+ this.properties =
68
+ {
69
+ 'chart.background.axes': true,
70
+ 'chart.background.axes.color': 'black',
71
+ 'chart.background.grid': true,
72
+ 'chart.background.grid.color': '#ccc',
73
+ 'chart.background.grid.size': null,
74
+ 'chart.background.grid.radials':null,
75
+ 'chart.background.grid.count': 5,
76
+ 'chart.centerx': null,
77
+ 'chart.centery': null,
78
+ 'chart.radius': null,
79
+ 'chart.angles.start': 0,
80
+ 'chart.colors': ['rgba(255,0,0,0.5)', 'rgba(255,255,0,0.5)', 'rgba(0,255,255,0.5)', 'rgb(0,255,0)', 'gray', 'blue', 'rgb(255,128,255)','green', 'pink', 'gray', 'aqua'],
81
+ 'chart.linewidth': 1,
82
+ 'chart.colors.sequential': false,
83
+ 'chart.colors.alpha': null,
84
+ 'chart.margin': 0,
85
+ 'chart.strokestyle': '#aaa',
86
+ 'chart.gutter.left': 25,
87
+ 'chart.gutter.right': 25,
88
+ 'chart.gutter.top': 25,
89
+ 'chart.gutter.bottom': 25,
90
+ 'chart.title': '',
91
+ 'chart.title.background': null,
92
+ 'chart.title.hpos': null,
93
+ 'chart.title.vpos': null,
94
+ 'chart.title.bold': true,
95
+ 'chart.title.font': null,
96
+ 'chart.title.x': null,
97
+ 'chart.title.y': null,
98
+ 'chart.title.halign': null,
99
+ 'chart.title.valign': null,
100
+ 'chart.labels': null,
101
+ 'chart.labels.color': null,
102
+ 'chart.labels.position': 'center',
103
+ 'chart.labels.axes': 'nsew',
104
+ 'chart.labels.boxed': false,
105
+ 'chart.labels.offset': 0,
106
+ 'chart.text.color': 'black',
107
+ 'chart.text.font': 'Arial',
108
+ 'chart.text.size': 12,
109
+ 'chart.key': null,
110
+ 'chart.key.background': 'white',
111
+ 'chart.key.position': 'graph',
112
+ 'chart.key.halign': 'right',
113
+ 'chart.key.shadow': false,
114
+ 'chart.key.shadow.color': '#666',
115
+ 'chart.key.shadow.blur': 3,
116
+ 'chart.key.shadow.offsetx': 2,
117
+ 'chart.key.shadow.offsety': 2,
118
+ 'chart.key.position.gutter.boxed': false,
119
+ 'chart.key.position.x': null,
120
+ 'chart.key.position.y': null,
121
+ 'chart.key.color.shape': 'square',
122
+ 'chart.key.rounded': true,
123
+ 'chart.key.linewidth': 1,
124
+ 'chart.key.colors': null,
125
+ 'chart.key.interactive': false,
126
+ 'chart.key.interactive.highlight.chart.stroke': 'black',
127
+ 'chart.key.interactive.highlight.chart.fill': 'rgba(255,255,255,0.7)',
128
+ 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
129
+ 'chart.key.text.color': 'black',
130
+ 'chart.contextmenu': null,
131
+ 'chart.tooltips': null,
132
+ 'chart.tooltips.event': 'onclick',
133
+ 'chart.tooltips.effect': 'fade',
134
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
135
+ 'chart.tooltips.highlight': true,
136
+ 'chart.highlight.stroke': 'rgba(0,0,0,0)',
137
+ 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
138
+ 'chart.annotatable': false,
139
+ 'chart.annotate.color': 'black',
140
+ 'chart.zoom.factor': 1.5,
141
+ 'chart.zoom.fade.in': true,
142
+ 'chart.zoom.fade.out': true,
143
+ 'chart.zoom.hdir': 'right',
144
+ 'chart.zoom.vdir': 'down',
145
+ 'chart.zoom.frames': 25,
146
+ 'chart.zoom.delay': 16.666,
147
+ 'chart.zoom.shadow': true,
148
+ 'chart.zoom.background': true,
149
+ 'chart.zoom.action': 'zoom',
150
+ 'chart.resizable': false,
151
+ 'chart.resize.handle.adjust': [0,0],
152
+ 'chart.resize.handle.background': null,
153
+ 'chart.adjustable': false,
154
+ 'chart.ymax': null,
155
+ 'chart.ymin': 0,
156
+ 'chart.scale.decimals': null,
157
+ 'chart.scale.point': '.',
158
+ 'chart.scale.thousand': ',',
159
+ 'chart.variant': 'stacked',
160
+ 'chart.variant.threed.depth': 10,
161
+ 'chart.exploded': 0,
162
+ 'chart.events.mousemove': null,
163
+ 'chart.events.click': null,
164
+ 'chart.animation.roundrobin.factor': 1,
165
+ 'chart.animation.roundrobin.radius': true,
166
+ 'chart.animation.grow.multiplier': 1,
167
+ 'chart.labels.count': 5,
168
+ 'chart.segment.highlight': false,
169
+ 'chart.segment.highlight.count': null,
170
+ 'chart.segment.highlight.fill': 'rgba(0,255,0,0.5)',
171
+ 'chart.segment.highlight.stroke': 'rgba(0,0,0,0)'
172
+ }
173
+
174
+
175
+
176
+ /**
177
+ * Create the $ objects. In the case of non-equi-angular rose charts it actually creates too many $ objects,
178
+ * but it doesn't matter.
179
+ */
180
+ var linear_data = RGraph.arrayLinearize(this.data);
181
+ for (var i=0; i<linear_data.length; ++i) {
182
+ this["$" + i] = {}
183
+ }
184
+
185
+
186
+ /**
187
+ * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
188
+ * done already
189
+ */
190
+ if (!this.canvas.__rgraph_aa_translated__) {
191
+ this.context.translate(0.5,0.5);
192
+
193
+ this.canvas.__rgraph_aa_translated__ = true;
194
+ }
195
+
196
+
197
+
198
+
199
+ // Short variable names
200
+ var RG = RGraph,
201
+ ca = this.canvas,
202
+ co = ca.getContext('2d'),
203
+ prop = this.properties,
204
+ pa = RG.Path,
205
+ pa2 = RG.path2,
206
+ win = window,
207
+ doc = document,
208
+ ma = Math
209
+
210
+
211
+
212
+ /**
213
+ * "Decorate" the object with the generic effects if the effects library has been included
214
+ */
215
+ if (RG.Effects && typeof RG.Effects.decorate === 'function') {
216
+ RG.Effects.decorate(this);
217
+ }
218
+
219
+
220
+
221
+
222
+ /**
223
+ * A simple setter
224
+ *
225
+ * @param string name The name of the property to set
226
+ * @param string value The value of the property
227
+ */
228
+ this.set =
229
+ this.Set = function (name)
230
+ {
231
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
232
+
233
+ /**
234
+ * the number of arguments is only one and it's an
235
+ * object - parse it for configuration data and return.
236
+ */
237
+ if (arguments.length === 1 && typeof name === 'object') {
238
+ RG.parseObjectStyleConfig(this, name);
239
+ return this;
240
+ }
241
+
242
+
243
+ /**
244
+ * This should be done first - prepend the propertyy name with "chart." if necessary
245
+ */
246
+ if (name.substr(0,6) != 'chart.') {
247
+ name = 'chart.' + name;
248
+ }
249
+
250
+
251
+
252
+
253
+ // Convert uppercase letters to dot+lower case letter
254
+ name = name.replace(/([A-Z])/g, function (str)
255
+ {
256
+ return '.' + String(RegExp.$1).toLowerCase();
257
+ });
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+ // A little BC...
268
+ if (name === 'chart.background.grid.spokes') name = 'chart.background.grid.radials';
269
+
270
+
271
+
272
+ //
273
+ // Change chart.segments.highlight* to chart.segment.highlight (no plural)
274
+ //
275
+ if (name === 'chart.segments.highlight') name = 'chart.segment.highlight';
276
+ if (name === 'chart.segments.highlight.fill') name = 'chart.segment.highlight.fill';
277
+ if (name === 'chart.segments.highlight.stroke') name = 'chart.segment.highlight.stroke';
278
+
279
+ prop[name.toLowerCase()] = value;
280
+
281
+
282
+ return this;
283
+ };
284
+
285
+
286
+
287
+
288
+ /**
289
+ * A simple getter
290
+ *
291
+ * @param string name The name of the property to get
292
+ */
293
+ this.get =
294
+ this.Get = function (name)
295
+ {
296
+ /**
297
+ * This should be done first - prepend the property name with "chart." if necessary
298
+ */
299
+ if (name.substr(0,6) != 'chart.') {
300
+ name = 'chart.' + name;
301
+ }
302
+
303
+ // Convert uppercase letters to dot+lower case letter
304
+ name = name.replace(/([A-Z])/g, function (str)
305
+ {
306
+ return '.' + String(RegExp.$1).toLowerCase()
307
+ });
308
+
309
+ return prop[name.toLowerCase()];
310
+ };
311
+
312
+
313
+
314
+
315
+ /**
316
+ * This method draws the rose chart
317
+ */
318
+ this.draw =
319
+ this.Draw = function ()
320
+ {
321
+
322
+ /**
323
+ * Fire the onbeforedraw event
324
+ */
325
+ RG.fireCustomEvent(this, 'onbeforedraw');
326
+
327
+
328
+
329
+ /**
330
+ * This doesn't affect the chart, but is used for compatibility
331
+ */
332
+ this.gutterLeft = prop['chart.gutter.left'];
333
+ this.gutterRight = prop['chart.gutter.right'];
334
+ this.gutterTop = prop['chart.gutter.top'];
335
+ this.gutterBottom = prop['chart.gutter.bottom'];
336
+
337
+ // Calculate the radius
338
+ this.radius = (ma.min(ca.width - this.gutterLeft - this.gutterRight, ca.height - this.gutterTop - this.gutterBottom) / 2);
339
+ this.centerx = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
340
+ this.centery = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
341
+ this.angles = [];
342
+ this.angles2 = [];
343
+ this.total = 0;
344
+ this.startRadians = prop['chart.angles.start'];
345
+ this.coordsText = [];
346
+
347
+ /**
348
+ * Change the centerx marginally if the key is defined
349
+ */
350
+ if (prop['chart.key'] && prop['chart.key'].length > 0 && prop['chart.key'].length >= 3) {
351
+ this.centerx = this.centerx - this.gutterRight + 5;
352
+ }
353
+
354
+
355
+
356
+ // User specified radius, centerx and centery
357
+ if (typeof prop['chart.centerx'] == 'number') this.centerx = prop['chart.centerx'];
358
+ if (typeof prop['chart.centery'] == 'number') this.centery = prop['chart.centery'];
359
+ if (typeof prop['chart.radius'] == 'number') this.radius = prop['chart.radius'];
360
+
361
+ /**
362
+ * Parse the colors for gradients. Its down here so that the center X/Y can be used
363
+ */
364
+ if (!this.colorsParsed) {
365
+
366
+ this.parseColors();
367
+
368
+ // Don't want to do this again
369
+ this.colorsParsed = true;
370
+ }
371
+
372
+
373
+
374
+ // 3D variant
375
+ if (prop['chart.variant'].indexOf('3d') !== -1) {
376
+
377
+ var scaleX = 1.5;
378
+
379
+ this.context.setTransform(
380
+ scaleX,
381
+ 0,
382
+ 0,
383
+ 1,
384
+ (ca.width * scaleX - ca.width) * -0.5,
385
+ 0
386
+ );
387
+ }
388
+
389
+
390
+
391
+
392
+
393
+ this.drawBackground();
394
+
395
+
396
+
397
+
398
+
399
+ // If a 3D variant draw the depth
400
+ if (prop['chart.variant'].indexOf('3d') !== -1) {
401
+
402
+ RG.setShadow(this,'rgba(0,0,0,0.35)',0,15,25);
403
+
404
+ for (var i=prop['chart.variant.threed.depth']; i>0; i-=1) {
405
+
406
+ this.centery -= 1;
407
+ this.drawRose({storeAngles: false});
408
+
409
+ RG.setShadow(this,'rgba(0,0,0,0)',0,0,0);
410
+
411
+
412
+ // Make the segments darker
413
+ for (var j=0,len=this.angles.length; j<len; j+=1) {
414
+
415
+ var a = this.angles[j];
416
+
417
+ pa(co, [
418
+ 'b',
419
+ 'm', a[4], a[5],
420
+ 'a', a[4], a[5], a[3] + 1.5, a[0] - 0.01, a[1] + 0.01, false,
421
+ 'c',
422
+ 'f', 'rgba(0,0,0,0.1)'
423
+ ]);
424
+ }
425
+ }
426
+ }
427
+
428
+ this.drawRose();
429
+ this.drawLabels();
430
+
431
+ /**
432
+ * Set the strokestyle to transparent because of a strange double stroke bug
433
+ *
434
+ * DO NOT REMOVE
435
+ */
436
+ co.strokeStyle = 'rgba(0,0,0,0)'
437
+
438
+
439
+ /**
440
+ * Setup the context menu if required
441
+ */
442
+ if (prop['chart.contextmenu']) {
443
+ RG.ShowContext(this);
444
+ }
445
+
446
+
447
+ /**
448
+ * This function enables resizing
449
+ */
450
+ if (prop['chart.resizable']) {
451
+ RG.AllowResizing(this);
452
+ }
453
+
454
+
455
+ /**
456
+ * This function enables adjusting
457
+ */
458
+ if (prop['chart.adjustable']) {
459
+ RG.AllowAdjusting(this);
460
+ }
461
+
462
+
463
+ /**
464
+ * This installs the event listeners
465
+ */
466
+ RG.InstallEventListeners(this);
467
+
468
+
469
+
470
+
471
+
472
+ //
473
+ // Allow the segments to be highlighted
474
+ //
475
+ if (prop['chart.segment.highlight']) {
476
+
477
+ // Check to see if the dynamic library has been included
478
+ if (!RG.allowSegmentHighlight) {
479
+ alert('[WARNING] The segment highlight function does not exist - have you included the dynamic library?');
480
+ }
481
+
482
+ RG.allowSegmentHighlight({
483
+ object: this,
484
+ count: typeof prop['chart.segment.highlight.count'] === 'number' ? prop['chart.segment.highlight.count'] : this.data.length,
485
+ fill: prop['chart.segment.highlight.fill'],
486
+ stroke: prop['chart.segment.highlight.stroke']
487
+ });
488
+ }
489
+
490
+
491
+
492
+ /**
493
+ * Fire the onfirstdraw event
494
+ */
495
+ if (this.firstDraw) {
496
+ RG.fireCustomEvent(this, 'onfirstdraw');
497
+ this.firstDraw = false;
498
+ this.firstDrawFunc();
499
+ }
500
+
501
+
502
+
503
+ /**
504
+ * Fire the RGraph ondraw event
505
+ */
506
+ RG.FireCustomEvent(this, 'ondraw');
507
+
508
+ return this;
509
+ };
510
+
511
+
512
+
513
+
514
+ /**
515
+ * This method draws the rose charts background
516
+ */
517
+ this.drawBackground =
518
+ this.DrawBackground = function ()
519
+ {
520
+ co.lineWidth = 1;
521
+
522
+
523
+ // Draw the background grey circles/spokes
524
+ if (prop['chart.background.grid']) {
525
+ if (typeof(prop['chart.background.grid.count']) == 'number') {
526
+ prop['chart.background.grid.size'] = this.radius / prop['chart.background.grid.count'];
527
+ }
528
+
529
+ co.beginPath();
530
+ co.strokeStyle = prop['chart.background.grid.color'];
531
+
532
+ // Radius must be greater than 0 for Opera to work
533
+ for (var i=prop['chart.background.grid.size']; i<=this.radius; i+=prop['chart.background.grid.size']) {
534
+
535
+ // Hmmm... This is questionable
536
+ co.moveTo(this.centerx + i, this.centery);
537
+
538
+ // Radius must be greater than 0 for Opera to work
539
+ co.arc(this.centerx,
540
+ this.centery,
541
+ i,
542
+ 0,
543
+ RG.TWOPI,
544
+ false);
545
+ }
546
+ co.stroke();
547
+
548
+
549
+
550
+
551
+
552
+
553
+ // Draw the background lines that go from the center outwards
554
+ co.beginPath();
555
+ if (typeof prop['chart.background.grid.radials'] !== 'number') {
556
+ prop['chart.background.grid.radials'] = this.data.length
557
+ }
558
+
559
+ var num = (360 / prop['chart.background.grid.radials']);
560
+
561
+ for (var i=num; i<=360; i+=num) {
562
+
563
+ // Radius must be greater than 0 for Opera to work
564
+ co.arc(this.centerx,
565
+ this.centery,
566
+ this.radius,
567
+ ((i / (180 / RG.PI)) - RG.HALFPI) + this.startRadians,
568
+ (((i + 0.0001) / (180 / RG.PI)) - RG.HALFPI) + this.startRadians,
569
+ false);
570
+
571
+ co.lineTo(this.centerx, this.centery);
572
+ }
573
+ co.stroke();
574
+ }
575
+
576
+
577
+
578
+ if (prop['chart.background.axes']) {
579
+ co.beginPath();
580
+ co.strokeStyle = prop['chart.background.axes.color'];
581
+
582
+ // Draw the X axis
583
+ co.moveTo(this.centerx - this.radius, Math.round(this.centery) );
584
+ co.lineTo(this.centerx + this.radius, Math.round(this.centery) );
585
+
586
+ // Draw the X ends
587
+ co.moveTo(Math.round(this.centerx - this.radius), this.centery - 5);
588
+ co.lineTo(Math.round(this.centerx - this.radius), this.centery + 5);
589
+ co.moveTo(Math.round(this.centerx + this.radius), this.centery - 5);
590
+ co.lineTo(Math.round(this.centerx + this.radius), this.centery + 5);
591
+
592
+ // Draw the X check marks
593
+ for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=(this.radius / 5)) {
594
+ co.moveTo(Math.round(i), this.centery - 3);
595
+ co.lineTo(Math.round(i), this.centery + 3.5);
596
+ }
597
+
598
+ // Draw the Y check marks
599
+ for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=(this.radius / 5)) {
600
+ co.moveTo(this.centerx - 3, Math.round(i));
601
+ co.lineTo(this.centerx + 3, Math.round(i));
602
+ }
603
+
604
+ // Draw the Y axis
605
+ co.moveTo(ma.round(this.centerx), this.centery - this.radius);
606
+ co.lineTo(ma.round(this.centerx), this.centery + this.radius);
607
+
608
+ // Draw the Y ends
609
+ co.moveTo(this.centerx - 5, ma.round(this.centery - this.radius));
610
+ co.lineTo(this.centerx + 5, ma.round(this.centery - this.radius));
611
+
612
+ co.moveTo(this.centerx - 5, ma.round(this.centery + this.radius));
613
+ co.lineTo(this.centerx + 5, ma.round(this.centery + this.radius));
614
+
615
+ // Stroke it
616
+ co.closePath();
617
+ co.stroke();
618
+ }
619
+ };
620
+
621
+
622
+
623
+
624
+ /**
625
+ * This method draws the data on the graph
626
+ */
627
+ this.drawRose =
628
+ this.DrawRose = function ()
629
+ {
630
+ var max = 0,
631
+ data = this.data,
632
+ margin = RG.degrees2Radians(prop['chart.margin']),
633
+ opt = arguments[0] || {}
634
+
635
+ co.lineWidth = prop['chart.linewidth'];
636
+
637
+ // Work out the maximum value and the sum
638
+ if (RG.isNull(prop['chart.ymax'])) {
639
+
640
+ // Work out the max
641
+ for (var i=0; i<data.length; ++i) {
642
+ if (typeof data[i] == 'number') {
643
+ max = ma.max(max, data[i]);
644
+ } else if (typeof data[i] == 'object' && prop['chart.variant'].indexOf('non-equi-angular') !== -1) {
645
+ max = ma.max(max, data[i][0]);
646
+
647
+ // Fallback is stacked
648
+ } else {
649
+ max = ma.max(max, RG.arraySum(data[i]));
650
+ }
651
+ }
652
+
653
+ this.scale2 = RG.getScale2(this, {
654
+ 'max':max,
655
+ 'min':0,
656
+ 'scale.thousand':prop['chart.scale.thousand'],
657
+ 'scale.point':prop['chart.scale.point'],
658
+ 'scale.decimals':prop['chart.scale.decimals'],
659
+ 'ylabels.count':prop['chart.labels.count'],
660
+ 'scale.round':prop['chart.scale.round'],
661
+ 'units.pre': prop['chart.units.pre'],
662
+ 'units.post': prop['chart.units.post']
663
+ });
664
+ this.max = this.scale2.max;
665
+
666
+ } else {
667
+
668
+ var ymax = prop['chart.ymax'];
669
+
670
+
671
+
672
+ this.scale2 = RG.getScale2(this, {
673
+ 'max':ymax,
674
+ 'strict':true,
675
+ 'scale.thousand':prop['chart.scale.thousand'],
676
+ 'scale.point':prop['chart.scale.point'],
677
+ 'scale.decimals':prop['chart.scale.decimals'],
678
+ 'ylabels.count':prop['chart.labels.count'],
679
+ 'scale.round':prop['chart.scale.round'],
680
+ 'units.pre': prop['chart.units.pre'],
681
+ 'units.post': prop['chart.units.post']
682
+ });
683
+ this.max = this.scale2.max
684
+ }
685
+
686
+ this.sum = RG.array_sum(data);
687
+
688
+ // Move to the centre
689
+ co.moveTo(this.centerx, this.centery);
690
+
691
+ co.stroke(); // Stroke the background so it stays grey
692
+
693
+ // Transparency
694
+ if (prop['chart.colors.alpha']) {
695
+ co.globalAlpha = prop['chart.colors.alpha'];
696
+ }
697
+
698
+ var sequentialIndex = 0;
699
+
700
+ /*******************************************************
701
+ * A non-equi-angular Rose chart
702
+ *******************************************************/
703
+ if (typeof(prop['chart.variant']) == 'string' && prop['chart.variant'].indexOf('non-equi-angular') !== -1) {
704
+
705
+ var total=0;
706
+ for (var i=0; i<data.length; ++i) {
707
+ total += data[i][1];
708
+ }
709
+
710
+
711
+ for (var i=0; i<this.data.length; ++i) {
712
+
713
+ var segmentRadians = ((this.data[i][1] / total) * RG.TWOPI);
714
+ var radius = ((this.data[i][0] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius;
715
+ radius = radius * prop['chart.animation.grow.multiplier'];
716
+
717
+ co.strokeStyle = prop['chart.strokestyle'];
718
+ co.fillStyle = prop['chart.colors'][0];
719
+
720
+ if (prop['chart.colors.sequential']) {
721
+ co.fillStyle = prop['chart.colors'][i];
722
+ }
723
+
724
+ co.beginPath(); // Begin the segment
725
+
726
+ var startAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) - RG.HALFPI + margin;
727
+ var endAngle = ((this.startRadians + segmentRadians) * prop['chart.animation.roundrobin.factor']) - RG.HALFPI - margin;
728
+
729
+ var exploded = this.getexploded(i, startAngle, endAngle, prop['chart.exploded']);
730
+ var explodedX = exploded[0];
731
+ var explodedY = exploded[1];
732
+
733
+
734
+ co.arc(
735
+ this.centerx + explodedX,
736
+ this.centery + explodedY,
737
+ prop['chart.animation.roundrobin.radius'] ? radius * prop['chart.animation.roundrobin.factor'] : radius,
738
+ startAngle,
739
+ endAngle,
740
+ 0
741
+ );
742
+ co.lineTo(this.centerx + explodedX, this.centery + explodedY);
743
+ co.closePath(); // End the segment
744
+
745
+ co.stroke();
746
+ co.fill();
747
+
748
+ // Store the start and end angles
749
+
750
+ this.angles[i] = [
751
+ startAngle,
752
+ endAngle,
753
+ 0,
754
+ radius,
755
+ this.centerx + explodedX,
756
+ this.centery + explodedY
757
+ ];
758
+
759
+ sequentialIndex++;
760
+ this.startRadians += segmentRadians;
761
+ }
762
+ } else {
763
+
764
+ var sequentialColorIndex = 0;
765
+
766
+ /*******************************************************
767
+ * Draw regular segments here
768
+ *******************************************************/
769
+ for (var i=0; i<this.data.length; ++i) {
770
+
771
+ var segmentRadians = (1 / this.data.length) * RG.TWOPI;
772
+
773
+ if (typeof this.data[i] == 'number') {
774
+ co.beginPath(); // Begin the segment
775
+
776
+ co.strokeStyle = prop['chart.strokestyle'];
777
+ co.fillStyle = prop['chart.colors'][0];
778
+
779
+ /*******************************************************
780
+ * This allows sequential colors
781
+ *******************************************************/
782
+ if (prop['chart.colors.sequential']) {
783
+ co.fillStyle = prop['chart.colors'][i];
784
+ }
785
+
786
+ var radius = ((this.data[i] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius;
787
+ radius = radius * prop['chart.animation.grow.multiplier'];
788
+
789
+ var startAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) - RG.HALFPI + margin;
790
+ var endAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) + (segmentRadians * prop['chart.animation.roundrobin.factor']) - RG.HALFPI - margin;
791
+
792
+ var exploded = this.getexploded(i, startAngle, endAngle, prop['chart.exploded']);
793
+ var explodedX = exploded[0];
794
+ var explodedY = exploded[1];
795
+
796
+ co.arc(this.centerx + explodedX,
797
+ this.centery + explodedY,
798
+ prop['chart.animation.roundrobin.radius'] ? radius * prop['chart.animation.roundrobin.factor'] : radius,
799
+ startAngle,
800
+ endAngle,
801
+ 0);
802
+ co.lineTo(this.centerx + explodedX, this.centery + explodedY);
803
+ co.closePath(); // End the segment
804
+ co.stroke();
805
+ co.fill();
806
+
807
+ // This skirts a double-stroke bug
808
+ co.beginPath();
809
+
810
+ if (endAngle == 0) {
811
+ //endAngle = RG.TWOPI;
812
+ }
813
+
814
+ // Store the start and end angles
815
+ this.angles[i] = [
816
+ startAngle,
817
+ endAngle,
818
+ 0,
819
+ radius * prop['chart.animation.roundrobin.factor'],
820
+ this.centerx + explodedX,
821
+ this.centery + explodedY
822
+ ];
823
+
824
+ sequentialIndex++;
825
+
826
+ /*******************************************************
827
+ * Draw a stacked segment
828
+ *******************************************************/
829
+ } else if (typeof(this.data[i]) == 'object') {
830
+
831
+ var margin = prop['chart.margin'] / (180 / RG.PI);
832
+
833
+
834
+ // Initialise the angles2 array
835
+ if (!this.angles2[i]) {
836
+ this.angles2[i] = [];
837
+ }
838
+
839
+
840
+ for (var j=0; j<this.data[i].length; ++j) {
841
+
842
+ var startAngle = (this.startRadians * prop['chart.animation.roundrobin.factor']) - RG.HALFPI + margin;
843
+ var endAngle = (this.startRadians * prop['chart.animation.roundrobin.factor'])+ (segmentRadians * prop['chart.animation.roundrobin.factor']) - RG.HALFPI - margin;
844
+
845
+ var exploded = this.getexploded(i, startAngle, endAngle, prop['chart.exploded']);
846
+ var explodedX = exploded[0];
847
+ var explodedY = exploded[1];
848
+
849
+ co.strokeStyle = prop['chart.strokestyle'];
850
+ co.fillStyle = prop['chart.colors'][j];
851
+
852
+ // This facilitates sequential color support
853
+ if (prop['chart.colors.sequential']) {
854
+ co.fillStyle = prop['chart.colors'][sequentialColorIndex++];
855
+ }
856
+
857
+ if (j == 0) {
858
+ co.beginPath(); // Begin the segment
859
+ var startRadius = 0;
860
+ var endRadius = ((this.data[i][j] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius;
861
+ endRadius = endRadius * prop['chart.animation.grow.multiplier'];
862
+
863
+ co.arc(this.centerx + explodedX,
864
+ this.centery + explodedY,
865
+ prop['chart.animation.roundrobin.radius'] ? endRadius * prop['chart.animation.roundrobin.factor'] : endRadius,
866
+ startAngle,
867
+ endAngle,
868
+ 0);
869
+ co.lineTo(this.centerx + explodedX, this.centery + explodedY);
870
+ co.closePath(); // End the segment
871
+ co.stroke();
872
+ co.fill();
873
+
874
+ this.angles[sequentialIndex++] = [
875
+ startAngle,
876
+ endAngle,
877
+ 0,
878
+ endRadius * prop['chart.animation.roundrobin.factor'],
879
+ this.centerx + explodedX,
880
+ this.centery + explodedY
881
+ ];
882
+
883
+ this.angles2[i][j] = [
884
+ startAngle,
885
+ endAngle,
886
+ 0,
887
+ endRadius * prop['chart.animation.roundrobin.factor'],
888
+ this.centerx + explodedX,
889
+ this.centery + explodedY
890
+ ];
891
+
892
+ } else {
893
+
894
+ co.beginPath(); // Begin the segment
895
+
896
+ var startRadius = endRadius; // This comes from the prior iteration of this loop
897
+ var endRadius = (((this.data[i][j] - prop['chart.ymin']) / (this.max - prop['chart.ymin'])) * this.radius) + startRadius;
898
+ endRadius = endRadius * prop['chart.animation.grow.multiplier'];
899
+
900
+ co.arc(this.centerx + explodedX,
901
+ this.centery + explodedY,
902
+ startRadius * prop['chart.animation.roundrobin.factor'],
903
+ startAngle,
904
+ endAngle,
905
+ 0);
906
+
907
+ co.arc(this.centerx + explodedX,
908
+ this.centery + explodedY,
909
+ endRadius * prop['chart.animation.roundrobin.factor'],
910
+ endAngle,
911
+ startAngle,
912
+ true);
913
+
914
+ co.closePath(); // End the segment
915
+ co.stroke();
916
+ co.fill();
917
+
918
+
919
+ this.angles[sequentialIndex++] = [
920
+ startAngle,
921
+ endAngle,
922
+ startRadius * prop['chart.animation.roundrobin.factor'],
923
+ endRadius * prop['chart.animation.roundrobin.factor'],
924
+ this.centerx + explodedX,
925
+ this.centery + explodedY
926
+ ];
927
+
928
+ this.angles2[i][j] = [
929
+ startAngle,
930
+ endAngle,
931
+ startRadius * prop['chart.animation.roundrobin.factor'],
932
+ endRadius * prop['chart.animation.roundrobin.factor'],
933
+ this.centerx + explodedX,
934
+ this.centery + explodedY
935
+ ];
936
+ }
937
+ }
938
+ }
939
+
940
+ this.startRadians += segmentRadians;
941
+ }
942
+ }
943
+
944
+ // Turn off the transparency
945
+ if (prop['chart.colors.alpha']) {
946
+ co.globalAlpha = 1;
947
+ }
948
+
949
+ // Draw the title if any has been set
950
+ if (prop['chart.title']) {
951
+ RG.drawTitle(
952
+ this,
953
+ prop['chart.title'],
954
+ (ca.height / 2) - this.radius,
955
+ this.centerx,
956
+ prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2
957
+ );
958
+ }
959
+ };
960
+
961
+
962
+
963
+
964
+ /**
965
+ * Unsuprisingly, draws the labels
966
+ */
967
+ this.drawLabels =
968
+ this.DrawLabels = function ()
969
+ {
970
+ co.lineWidth = 1;
971
+ var key = prop['chart.key'];
972
+
973
+ if (key && key.length) {
974
+ RG.DrawKey(this, key, prop['chart.colors']);
975
+ }
976
+
977
+ // Set the color to black
978
+ co.fillStyle = prop['chart.text.color'];
979
+ co.strokeStyle = 'black';
980
+
981
+ var radius = this.radius,
982
+ font = prop['chart.text.font'],
983
+ size = prop['chart.text.size'],
984
+ axes = prop['chart.labels.axes'].toLowerCase(),
985
+ decimals = prop['chart.scale.decimals'],
986
+ units_pre = prop['chart.units.pre'],
987
+ units_post = prop['chart.units.post'],
988
+ centerx = this.centerx,
989
+ centery = this.centery + (prop['chart.variant'].indexOf('3d') !== -1 ? prop['chart.variant.threed.depth'] : 0);
990
+
991
+ // Draw any circular labels
992
+ if (typeof prop['chart.labels'] == 'object' && prop['chart.labels']) {
993
+ this.DrawCircularLabels(co, prop['chart.labels'], font, size, radius + 10);
994
+ }
995
+
996
+
997
+ // Size can be specified seperately for the scale now
998
+ if (typeof(prop['chart.text.size.scale']) == 'number') {
999
+ size = prop['chart.text.size.scale'];
1000
+ }
1001
+
1002
+
1003
+ var color = 'rgba(255,255,255,0.8)';
1004
+
1005
+ // The "North" axis labels
1006
+ if (axes.indexOf('n') > -1) {
1007
+ for (var i=0; i<prop['chart.labels.count']; ++i) {
1008
+ RG.text2(this, {
1009
+ 'font':font,
1010
+ 'size':size,
1011
+ 'x':centerx - 10,
1012
+ 'y':centery - (radius * ((i+1) / prop['chart.labels.count'])),
1013
+ 'text':this.scale2.labels[i],
1014
+ 'valign':'center',
1015
+ 'halign':'right',
1016
+ 'bounding':true,
1017
+ 'boundingFill':color,
1018
+ 'boundingStroke':prop['chart.labels.boxed'] ? 'black' : 'rgba(0,0,0,0)',
1019
+ 'tag': 'scale'
1020
+ });
1021
+ }
1022
+ }
1023
+
1024
+ // The "South" axis labels
1025
+ if (axes.indexOf('s') > -1) {
1026
+ for (var i=0; i<prop['chart.labels.count']; ++i) {
1027
+ RG.Text2(this, {
1028
+ 'font':font,
1029
+ 'size':size,
1030
+ 'x':centerx - 10,
1031
+ 'y':centery + (radius * ((i+1) / prop['chart.labels.count'])),
1032
+ 'text':this.scale2.labels[i],
1033
+ 'valign':'center',
1034
+ 'halign':'right',
1035
+ 'bounding':true,
1036
+ 'boundingFill':color,
1037
+ 'boundingStroke':prop['chart.labels.boxed'] ? 'black' : 'rgba(0,0,0,0)',
1038
+ 'tag': 'scale'
1039
+ });
1040
+ }
1041
+ }
1042
+
1043
+ // The "East" axis labels
1044
+ if (axes.indexOf('e') > -1) {
1045
+ for (var i=0; i<prop['chart.labels.count']; ++i) {
1046
+ RG.Text2(this, {
1047
+ 'font':font,
1048
+ 'size':size,
1049
+ 'x':centerx + (radius * ((i+1) / prop['chart.labels.count'])),
1050
+ 'y':centery + 10,
1051
+ 'text':this.scale2.labels[i],
1052
+ 'valign':'top',
1053
+ 'halign':'center',
1054
+ 'bounding':true,
1055
+ 'boundingFill':color,
1056
+ 'boundingStroke':prop['chart.labels.boxed'] ? 'black' : 'rgba(0,0,0,0)',
1057
+ 'tag': 'scale'
1058
+ });
1059
+ }
1060
+ }
1061
+
1062
+ // The "West" axis labels
1063
+ if (axes.indexOf('w') > -1) {
1064
+ for (var i=0; i<prop['chart.labels.count']; ++i) {
1065
+ RG.Text2(this, {
1066
+ 'font':font,
1067
+ 'size':size,
1068
+ 'x':centerx - (radius * ((i+1) / prop['chart.labels.count'])),
1069
+ 'y':centery + 10,
1070
+ 'text':this.scale2.labels[i],
1071
+ 'valign':'top',
1072
+ 'halign':'center',
1073
+ 'bounding':true,
1074
+ 'boundingFill':color,
1075
+ 'boundingStroke':prop['chart.labels.boxed'] ? 'black' : 'rgba(0,0,0,0)',
1076
+ 'tag': 'scale'
1077
+ });
1078
+ }
1079
+ }
1080
+
1081
+ if (RG.trim(axes).length > 0) {
1082
+ RG.Text2(this, {
1083
+ 'font':font,
1084
+ 'size':size,
1085
+ 'x':centerx,
1086
+ 'y':centery,
1087
+ 'text':typeof(prop['chart.ymin']) == 'number' ? RG.number_format(this, Number(prop['chart.ymin']).toFixed(prop['chart.scale.decimals']), units_pre, units_post) : '0',
1088
+ 'valign':'center',
1089
+ 'halign':'center',
1090
+ 'bounding':true,
1091
+ 'boundingFill':color,
1092
+ 'boundingStroke':prop['chart.labels.boxed'] ? 'black' : 'rgba(0,0,0,0)',
1093
+ 'tag': 'scale'
1094
+ });
1095
+ }
1096
+ };
1097
+
1098
+
1099
+
1100
+
1101
+ /**
1102
+ * Draws the circular labels that go around the charts
1103
+ *
1104
+ * @param labels array The labels that go around the chart
1105
+ */
1106
+ this.drawCircularLabels =
1107
+ this.DrawCircularLabels = function (co, labels, font, size, radius)
1108
+ {
1109
+ var variant = prop['chart.variant'],
1110
+ position = prop['chart.labels.position'],
1111
+ radius = radius + 5 + prop['chart.labels.offset'],
1112
+ centerx = this.centerx,
1113
+ centery = this.centery + (prop['chart.variant'].indexOf('3d') !== -1 ? prop['chart.variant.threed.depth'] : 0),
1114
+ labelsColor = prop['chart.labels.color'] || prop['chart.text.color']
1115
+
1116
+ for (var i=0; i<labels.length; ++i) {
1117
+ if (typeof(variant) == 'string' && variant.indexOf('non-equi-angular') !== -1) {
1118
+ var a = Number(this.angles[i][0]) + ((this.angles[i][1] - this.angles[i][0]) / 2);
1119
+ } else {
1120
+ var a = (RG.TWOPI / labels.length) * (i + 1) - (RG.TWOPI / (labels.length * 2));
1121
+ var a = a - RG.HALFPI + (prop['chart.labels.position'] == 'edge' ? ((RG.TWOPI / labels.length) / 2) : 0);
1122
+ }
1123
+
1124
+ var x = centerx + (ma.cos(a) * radius);
1125
+ var y = centery + (ma.sin(a) * radius);
1126
+
1127
+ // Horizontal alignment
1128
+ if (x > centerx) {
1129
+ halign = 'left';
1130
+ } else if (Math.round(x) == centerx) {
1131
+ halign = 'center';
1132
+ } else {
1133
+ halign = 'right';
1134
+ }
1135
+
1136
+ RG.text2(this, {
1137
+ 'color': labelsColor,
1138
+ 'font':font,
1139
+ 'size':size,
1140
+ 'x':x,
1141
+ 'y':y,
1142
+ 'text':String(labels[i]),
1143
+ 'halign':halign,
1144
+ 'valign':'center',
1145
+ 'tag': 'labels'
1146
+ });
1147
+ }
1148
+ };
1149
+
1150
+
1151
+
1152
+
1153
+
1154
+
1155
+
1156
+
1157
+
1158
+
1159
+
1160
+
1161
+
1162
+
1163
+
1164
+
1165
+
1166
+
1167
+
1168
+
1169
+
1170
+
1171
+
1172
+
1173
+
1174
+
1175
+ /**
1176
+ * This function is for use with circular graph types, eg the Pie or Rose. Pass it your event object
1177
+ * and it will pass you back the corresponding segment details as an array:
1178
+ *
1179
+ * [x, y, r, startAngle, endAngle]
1180
+ *
1181
+ * Angles are measured in degrees, and are measured from the "east" axis (just like the canvas).
1182
+ *
1183
+ * @param object e Your event object
1184
+ * @param object Options (OPTIONAL):
1185
+ * radius - whether to take into account
1186
+ * the radius of the segment
1187
+ */
1188
+ this.getShape =
1189
+ this.getSegment = function (e)
1190
+ {
1191
+ RG.fixEventObject(e);
1192
+
1193
+ var angles = this.angles;
1194
+ var ret = [];
1195
+ var opt = arguments[1] ? arguments[1] : {radius: true};
1196
+
1197
+ /**
1198
+ * Go through all of the angles checking each one
1199
+ */
1200
+ for (var i=0; i<angles.length ; ++i) {
1201
+
1202
+ var angleStart = angles[i][0];
1203
+ var angleEnd = angles[i][1];
1204
+ var radiusStart = opt.radius === false ? 0 : angles[i][2];
1205
+ var radiusEnd = opt.radius === false ? this.radius : angles[i][3];
1206
+ var centerX = angles[i][4];
1207
+ var centerY = angles[i][5];// - (prop['chart.variant'].indexOf('3d') !== -1 ? prop['chart.variant.threed.depth'] : 0);
1208
+ var mouseXY = RG.getMouseXY(e);
1209
+ var mouseX = mouseXY[0] - centerX;
1210
+ var mouseY = mouseXY[1] - centerY;
1211
+
1212
+ // New click testing (the 0.01 is there because Opera doesn't like 0 as the radius)
1213
+ co.beginPath();
1214
+ co.arc(centerX, centerY, radiusStart ? radiusStart : 0.01, angleStart, angleEnd, false);
1215
+ co.arc(centerX, centerY, radiusEnd, angleEnd, angleStart, true);
1216
+ co.closePath();
1217
+
1218
+ // No stroke() or fill()
1219
+
1220
+
1221
+ if (co.isPointInPath(mouseXY[0], mouseXY[1])) {
1222
+
1223
+ angles[i][6] = i;
1224
+
1225
+ if (RG.parseTooltipText) {
1226
+ var tooltip = RG.parseTooltipText(prop['chart.tooltips'], angles[i][6]);
1227
+ }
1228
+
1229
+ // Add the textual keys
1230
+ angles[i]['object'] = this;
1231
+ angles[i]['x'] = angles[i][4];
1232
+ angles[i]['y'] = angles[i][5];
1233
+ angles[i]['angle.start'] = angles[i][0];
1234
+ angles[i]['angle.end'] = angles[i][1];
1235
+ angles[i]['radius.start'] = angles[i][2];
1236
+ angles[i]['radius.end'] = angles[i][3];
1237
+ angles[i]['index'] = angles[i][6];
1238
+ angles[i]['tooltip'] = tooltip ? tooltip : null;
1239
+
1240
+ return angles[i];
1241
+ }
1242
+ }
1243
+
1244
+ return null;
1245
+ };
1246
+
1247
+
1248
+
1249
+
1250
+
1251
+
1252
+
1253
+
1254
+
1255
+
1256
+
1257
+
1258
+
1259
+
1260
+
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+
1269
+
1270
+ /**
1271
+ * Returns any exploded for a particular segment
1272
+ */
1273
+ this.getExploded =
1274
+ this.getexploded = function (index, startAngle, endAngle, exploded)
1275
+ {
1276
+ var explodedx, explodedy;
1277
+
1278
+ /**
1279
+ * Retrieve any exploded - the exploded can be an array of numbers or a single number
1280
+ * (which is applied to all segments)
1281
+ */
1282
+ if (typeof(exploded) == 'object' && typeof(exploded[index]) == 'number') {
1283
+ explodedx = Math.cos(((endAngle - startAngle) / 2) + startAngle) * exploded[index];
1284
+ explodedy = Math.sin(((endAngle - startAngle) / 2) + startAngle) * exploded[index];
1285
+
1286
+ } else if (typeof(exploded) == 'number') {
1287
+ explodedx = Math.cos(((endAngle - startAngle) / 2) + startAngle) * exploded;
1288
+ explodedy = Math.sin(((endAngle - startAngle) / 2) + startAngle) * exploded;
1289
+
1290
+ } else {
1291
+ explodedx = 0;
1292
+ explodedy = 0;
1293
+ }
1294
+
1295
+ return [explodedx, explodedy];
1296
+ };
1297
+
1298
+
1299
+
1300
+
1301
+ /**
1302
+ * This function facilitates the installation of tooltip event listeners if
1303
+ * tooltips are defined.
1304
+ */
1305
+ this.allowTooltips =
1306
+ this.AllowTooltips = function ()
1307
+ {
1308
+ // Preload any tooltip images that are used in the tooltips
1309
+ RG.PreLoadTooltipImages(this);
1310
+
1311
+
1312
+ /**
1313
+ * This installs the window mousedown event listener that lears any
1314
+ * highlight that may be visible.
1315
+ */
1316
+ RG.InstallWindowMousedownTooltipListener(this);
1317
+
1318
+
1319
+ /**
1320
+ * This installs the canvas mousemove event listener. This function
1321
+ * controls the pointer shape.
1322
+ */
1323
+ RG.InstallCanvasMousemoveTooltipListener(this);
1324
+
1325
+
1326
+ /**
1327
+ * This installs the canvas mouseup event listener. This is the
1328
+ * function that actually shows the appropriate tooltip (if any).
1329
+ */
1330
+ RG.InstallCanvasMouseupTooltipListener(this);
1331
+ };
1332
+
1333
+
1334
+
1335
+
1336
+ /**
1337
+ * Each object type has its own Highlight() function which highlights the appropriate shape
1338
+ *
1339
+ * @param object shape The shape to highlight
1340
+ */
1341
+ this.highlight =
1342
+ this.Highlight = function (shape)
1343
+ {
1344
+ if (prop['chart.tooltips.highlight']) {
1345
+ // Add the new segment highlight
1346
+ co.beginPath();
1347
+
1348
+ co.strokeStyle = prop['chart.highlight.stroke'];
1349
+ co.fillStyle = prop['chart.highlight.fill'];
1350
+
1351
+ co.arc(
1352
+ shape['x'],
1353
+ shape['y'],//- (prop['chart.variant'].indexOf('3d') !== -1 ? prop['chart.variant.threed.depth'] : 0),
1354
+ shape['radius.end'],
1355
+ shape['angle.start'],
1356
+ shape['angle.end'],
1357
+ false
1358
+ );
1359
+
1360
+ if (shape['radius.start'] > 0) {
1361
+ co.arc(
1362
+ shape['x'],
1363
+ shape['y'],
1364
+ shape['radius.start'],
1365
+ shape['angle.end'],
1366
+ shape['angle.start'],
1367
+ true
1368
+ );
1369
+ } else {
1370
+ co.lineTo(
1371
+ shape['x'],
1372
+ shape['y']
1373
+ );
1374
+ }
1375
+ co.closePath();
1376
+
1377
+ co.stroke();
1378
+ co.fill();
1379
+ }
1380
+ };
1381
+
1382
+
1383
+
1384
+
1385
+ /**
1386
+ * The getObjectByXY() worker method. Don't call this call:
1387
+ *
1388
+ * RGraph.ObjectRegistry.getObjectByXY(e)
1389
+ *
1390
+ * @param object e The event object
1391
+ */
1392
+ this.getObjectByXY = function (e)
1393
+ {
1394
+ var mouseXY = RGraph.getMouseXY(e);
1395
+
1396
+ // Work out the radius
1397
+ var radius = RG.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
1398
+
1399
+ // Account for the 3D stretching effect
1400
+ if (prop['chart.variant'].indexOf('3d') !== -1) {
1401
+ radius /= -1;
1402
+ }
1403
+
1404
+ if (
1405
+ mouseXY[0] > (this.centerx - this.radius)
1406
+ && mouseXY[0] < (this.centerx + this.radius)
1407
+ && mouseXY[1] > (this.centery - this.radius)
1408
+ && mouseXY[1] < (this.centery + this.radius)
1409
+ && radius <= this.radius
1410
+ ) {
1411
+
1412
+ return this;
1413
+ }
1414
+ };
1415
+
1416
+
1417
+
1418
+
1419
+ /**
1420
+ * This function positions a tooltip when it is displayed
1421
+ *
1422
+ * @param obj object The chart object
1423
+ * @param int x The X coordinate specified for the tooltip
1424
+ * @param int y The Y coordinate specified for the tooltip
1425
+ * @param objec tooltip The tooltips DIV element
1426
+ */
1427
+ this.positionTooltip = function (obj, x, y, tooltip, idx)
1428
+ {
1429
+ var centerX = this.angles[idx][4],
1430
+ centerY = this.angles[idx][5] - (prop['chart.variant'].indexOf('3d') !== -1 ? prop['chart.variant.threed.depth'] : 0),
1431
+ radiusStart = this.angles[idx][2],
1432
+ radiusEnd = this.angles[idx][3],
1433
+ coordX = this.angles[idx][4],
1434
+ coordY = this.angles[idx][5] - (prop['chart.variant'].indexOf('3d') !== -1 ? prop['chart.variant.threed.depth'] : 0),
1435
+ angleStart = this.angles[idx][0],
1436
+ angleEnd = this.angles[idx][1],
1437
+ radius = ((radiusEnd - radiusStart) / 2) + radiusStart,
1438
+ angleCenter = ((angleEnd - angleStart) / 2) + angleStart,
1439
+ canvasXY = RG.getCanvasXY(this.canvas),
1440
+ gutterLeft = this.gutterLeft,
1441
+ gutterTop = this.gutterTop,
1442
+ width = tooltip.offsetWidth,
1443
+ height = tooltip.offsetHeight
1444
+
1445
+ // By default any overflow is hidden
1446
+ tooltip.style.overflow = '';
1447
+
1448
+ // The arrow
1449
+ var img = new Image();
1450
+ img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
1451
+ img.style.position = 'absolute';
1452
+ img.id = '__rgraph_tooltip_pointer__';
1453
+ img.style.top = (tooltip.offsetHeight - 2) + 'px';
1454
+ tooltip.appendChild(img);
1455
+
1456
+ // Reposition the tooltip if at the edges:
1457
+
1458
+ // LEFT edge
1459
+ if ((canvasXY[0] + coordX + (Math.cos(angleCenter) * radius) - (width / 2)) < 10) {
1460
+ tooltip.style.left = (canvasXY[0] + coordX + (Math.cos(angleCenter) * (radius * (prop['chart.variant'].indexOf('3d') !== -1 ? 1.5 : 1)) )- (width * 0.1)) + 'px';
1461
+ tooltip.style.top = (canvasXY[1] + coordY + (Math.sin(angleCenter) * radius)- height - 5) + 'px';
1462
+ img.style.left = ((width * 0.1) - 8.5) + 'px';
1463
+
1464
+ // RIGHT edge
1465
+ } else if ((canvasXY[0] + coordX + (Math.cos(angleCenter) * radius) + (width / 2)) > (doc.body.offsetWidth - 10) ) {
1466
+ tooltip.style.left = (canvasXY[0] + coordX + (Math.cos(angleCenter) * (radius * (prop['chart.variant'].indexOf('3d') !== -1 ? 1.5 : 1)) ) - (width * 0.9)) + 'px';
1467
+ tooltip.style.top = (canvasXY[1] + coordY + (Math.sin(angleCenter) * radius)- height - 5) + 'px';
1468
+ img.style.left = ((width * 0.9) - 8.5) + 'px';
1469
+
1470
+ // Default positioning - CENTERED
1471
+ } else {
1472
+
1473
+ tooltip.style.left = (canvasXY[0] + coordX + (ma.cos(angleCenter) * (radius * (prop['chart.variant'].indexOf('3d') !== -1 ? 1.5 : 1) ) )- (width / 2)) + 'px';
1474
+ tooltip.style.top = (canvasXY[1] + coordY + (ma.sin(angleCenter) * radius)- height - 5) + 'px';
1475
+ img.style.left = ((width * 0.5) - 8.5) + 'px';
1476
+ }
1477
+ };
1478
+
1479
+
1480
+
1481
+
1482
+ /**
1483
+ * This method gives you the relevant radius for a particular value
1484
+ *
1485
+ * @param number value The relevant value to get the radius for
1486
+ */
1487
+ this.getRadius = function (value)
1488
+ {
1489
+ // Range checking (the Rose minimum is always 0)
1490
+ if (value < 0 || value > this.max) {
1491
+ return null;
1492
+ }
1493
+
1494
+ var r = (value / this.max) * this.radius;
1495
+
1496
+ return r;
1497
+ };
1498
+
1499
+
1500
+
1501
+
1502
+ /**
1503
+ * This allows for easy specification of gradients
1504
+ */
1505
+ this.parseColors = function ()
1506
+ {
1507
+ // Save the original colors so that they can be restored when the canvas is reset
1508
+ if (this.original_colors.length === 0) {
1509
+ this.original_colors['chart.colors'] = RG.array_clone(prop['chart.colors']);
1510
+ this.original_colors['chart.key.colors'] = RG.array_clone(prop['chart.key.colors']);
1511
+ this.original_colors['chart.text.color'] = RG.array_clone(prop['chart.text.color']);
1512
+ this.original_colors['chart.title.color'] = RG.array_clone(prop['chart.title.color']);
1513
+ this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
1514
+ this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
1515
+ }
1516
+
1517
+
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+
1525
+
1526
+ for (var i=0; i<prop['chart.colors'].length; ++i) {
1527
+ prop['chart.colors'][i] = this.parseSingleColorForGradient(prop['chart.colors'][i]);
1528
+ }
1529
+
1530
+ /**
1531
+ * Key colors
1532
+ */
1533
+ if (!RG.is_null(prop['chart.key.colors'])) {
1534
+ for (var i=0; i<prop['chart.key.colors'].length; ++i) {
1535
+ prop['chart.key.colors'][i] = this.parseSingleColorForGradient(prop['chart.key.colors'][i]);
1536
+ }
1537
+ }
1538
+
1539
+ prop['chart.text.color'] = this.parseSingleColorForGradient(prop['chart.text.color']);
1540
+ prop['chart.title.color'] = this.parseSingleColorForGradient(prop['chart.title.color']);
1541
+ prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
1542
+ prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
1543
+ prop['chart.segment.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.segment.highlight.stroke']);
1544
+ prop['chart.segment.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.segment.highlight.fill']);
1545
+ };
1546
+
1547
+
1548
+
1549
+
1550
+ /**
1551
+ * Use this function to reset the object to the post-constructor state. Eg reset colors if
1552
+ * need be etc
1553
+ */
1554
+ this.reset = function ()
1555
+ {
1556
+ };
1557
+
1558
+
1559
+
1560
+
1561
+ /**
1562
+ * This parses a single color value
1563
+ */
1564
+ this.parseSingleColorForGradient = function (color)
1565
+ {
1566
+ if (!color || typeof(color) != 'string') {
1567
+ return color;
1568
+ }
1569
+
1570
+ if (color.match(/^gradient\((.*)\)$/i)) {
1571
+
1572
+ var parts = RegExp.$1.split(':');
1573
+
1574
+ // Create the gradient
1575
+ //var grad = context.createLinearGradient(0,0,canvas.width,0);
1576
+ var grad = co.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
1577
+
1578
+ var diff = 1 / (parts.length - 1);
1579
+
1580
+ grad.addColorStop(0, RG.trim(parts[0]));
1581
+
1582
+ for (var j=1; j<parts.length; ++j) {
1583
+ grad.addColorStop(j * diff, RG.trim(parts[j]));
1584
+ }
1585
+ }
1586
+
1587
+ return grad ? grad : color;
1588
+ };
1589
+
1590
+
1591
+
1592
+
1593
+ /**
1594
+ * This function handles highlighting an entire data-series for the interactive
1595
+ * key
1596
+ *
1597
+ * @param int index The index of the data series to be highlighted
1598
+ */
1599
+ this.interactiveKeyHighlight = function (index)
1600
+ {
1601
+ var segments = this.angles2;
1602
+
1603
+ for (var i=0; i<this.angles2.length; i+=1) {
1604
+ co.beginPath();
1605
+ co.lineWidth = 2;
1606
+ co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
1607
+ co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
1608
+ co.arc(segments[i][index][4], segments[i][index][5], segments[i][index][2], segments[i][index][0], segments[i][index][1], false);
1609
+ co.arc(segments[i][index][4], segments[i][index][5], segments[i][index][3], segments[i][index][1], segments[i][index][0], true);
1610
+ co.closePath();
1611
+ co.fill();
1612
+ co.stroke();
1613
+ }
1614
+
1615
+ return/*
1616
+ if (segments) {
1617
+ for (var i=0; i<segments.length; i+=1) {
1618
+
1619
+ }
1620
+ }
1621
+ */
1622
+ };
1623
+
1624
+
1625
+
1626
+
1627
+ /**
1628
+ * Using a function to add events makes it easier to facilitate method chaining
1629
+ *
1630
+ * @param string type The type of even to add
1631
+ * @param function func
1632
+ */
1633
+ this.on = function (type, func)
1634
+ {
1635
+ if (type.substr(0,2) !== 'on') {
1636
+ type = 'on' + type;
1637
+ }
1638
+
1639
+ this[type] = func;
1640
+
1641
+ return this;
1642
+ };
1643
+
1644
+
1645
+
1646
+
1647
+ /**
1648
+ * This function runs once only
1649
+ * (put at the end of the file (before any effects))
1650
+ */
1651
+ this.firstDrawFunc = function ()
1652
+ {
1653
+ };
1654
+
1655
+
1656
+
1657
+
1658
+ /**
1659
+ * Rose chart explode
1660
+ *
1661
+ * Explodes the Rose chart - gradually incrementing the size of the chart.explode property
1662
+ *
1663
+ * @param object Optional options for the effect. You can pass in frames here - such as:
1664
+ * myRose.roundRobin({frames: 60}; function () {alert('Done!');})
1665
+ * @param function A callback function which is called when the effect is finished
1666
+ */
1667
+ this.explode = function ()
1668
+ {
1669
+ var obj = this;
1670
+ var opt = arguments[0] || {};
1671
+ var callback = arguments[1] || function (){};
1672
+ var frames = opt.frames ? opt.frames : 30;
1673
+ var frame = 0;
1674
+ var explodedMax = ma.max(ca.width, ca.height);
1675
+ var exploded = Number(this.Get('exploded'));
1676
+
1677
+
1678
+
1679
+
1680
+ function iterator ()
1681
+ {
1682
+ exploded = (frame / frames) * explodedMax;
1683
+
1684
+ // Set the new value
1685
+ obj.Set('exploded', exploded);
1686
+
1687
+ RG.clear(ca);
1688
+ RG.redrawCanvas(ca);
1689
+
1690
+ if (frame++ < frames) {
1691
+ RG.Effects.updateCanvas(iterator);
1692
+ } else {
1693
+ callback(obj);
1694
+ }
1695
+ }
1696
+
1697
+
1698
+
1699
+
1700
+ iterator();
1701
+
1702
+ return this;
1703
+ };
1704
+
1705
+
1706
+
1707
+
1708
+ /**
1709
+ * RoundRobin
1710
+ *
1711
+ * This effect is similar to the Pie chart RoundRobin effect
1712
+ *
1713
+ * @param object Optional options for the effect. You can pass in frames here - such as:
1714
+ * myRose.roundRobin({frames: 60}; function () {alert('Done!');})
1715
+ * @param function A callback function which is called when the effect is finished
1716
+ */
1717
+ this.roundrobin =
1718
+ this.roundRobin = function ()
1719
+ {
1720
+ var obj = this;
1721
+ var opt = arguments[0] || {}
1722
+ var frames = opt.frames || 30;
1723
+ var frame = 0;
1724
+ var original_margin = prop['chart.margin'];
1725
+ var margin = (360 / this.data.length) / 2;
1726
+ var callback = arguments[1] || function () {};
1727
+
1728
+ this.Set('chart.margin', margin);
1729
+ this.Set('chart.animation.roundrobin.factor', 0);
1730
+
1731
+ function iterator ()
1732
+ {
1733
+ RG.clear(obj.canvas);
1734
+ RG.redrawCanvas(obj.canvas);
1735
+
1736
+ if (frame++ < frames) {
1737
+ obj.set('animation.roundrobin.factor', frame / frames);
1738
+ obj.set('margin', (frame / frames) * original_margin);
1739
+ RG.Effects.updateCanvas(iterator);
1740
+ } else {
1741
+ obj.set('animation.roundrobin.factor', 1);
1742
+ obj.set('margin', original_margin);
1743
+
1744
+ callback(obj);
1745
+ }
1746
+ }
1747
+
1748
+ iterator();
1749
+
1750
+ return this;
1751
+ };
1752
+
1753
+
1754
+
1755
+
1756
+ /**
1757
+ * Rose chart implode
1758
+ *
1759
+ * Implodes the Rose chart - gradually decreasing the size of the chart.explode property. It starts at the largest of
1760
+ * the canvas width./height
1761
+ *
1762
+ * @param object Optional options for the effect. You can pass in frames here - such as:
1763
+ * myRose.implode({frames: 60}; function () {alert('Done!');})
1764
+ * @param function A callback function which is called when the effect is finished
1765
+ */
1766
+ this.implode = function ()
1767
+ {
1768
+ var obj = this;
1769
+ var opt = arguments[0] || {};
1770
+ var callback = arguments[1] || function (){};
1771
+ var frames = opt.frames || 30;
1772
+ var frame = 0;
1773
+ var explodedMax = ma.max(ca.width, ca.height);
1774
+ var exploded = explodedMax;
1775
+
1776
+
1777
+
1778
+ function iterator ()
1779
+ {
1780
+ exploded = explodedMax - ((frame / frames) * explodedMax);
1781
+
1782
+ // Set the new value
1783
+ obj.Set('exploded', exploded);
1784
+
1785
+ RG.clear(ca);
1786
+ RG.redrawCanvas(ca);
1787
+
1788
+ if (frame++ < frames) {
1789
+ RG.Effects.updateCanvas(iterator);
1790
+ } else {
1791
+ RG.clear(obj.canvas);
1792
+ RG.redrawCanvas(obj.canvas);
1793
+ callback(obj);
1794
+ }
1795
+ }
1796
+
1797
+ iterator();
1798
+
1799
+ return this;
1800
+ };
1801
+
1802
+
1803
+
1804
+
1805
+ /**
1806
+ * Rose chart Grow
1807
+ *
1808
+ * This effect gradually increases the size of the Rose chart
1809
+ *
1810
+ * @param object Optional options for the effect. You can pass in frames here - such as:
1811
+ * myRose.grow({frames: 60}; function () {alert('Done!');})
1812
+ * @param function A callback function which is called when the effect is finished
1813
+ */
1814
+ this.grow = function ()
1815
+ {
1816
+ var obj = this;
1817
+ var opt = arguments[0] || {};
1818
+ var callback = arguments[1] || function (){};
1819
+ var frames = opt.frames || 30;
1820
+ var frame = 0;
1821
+
1822
+ function iterator ()
1823
+ {
1824
+ obj.Set('animation.grow.multiplier', frame / frames);
1825
+
1826
+ RG.clear(ca);
1827
+ RG.redrawCanvas(ca);
1828
+
1829
+ if (frame < frames) {
1830
+ frame++;
1831
+ RG.Effects.updateCanvas(iterator);
1832
+ } else {
1833
+ callback(obj);
1834
+ }
1835
+ }
1836
+
1837
+ iterator();
1838
+
1839
+ return this;
1840
+ };
1841
+
1842
+
1843
+
1844
+
1845
+ /**
1846
+ * Register this object
1847
+ */
1848
+ RG.Register(this);
1849
+
1850
+
1851
+
1852
+
1853
+ /**
1854
+ * This is the 'end' of the constructor so if the first argument
1855
+ * contains configuration data - handle that.
1856
+ */
1857
+ if (parseConfObjectForOptions) {
1858
+ RG.parseObjectStyleConfig(this, conf.options);
1859
+ }
1860
+ };