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,4751 @@
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
+
18
+ // Module pattern
19
+ (function (win, doc, undefined)
20
+ {
21
+ var RG = RGraph,
22
+ ua = navigator.userAgent,
23
+ ma = Math;
24
+
25
+
26
+
27
+
28
+ /**
29
+ * Initialise the various objects
30
+ */
31
+ RG.Highlight = {};
32
+ RG.Registry = {};
33
+ RG.Registry.store = [];
34
+ RG.Registry.store['chart.event.handlers'] = [];
35
+ RG.Registry.store['__rgraph_event_listeners__'] = []; // Used in the new system for tooltips
36
+ RG.Background = {};
37
+ RG.background = {};
38
+ RG.objects = [];
39
+ RG.Resizing = {};
40
+ RG.events = [];
41
+ RG.cursor = [];
42
+ RG.Effects = RG.Effects || {};
43
+ RG.cache = [];
44
+
45
+ RG.ObjectRegistry = {};
46
+ RG.ObjectRegistry.objects = {};
47
+ RG.ObjectRegistry.objects.byUID = [];
48
+ RG.ObjectRegistry.objects.byCanvasID = [];
49
+
50
+
51
+
52
+
53
+ /**
54
+ * Some "constants". The ua variable is navigator.userAgent (definedabove)
55
+ */
56
+ RG.PI = ma.PI;
57
+ RG.HALFPI = RG.PI / 2;
58
+ RG.TWOPI = RG.PI * 2;
59
+
60
+ RG.ISFF = ua.indexOf('Firefox') != -1;
61
+ RG.ISOPERA = ua.indexOf('Opera') != -1;
62
+ RG.ISCHROME = ua.indexOf('Chrome') != -1;
63
+ RG.ISSAFARI = ua.indexOf('Safari') != -1 && !RG.ISCHROME;
64
+ RG.ISWEBKIT = ua.indexOf('WebKit') != -1;
65
+
66
+ RG.ISIE = ua.indexOf('Trident') > 0 || navigator.userAgent.indexOf('MSIE') > 0;
67
+ RG.ISIE6 = ua.indexOf('MSIE 6') > 0;
68
+ RG.ISIE7 = ua.indexOf('MSIE 7') > 0;
69
+ RG.ISIE8 = ua.indexOf('MSIE 8') > 0;
70
+ RG.ISIE9 = ua.indexOf('MSIE 9') > 0;
71
+ RG.ISIE10 = ua.indexOf('MSIE 10') > 0;
72
+ RG.ISOLD = RGraph.ISIE6 || RGraph.ISIE7 || RGraph.ISIE8; // MUST be here
73
+
74
+ RG.ISIE11UP = ua.indexOf('MSIE') == -1 && ua.indexOf('Trident') > 0;
75
+ RG.ISIE10UP = RG.ISIE10 || RG.ISIE11UP;
76
+ RG.ISIE9UP = RG.ISIE9 || RG.ISIE10UP;
77
+
78
+
79
+
80
+
81
+ /**
82
+ * Returns five values which are used as a nice scale
83
+ *
84
+ * @param max int The maximum value of the graph
85
+ * @param obj object The graph object
86
+ * @return array An appropriate scale
87
+ */
88
+ RG.getScale = function (max, obj)
89
+ {
90
+ /**
91
+ * Special case for 0
92
+ */
93
+ if (max == 0) {
94
+ return ['0.2', '0.4', '0.6', '0.8', '1.0'];
95
+ }
96
+
97
+ var original_max = max;
98
+
99
+ /**
100
+ * Manually do decimals
101
+ */
102
+ if (max <= 1) {
103
+ if (max > 0.5) {
104
+ return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)];
105
+
106
+ } else if (max >= 0.1) {
107
+ return obj.Get('chart.scale.round') ? [0.2,0.4,0.6,0.8,1] : [0.1,0.2,0.3,0.4,0.5];
108
+
109
+ } else {
110
+
111
+ var tmp = max;
112
+ var exp = 0;
113
+
114
+ while (tmp < 1.01) {
115
+ exp += 1;
116
+ tmp *= 10;
117
+ }
118
+
119
+ var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp];
120
+
121
+
122
+ if (max <= ('5e-' + exp)) {
123
+ ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp];
124
+ }
125
+
126
+ return ret;
127
+ }
128
+ }
129
+
130
+ // Take off any decimals
131
+ if (String(max).indexOf('.') > 0) {
132
+ max = String(max).replace(/\.\d+$/, '');
133
+ }
134
+
135
+ var interval = ma.pow(10, Number(String(Number(max)).length - 1));
136
+ var topValue = interval;
137
+
138
+ while (topValue < max) {
139
+ topValue += (interval / 2);
140
+ }
141
+
142
+ // Handles cases where the max is (for example) 50.5
143
+ if (Number(original_max) > Number(topValue)) {
144
+ topValue += (interval / 2);
145
+ }
146
+
147
+ // Custom if the max is greater than 5 and less than 10
148
+ if (max < 10) {
149
+ topValue = (Number(original_max) <= 5 ? 5 : 10);
150
+ }
151
+
152
+ /**
153
+ * Added 02/11/2010 to create "nicer" scales
154
+ */
155
+ if (obj && typeof(obj.Get('chart.scale.round')) == 'boolean' && obj.Get('chart.scale.round')) {
156
+ topValue = 10 * interval;
157
+ }
158
+
159
+ return [topValue * 0.2, topValue * 0.4, topValue * 0.6, topValue * 0.8, topValue];
160
+ };
161
+
162
+
163
+
164
+
165
+ /**
166
+ * Returns an appropriate scale. The return value is actualy an object consisting of:
167
+ * scale.max
168
+ * scale.min
169
+ * scale.scale
170
+ *
171
+ * @param obj object The graph object
172
+ * @param prop object An object consisting of configuration properties
173
+ * @return object An object containg scale information
174
+ */
175
+ RG.getScale2 = function (obj, opt)
176
+ {
177
+ var ca = obj.canvas,
178
+ co = obj.context,
179
+ prop = obj.properties,
180
+ numlabels = typeof opt['ylabels.count'] == 'number' ? opt['ylabels.count'] : 5,
181
+ units_pre = typeof opt['units.pre'] == 'string' ? opt['units.pre'] : '',
182
+ units_post = typeof opt['units.post'] == 'string' ? opt['units.post'] : '',
183
+ max = Number(opt['max']),
184
+ min = typeof opt['min'] == 'number' ? opt['min'] : 0,
185
+ strict = opt['strict'],
186
+ decimals = Number(opt['scale.decimals']), // Sometimes the default is null
187
+ point = opt['scale.point'], // Default is a string in all chart libraries so no need to cast it
188
+ thousand = opt['scale.thousand'], // Default is a string in all chart libraries so no need to cast it
189
+ original_max = max,
190
+ round = opt['scale.round'],
191
+ scale = {'max':1,'labels':[]}
192
+
193
+
194
+
195
+ /**
196
+ * Special case for 0
197
+ *
198
+ * ** Must be first **
199
+ */
200
+ if (!max) {
201
+
202
+ var max = 1;
203
+
204
+ var scale = {max:1,min:0,labels:[]};
205
+
206
+ for (var i=0; i<numlabels; ++i) {
207
+ var label = ((((max - min) / numlabels) + min) * (i + 1)).toFixed(decimals);
208
+ scale.labels.push(units_pre + label + units_post);
209
+ }
210
+
211
+ /**
212
+ * Manually do decimals
213
+ */
214
+ } else if (max <= 1 && !strict) {
215
+
216
+ if (max > 0.5) {
217
+
218
+ max = 1;
219
+ min = min;
220
+ scale.min = min;
221
+
222
+ for (var i=0; i<numlabels; ++i) {
223
+ var label = ((((max - min) / numlabels) * (i + 1)) + min).toFixed(decimals);
224
+
225
+ scale.labels.push(units_pre + label + units_post);
226
+ }
227
+
228
+ } else if (max >= 0.1) {
229
+
230
+ max = 0.5;
231
+ min = min;
232
+ scale = {'max': 0.5, 'min':min,'labels':[]}
233
+
234
+ for (var i=0; i<numlabels; ++i) {
235
+ var label = ((((max - min) / numlabels) + min) * (i + 1)).toFixed(decimals);
236
+ scale.labels.push(units_pre + label + units_post);
237
+ }
238
+
239
+ } else {
240
+
241
+ scale = {'min':min,'labels':[]}
242
+ var max_str = String(max);
243
+
244
+ if (max_str.indexOf('e') > 0) {
245
+ var numdecimals = ma.abs(max_str.substring(max_str.indexOf('e') + 1));
246
+ } else {
247
+ var numdecimals = String(max).length - 2;
248
+ }
249
+
250
+ var max = 1 / ma.pow(10,numdecimals - 1);
251
+
252
+ for (var i=0; i<numlabels; ++i) {
253
+ var label = ((((max - min) / numlabels) + min) * (i + 1));
254
+ label = label.toExponential();
255
+ label = label.split(/e/);
256
+ label[0] = ma.round(label[0]);
257
+ label = label.join('e');
258
+
259
+ scale.labels.push(label);
260
+ }
261
+
262
+ //This makes the top scale value of the format 10e-2 instead of 1e-1
263
+ tmp = scale.labels[scale.labels.length - 1].split(/e/);
264
+ tmp[0] += 0;
265
+ tmp[1] = Number(tmp[1]) - 1;
266
+ tmp = tmp[0] + 'e' + tmp[1];
267
+ scale.labels[scale.labels.length - 1] = tmp;
268
+
269
+ // Add the units
270
+ for (var i=0; i<scale.labels.length ; ++i) {
271
+ scale.labels[i] = units_pre + scale.labels[i] + units_post;
272
+ }
273
+
274
+ scale.max = Number(max);
275
+ }
276
+
277
+
278
+ } else if (!strict) {
279
+
280
+ /**
281
+ * Now comes the scale handling for integer values
282
+ */
283
+
284
+
285
+ // This accomodates decimals by rounding the max up to the next integer
286
+ max = ma.ceil(max);
287
+
288
+ var interval = ma.pow(10, ma.max(1, Number(String(Number(max) - Number(min)).length - 1)) );
289
+
290
+ var topValue = interval;
291
+
292
+ while (topValue < max) {
293
+ topValue += (interval / 2);
294
+ }
295
+
296
+ // Handles cases where the max is (for example) 50.5
297
+ if (Number(original_max) > Number(topValue)) {
298
+ topValue += (interval / 2);
299
+ }
300
+
301
+ // Custom if the max is greater than 5 and less than 10
302
+ if (max <= 10) {
303
+ topValue = (Number(original_max) <= 5 ? 5 : 10);
304
+ }
305
+
306
+
307
+ // Added 02/11/2010 to create "nicer" scales
308
+ if (obj && typeof(round) == 'boolean' && round) {
309
+ topValue = 10 * interval;
310
+ }
311
+
312
+ scale.max = topValue;
313
+
314
+ // Now generate the scale. Temporarily set the objects chart.scale.decimal and chart.scale.point to those
315
+ //that we've been given as the number_format functuion looks at those instead of using argumrnts.
316
+ var tmp_point = prop['chart.scale.point'];
317
+ var tmp_thousand = prop['chart.scale.thousand'];
318
+
319
+ obj.Set('chart.scale.thousand', thousand);
320
+ obj.Set('chart.scale.point', point);
321
+
322
+
323
+ for (var i=0; i<numlabels; ++i) {
324
+ scale.labels.push( RG.number_format(obj, ((((i+1) / numlabels) * (topValue - min)) + min).toFixed(decimals), units_pre, units_post) );
325
+ }
326
+
327
+ obj.Set('chart.scale.thousand', tmp_thousand);
328
+ obj.Set('chart.scale.point', tmp_point);
329
+
330
+ } else if (typeof(max) == 'number' && strict) {
331
+
332
+ /**
333
+ * ymax is set and also strict
334
+ */
335
+ for (var i=0; i<numlabels; ++i) {
336
+ scale.labels.push( RG.number_format(obj, ((((i+1) / numlabels) * (max - min)) + min).toFixed(decimals), units_pre, units_post) );
337
+ }
338
+
339
+ // ???
340
+ scale.max = max;
341
+ }
342
+
343
+
344
+ scale.units_pre = units_pre;
345
+ scale.units_post = units_post;
346
+ scale.point = point;
347
+ scale.decimals = decimals;
348
+ scale.thousand = thousand;
349
+ scale.numlabels = numlabels;
350
+ scale.round = Boolean(round);
351
+ scale.min = min;
352
+
353
+
354
+ return scale;
355
+ };
356
+
357
+
358
+
359
+
360
+ /**
361
+ * Makes a clone of an object
362
+ *
363
+ * @param obj val The object to clone
364
+ */
365
+ RG.arrayClone =
366
+ RG.array_clone = function (obj)
367
+ {
368
+ if(obj === null || typeof obj !== 'object') {
369
+ return obj;
370
+ }
371
+
372
+ var temp = [];
373
+
374
+ for (var i=0,len=obj.length;i<len; ++i) {
375
+
376
+ if (typeof obj[i] === 'number') {
377
+ temp[i] = (function (arg) {return Number(arg);})(obj[i]);
378
+
379
+ } else if (typeof obj[i] === 'string') {
380
+ temp[i] = (function (arg) {return String(arg);})(obj[i]);
381
+
382
+ } else if (typeof obj[i] === 'function') {
383
+ temp[i] = obj[i];
384
+
385
+ } else {
386
+ temp[i] = RG.array_clone(obj[i]);
387
+ }
388
+ }
389
+
390
+ return temp;
391
+ };
392
+
393
+
394
+
395
+
396
+ /**
397
+ * Returns the maximum numeric value which is in an array
398
+ *
399
+ * @param array arr The array (can also be a number, in which case it's returned as-is)
400
+ * @param int Whether to ignore signs (ie negative/positive)
401
+ * @return int The maximum value in the array
402
+ */
403
+ RG.arrayMax =
404
+ RG.array_max = function (arr)
405
+ {
406
+ var max = null,
407
+ ma = Math
408
+
409
+ if (typeof arr === 'number') {
410
+ return arr;
411
+ }
412
+
413
+ if (RG.isNull(arr)) {
414
+ return 0;
415
+ }
416
+
417
+ for (var i=0,len=arr.length; i<len; ++i) {
418
+ if (typeof arr[i] === 'number') {
419
+
420
+ var val = arguments[1] ? ma.abs(arr[i]) : arr[i];
421
+
422
+ if (typeof max === 'number') {
423
+ max = ma.max(max, val);
424
+ } else {
425
+ max = val;
426
+ }
427
+ }
428
+ }
429
+
430
+ return max;
431
+ };
432
+
433
+
434
+
435
+
436
+ /**
437
+ * Returns the minimum numeric value which is in an array
438
+ *
439
+ * @param array arr The array (can also be a number, in which case it's returned as-is)
440
+ * @param int Whether to ignore signs (ie negative/positive)
441
+ * @return int The minimum value in the array
442
+ */
443
+ RG.arrayMin = function (arr)
444
+ {
445
+ var max = null,
446
+ ma = Math;
447
+
448
+ if (typeof arr === 'number') {
449
+ return arr;
450
+ }
451
+
452
+ if (RG.isNull(arr)) {
453
+ return 0;
454
+ }
455
+
456
+ for (var i=0,len=arr.length; i<len; ++i) {
457
+ if (typeof arr[i] === 'number') {
458
+
459
+ var val = arguments[1] ? ma.abs(arr[i]) : arr[i];
460
+
461
+ if (typeof min === 'number') {
462
+ min = ma.min(min, val);
463
+ } else {
464
+ min = val;
465
+ }
466
+ }
467
+ }
468
+
469
+ return min;
470
+ };
471
+
472
+
473
+
474
+
475
+ /**
476
+ * Returns the maximum value which is in an array
477
+ *
478
+ * @param array arr The array
479
+ * @param int len The length to pad the array to
480
+ * @param mixed The value to use to pad the array (optional)
481
+ */
482
+ RG.arrayPad =
483
+ RG.array_pad = function (arr, len)
484
+ {
485
+ if (arr.length < len) {
486
+ var val = arguments[2] ? arguments[2] : null;
487
+
488
+ for (var i=arr.length; i<len; i+=1) {
489
+ arr[i] = val;
490
+ }
491
+ }
492
+
493
+ return arr;
494
+ };
495
+
496
+
497
+
498
+
499
+ /**
500
+ * An array sum function
501
+ *
502
+ * @param array arr The array to calculate the total of
503
+ * @return int The summed total of the arrays elements
504
+ */
505
+ RG.arraySum =
506
+ RG.array_sum = function (arr)
507
+ {
508
+ // Allow integers
509
+ if (typeof arr === 'number') {
510
+ return arr;
511
+ }
512
+
513
+ // Account for null
514
+ if (RG.is_null(arr)) {
515
+ return 0;
516
+ }
517
+
518
+ var i, sum, len = arr.length;
519
+
520
+ for(i=0,sum=0;i<len;sum+=arr[i++]);
521
+
522
+ return sum;
523
+ };
524
+
525
+
526
+
527
+
528
+ /**
529
+ * Takes any number of arguments and adds them to one big linear array
530
+ * which is then returned
531
+ *
532
+ * @param ... mixed The data to linearise. You can strings, booleans, numbers or arrays
533
+ */
534
+ RG.arrayLinearize =
535
+ RG.array_linearize = function ()
536
+ {
537
+ var arr = [],
538
+ args = arguments
539
+
540
+ for (var i=0,len=args.length; i<len; ++i) {
541
+
542
+ if (typeof args[i] === 'object' && args[i]) {
543
+ for (var j=0,len2=args[i].length; j<len2; ++j) {
544
+ var sub = RG.array_linearize(args[i][j]);
545
+
546
+ for (var k=0,len3=sub.length; k<len3; ++k) {
547
+ arr.push(sub[k]);
548
+ }
549
+ }
550
+ } else {
551
+ arr.push(args[i]);
552
+ }
553
+ }
554
+
555
+ return arr;
556
+ };
557
+
558
+
559
+
560
+
561
+ /**
562
+ * Takes one off the front of the given array and returns the new array.
563
+ *
564
+ * @param array arr The array from which to take one off the front of array
565
+ *
566
+ * @return array The new array
567
+ */
568
+ RG.arrayShift =
569
+ RG.array_shift = function(arr)
570
+ {
571
+ var ret = [];
572
+
573
+ for(var i=1,len=arr.length; i<len; ++i) {
574
+ ret.push(arr[i]);
575
+ }
576
+
577
+ return ret;
578
+ };
579
+
580
+
581
+
582
+
583
+ /**
584
+ * Reverses the order of an array
585
+ *
586
+ * @param array arr The array to reverse
587
+ */
588
+ RG.arrayReverse =
589
+ RG.array_reverse = function (arr)
590
+ {
591
+ var newarr=[];
592
+
593
+ for(var i=arr.length - 1; i>=0; i-=1) {
594
+ newarr.push(arr[i]);
595
+ }
596
+
597
+ return newarr;
598
+ };
599
+
600
+
601
+
602
+
603
+ /**
604
+ * Clears the canvas by setting the width. You can specify a colour if you wish.
605
+ *
606
+ * @param object canvas The canvas to clear
607
+ * @param mixed Usually a color string to use to clear the canvas
608
+ * with - could also be a gradient object
609
+ */
610
+ RG.clear =
611
+ RG.Clear = function (ca)
612
+ {
613
+ var obj = ca.__object__,
614
+ co = ca.getContext('2d'),
615
+ color = arguments[1] || (obj && obj.get('clearto'))
616
+
617
+ if (!ca) {
618
+ return;
619
+ }
620
+
621
+ RG.FireCustomEvent(obj, 'onbeforeclear');
622
+
623
+ if (RG.ISIE8 && !color) {
624
+ color = 'white';
625
+ }
626
+
627
+ /**
628
+ * Can now clear the canvas back to fully transparent
629
+ */
630
+ if (!color || (color && color === 'rgba(0,0,0,0)' || color === 'transparent')) {
631
+
632
+ co.clearRect(0,0,ca.width, ca.height);
633
+
634
+ // Reset the globalCompositeOperation
635
+ co.globalCompositeOperation = 'source-over';
636
+
637
+ } else {
638
+
639
+ co.fillStyle = color;
640
+ co.beginPath();
641
+
642
+ if (RG.ISIE8) {
643
+ co.fillRect(0,0,ca.width,ca.height);
644
+ } else {
645
+ co.fillRect(-10,-10,ca.width + 20,ca.height + 20);
646
+ }
647
+
648
+ co.fill();
649
+ }
650
+
651
+ //if (RG.ClearAnnotations) {
652
+ //RG.ClearAnnotations(ca.id);
653
+ //}
654
+
655
+ /**
656
+ * This removes any background image that may be present
657
+ */
658
+ if (RG.Registry.Get('chart.background.image.' + ca.id)) {
659
+ var img = RG.Registry.Get('chart.background.image.' + ca.id);
660
+ img.style.position = 'absolute';
661
+ img.style.left = '-10000px';
662
+ img.style.top = '-10000px';
663
+ }
664
+
665
+ /**
666
+ * This hides the tooltip that is showing IF it has the same canvas ID as
667
+ * that which is being cleared
668
+ */
669
+ if (RG.Registry.Get('chart.tooltip') && obj.get('chart.tooltips.nohideonclear') !== true) {
670
+ RG.HideTooltip(ca);
671
+ //RG.Redraw();
672
+ }
673
+
674
+
675
+
676
+ //
677
+ // Hide all DOM text by positioning it outside the canvas
678
+ //
679
+ //for (i in RG.cache) {
680
+ // if (typeof i === 'string' && i.indexOf('-text-') > 0) {
681
+ // RG.cache[i].style.left = '-100px';
682
+ // RG.cache[i].style.top = '-100px';
683
+ // }
684
+ //}
685
+
686
+ /**
687
+ * Set the cursor to default
688
+ */
689
+ ca.style.cursor = 'default';
690
+
691
+ RG.FireCustomEvent(obj, 'onclear');
692
+ };
693
+
694
+
695
+
696
+
697
+ /**
698
+ * Draws the title of the graph
699
+ *
700
+ * @param object canvas The canvas object
701
+ * @param string text The title to write
702
+ * @param integer gutter The size of the gutter
703
+ * @param integer The center X point (optional - if not given it will be generated from the canvas width)
704
+ * @param integer Size of the text. If not given it will be 14
705
+ * @param object An optional object which has canvas and context properties to use instead of those on
706
+ * the obj argument (so as to enable caching)
707
+ */
708
+ RG.drawTitle =
709
+ RG.DrawTitle = function (obj, text, gutterTop)
710
+ {
711
+ var ca = canvas = obj.canvas,
712
+ co = context = obj.context,
713
+ prop = obj.properties,
714
+ gutterLeft = prop['chart.gutter.left'],
715
+ gutterRight = prop['chart.gutter.right'],
716
+ gutterTop = gutterTop,
717
+ gutterBottom = prop['chart.gutter.bottom'],
718
+ size = arguments[4] ? arguments[4] : 12,
719
+ bold = prop['chart.title.bold'],
720
+ centerx = (arguments[3] ? arguments[3] : ((ca.width - gutterLeft - gutterRight) / 2) + gutterLeft),
721
+ keypos = prop['chart.key.position'],
722
+ vpos = prop['chart.title.vpos'],
723
+ hpos = prop['chart.title.hpos'],
724
+ bgcolor = prop['chart.title.background'],
725
+ x = prop['chart.title.x'],
726
+ y = prop['chart.title.y'],
727
+ halign = 'center',
728
+ valign = 'center'
729
+
730
+ // Account for 3D effect by faking the key position
731
+ if (obj.type == 'bar' && prop['chart.variant'] == '3d') {
732
+ keypos = 'gutter';
733
+ }
734
+
735
+ co.beginPath();
736
+ co.fillStyle = prop['chart.text.color'] ? prop['chart.text.color'] : 'black';
737
+
738
+
739
+
740
+
741
+
742
+ /**
743
+ * Vertically center the text if the key is not present
744
+ */
745
+ if (keypos && keypos != 'gutter') {
746
+ var valign = 'center';
747
+
748
+ } else if (!keypos) {
749
+ var valign = 'center';
750
+
751
+ } else {
752
+ var valign = 'bottom';
753
+ }
754
+
755
+
756
+
757
+
758
+
759
+ // if chart.title.vpos is a number, use that
760
+ if (typeof prop['chart.title.vpos'] === 'number') {
761
+ vpos = prop['chart.title.vpos'] * gutterTop;
762
+
763
+ if (prop['chart.xaxispos'] === 'top') {
764
+ vpos = prop['chart.title.vpos'] * gutterBottom + gutterTop + (ca.height - gutterTop - gutterBottom);
765
+ }
766
+
767
+ } else {
768
+ vpos = gutterTop - size - 5;
769
+
770
+ if (prop['chart.xaxispos'] === 'top') {
771
+ vpos = ca.height - gutterBottom + size + 5;
772
+ }
773
+ }
774
+
775
+
776
+
777
+
778
+ // if chart.title.hpos is a number, use that. It's multiplied with the (entire) canvas width
779
+ if (typeof hpos === 'number') {
780
+ centerx = hpos * ca.width;
781
+ }
782
+
783
+ /**
784
+ * Now the chart.title.x and chart.title.y settings override (is set) the above
785
+ */
786
+ if (typeof x === 'number') centerx = x;
787
+ if (typeof y === 'number') vpos = y;
788
+
789
+
790
+
791
+
792
+ /**
793
+ * Horizontal alignment can now (Jan 2013) be specified
794
+ */
795
+ if (typeof prop['chart.title.halign'] === 'string') {
796
+ halign = prop['chart.title.halign'];
797
+ }
798
+
799
+ /**
800
+ * Vertical alignment can now (Jan 2013) be specified
801
+ */
802
+ if (typeof prop['chart.title.valign'] === 'string') {
803
+ valign = prop['chart.title.valign'];
804
+ }
805
+
806
+
807
+
808
+
809
+
810
+ // Set the colour
811
+ if (typeof prop['chart.title.color'] !== null) {
812
+ var oldColor = co.fillStyle
813
+ var newColor = prop['chart.title.color'];
814
+ co.fillStyle = newColor ? newColor : 'black';
815
+ }
816
+
817
+
818
+
819
+
820
+ /**
821
+ * Default font is Arial
822
+ */
823
+ var font = prop['chart.text.font'];
824
+
825
+
826
+
827
+
828
+ /**
829
+ * Override the default font with chart.title.font
830
+ */
831
+ if (typeof prop['chart.title.font'] === 'string') {
832
+ font = prop['chart.title.font'];
833
+ }
834
+
835
+
836
+
837
+
838
+ /**
839
+ * Draw the title
840
+ */
841
+ RG.Text2(co, {
842
+ 'font':font,
843
+ 'size':size,
844
+ 'x':centerx,
845
+ 'y':vpos,
846
+ 'text':text,
847
+ 'valign':valign,
848
+ 'halign':halign,
849
+ 'bounding':bgcolor != null,
850
+ 'bounding.fill':bgcolor,
851
+ 'bold':bold,
852
+ 'tag':'title'
853
+ });
854
+
855
+ // Reset the fill colour
856
+ co.fillStyle = oldColor;
857
+ };
858
+
859
+
860
+
861
+
862
+ /**
863
+ * Gets the mouse X/Y coordinates relative to the canvas
864
+ *
865
+ * @param object e The event object. As such this method should be used in an event listener.
866
+ */
867
+ RG.getMouseXY = function(e)
868
+ {
869
+ var el = e.target;
870
+ var ca = el;
871
+ var caStyle = ca.style;
872
+ var offsetX = 0;
873
+ var offsetY = 0;
874
+ var x;
875
+ var y;
876
+ var ISFIXED = (ca.style.position == 'fixed');
877
+ var borderLeft = parseInt(caStyle.borderLeftWidth) || 0;
878
+ var borderTop = parseInt(caStyle.borderTopWidth) || 0;
879
+ var paddingLeft = parseInt(caStyle.paddingLeft) || 0
880
+ var paddingTop = parseInt(caStyle.paddingTop) || 0
881
+ var additionalX = borderLeft + paddingLeft;
882
+ var additionalY = borderTop + paddingTop;
883
+
884
+
885
+ if (typeof e.offsetX === 'number' && typeof e.offsetY === 'number') {
886
+
887
+ if (ISFIXED) {
888
+ if (RG.ISOPERA) {
889
+ x = e.offsetX;
890
+ y = e.offsetY;
891
+
892
+ } else if (RG.ISWEBKIT) {
893
+ x = e.offsetX - paddingLeft - borderLeft;
894
+ y = e.offsetY - paddingTop - borderTop;
895
+
896
+ } else if (RG.ISIE) {
897
+ x = e.offsetX - paddingLeft;
898
+ y = e.offsetY - paddingTop;
899
+
900
+ } else {
901
+ x = e.offsetX;
902
+ y = e.offsetY;
903
+ }
904
+
905
+
906
+
907
+
908
+ } else {
909
+
910
+
911
+
912
+
913
+ if (!RG.ISIE && !RG.ISOPERA) {
914
+ x = e.offsetX - borderLeft - paddingLeft;
915
+ y = e.offsetY - borderTop - paddingTop;
916
+
917
+ } else if (RG.ISIE) {
918
+ x = e. offsetX - paddingLeft;
919
+ y = e.offsetY - paddingTop;
920
+
921
+ } else {
922
+ x = e.offsetX;
923
+ y = e.offsetY;
924
+ }
925
+ }
926
+
927
+ } else {
928
+
929
+ if (typeof el.offsetParent !== 'undefined') {
930
+ do {
931
+ offsetX += el.offsetLeft;
932
+ offsetY += el.offsetTop;
933
+ } while ((el = el.offsetParent));
934
+ }
935
+
936
+ x = e.pageX - offsetX - additionalX;
937
+ y = e.pageY - offsetY - additionalY;
938
+
939
+ x -= (2 * (parseInt(document.body.style.borderLeftWidth) || 0));
940
+ y -= (2 * (parseInt(document.body.style.borderTopWidth) || 0));
941
+
942
+ //x += (parseInt(caStyle.borderLeftWidth) || 0);
943
+ //y += (parseInt(caStyle.borderTopWidth) || 0);
944
+ }
945
+
946
+ // We return a javascript array with x and y defined
947
+ return [x, y];
948
+ };
949
+
950
+
951
+
952
+
953
+ /**
954
+ * This function returns a two element array of the canvas x/y position in
955
+ * relation to the page
956
+ *
957
+ * @param object canvas
958
+ */
959
+ RG.getCanvasXY = function (canvas)
960
+ {
961
+ var x = 0;
962
+ var y = 0;
963
+ var el = canvas; // !!!
964
+
965
+ do {
966
+
967
+ x += el.offsetLeft;
968
+ y += el.offsetTop;
969
+
970
+ // ACCOUNT FOR TABLES IN wEBkIT
971
+ if (el.tagName.toLowerCase() == 'table' && (RG.ISCHROME || RG.ISSAFARI)) {
972
+ x += parseInt(el.border) || 0;
973
+ y += parseInt(el.border) || 0;
974
+ }
975
+
976
+ el = el.offsetParent;
977
+
978
+ } while (el && el.tagName.toLowerCase() != 'body');
979
+
980
+
981
+ var paddingLeft = canvas.style.paddingLeft ? parseInt(canvas.style.paddingLeft) : 0;
982
+ var paddingTop = canvas.style.paddingTop ? parseInt(canvas.style.paddingTop) : 0;
983
+ var borderLeft = canvas.style.borderLeftWidth ? parseInt(canvas.style.borderLeftWidth) : 0;
984
+ var borderTop = canvas.style.borderTopWidth ? parseInt(canvas.style.borderTopWidth) : 0;
985
+
986
+ if (navigator.userAgent.indexOf('Firefox') > 0) {
987
+ x += parseInt(document.body.style.borderLeftWidth) || 0;
988
+ y += parseInt(document.body.style.borderTopWidth) || 0;
989
+ }
990
+
991
+ return [x + paddingLeft + borderLeft, y + paddingTop + borderTop];
992
+ };
993
+
994
+
995
+
996
+
997
+ /**
998
+ * This function determines whther a canvas is fixed (CSS positioning) or not. If not it returns
999
+ * false. If it is then the element that is fixed is returned (it may be a parent of the canvas).
1000
+ *
1001
+ * @return Either false or the fixed positioned element
1002
+ */
1003
+ RG.isFixed = function (canvas)
1004
+ {
1005
+ var obj = canvas;
1006
+ var i = 0;
1007
+
1008
+ while (obj && obj.tagName.toLowerCase() != 'body' && i < 99) {
1009
+
1010
+ if (obj.style.position == 'fixed') {
1011
+ return obj;
1012
+ }
1013
+
1014
+ obj = obj.offsetParent;
1015
+ }
1016
+
1017
+ return false;
1018
+ };
1019
+
1020
+
1021
+
1022
+
1023
+ /**
1024
+ * Registers a graph object (used when the canvas is redrawn)
1025
+ *
1026
+ * @param object obj The object to be registered
1027
+ */
1028
+ RG.register =
1029
+ RG.Register = function (obj)
1030
+ {
1031
+ // Checking this property ensures the object is only registered once
1032
+ if (!obj.Get('chart.noregister')) {
1033
+ // As of 21st/1/2012 the object registry is now used
1034
+ RGraph.ObjectRegistry.Add(obj);
1035
+ obj.Set('chart.noregister', true);
1036
+ }
1037
+ };
1038
+
1039
+
1040
+
1041
+
1042
+ /**
1043
+ * Causes all registered objects to be redrawn
1044
+ *
1045
+ * @param string An optional color to use to clear the canvas
1046
+ */
1047
+ RG.redraw =
1048
+ RG.Redraw = function ()
1049
+ {
1050
+ var objectRegistry = RGraph.ObjectRegistry.objects.byCanvasID;
1051
+
1052
+ // Get all of the canvas tags on the page
1053
+ var tags = document.getElementsByTagName('canvas');
1054
+
1055
+ for (var i=0,len=tags.length; i<len; ++i) {
1056
+ if (tags[i].__object__ && tags[i].__object__.isRGraph) {
1057
+
1058
+ // Only clear the canvas if it's not Trace'ing - this applies to the Line/Scatter Trace effects
1059
+ if (!tags[i].noclear) {
1060
+ RGraph.clear(tags[i], arguments[0] ? arguments[0] : null);
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ // Go through the object registry and redraw *all* of the canvas'es that have been registered
1066
+ for (var i=0,len=objectRegistry.length; i<len; ++i) {
1067
+ if (objectRegistry[i]) {
1068
+ var id = objectRegistry[i][0];
1069
+ objectRegistry[i][1].Draw();
1070
+ }
1071
+ }
1072
+ };
1073
+
1074
+
1075
+
1076
+
1077
+ /**
1078
+ * Causes all registered objects ON THE GIVEN CANVAS to be redrawn
1079
+ *
1080
+ * @param canvas object The canvas object to redraw
1081
+ * @param bool Optional boolean which defaults to true and determines whether to clear the canvas
1082
+ */
1083
+ RG.redrawCanvas =
1084
+ RG.RedrawCanvas = function (ca)
1085
+ {
1086
+ var objects = RG.ObjectRegistry.getObjectsByCanvasID(ca.id);
1087
+
1088
+ /**
1089
+ * First clear the canvas
1090
+ */
1091
+ if (!arguments[1] || (typeof arguments[1] === 'boolean' && !arguments[1] == false) ) {
1092
+ var color = arguments[2] || ca.__object__.get('clearto') || 'transparent';
1093
+ RG.clear(ca, color);
1094
+ }
1095
+
1096
+ /**
1097
+ * Now redraw all the charts associated with that canvas
1098
+ */
1099
+ for (var i=0,len=objects.length; i<len; ++i) {
1100
+ if (objects[i]) {
1101
+ if (objects[i] && objects[i].isRGraph) { // Is it an RGraph object ??
1102
+ objects[i].Draw();
1103
+ }
1104
+ }
1105
+ }
1106
+ };
1107
+
1108
+
1109
+
1110
+
1111
+ /**
1112
+ * This function draws the background for the bar chart, line chart and scatter chart.
1113
+ *
1114
+ * @param object obj The graph object
1115
+ */
1116
+ RG.Background.draw =
1117
+ RG.background.draw =
1118
+ RG.background.Draw = function (obj)
1119
+ {
1120
+ var func = function (obj, canvas, context)
1121
+ {
1122
+ var ca = canvas,
1123
+ co = context,
1124
+ prop = obj.properties,
1125
+
1126
+ height = 0,
1127
+ gutterLeft = obj.gutterLeft,
1128
+ gutterRight = obj.gutterRight,
1129
+ gutterTop = obj.gutterTop,
1130
+ gutterBottom = obj.gutterBottom,
1131
+ variant = prop['chart.variant']
1132
+
1133
+ co.fillStyle = prop['chart.text.color'];
1134
+
1135
+ // If it's a bar and 3D variant, translate
1136
+ if (variant == '3d') {
1137
+ co.save();
1138
+ co.translate(prop['chart.variant.threed.offsetx'], -1 * prop['chart.variant.threed.offsety']);
1139
+ }
1140
+
1141
+ // X axis title
1142
+ if (typeof prop['chart.title.xaxis'] === 'string' && prop['chart.title.xaxis'].length) {
1143
+
1144
+ var size = prop['chart.text.size'] + 2;
1145
+ var font = prop['chart.text.font'];
1146
+ var bold = prop['chart.title.xaxis.bold'];
1147
+
1148
+ if (typeof(prop['chart.title.xaxis.size']) == 'number') {
1149
+ size = prop['chart.title.xaxis.size'];
1150
+ }
1151
+
1152
+ if (typeof(prop['chart.title.xaxis.font']) == 'string') {
1153
+ font = prop['chart.title.xaxis.font'];
1154
+ }
1155
+
1156
+ var hpos = ((ca.width - gutterLeft - gutterRight) / 2) + gutterLeft;
1157
+ var vpos = ca.height - gutterBottom + 25;
1158
+
1159
+ if (typeof prop['chart.title.xaxis.pos'] === 'number') {
1160
+ vpos = ca.height - (gutterBottom * prop['chart.title.xaxis.pos']);
1161
+ }
1162
+
1163
+
1164
+
1165
+
1166
+ // Specifically specified X/Y positions
1167
+ if (typeof prop['chart.title.xaxis.x'] === 'number') {
1168
+ hpos = prop['chart.title.xaxis.x'];
1169
+ }
1170
+
1171
+ if (typeof prop['chart.title.xaxis.y'] === 'number') {
1172
+ vpos = prop['chart.title.xaxis.y'];
1173
+ }
1174
+
1175
+
1176
+
1177
+
1178
+ RG.Text2(co, {
1179
+ 'font':font,
1180
+ 'size':size,
1181
+ 'x':hpos,
1182
+ 'y':vpos,
1183
+ 'text':prop['chart.title.xaxis'],
1184
+ 'halign':'center',
1185
+ 'valign':'center',
1186
+ 'bold':bold,
1187
+ 'tag': 'title xaxis'
1188
+ });
1189
+ }
1190
+
1191
+ // Y axis title
1192
+ if (typeof(prop['chart.title.yaxis']) == 'string' && prop['chart.title.yaxis'].length) {
1193
+
1194
+ var size = prop['chart.text.size'] + 2;
1195
+ var font = prop['chart.text.font'];
1196
+ var angle = 270;
1197
+ var bold = prop['chart.title.yaxis.bold'];
1198
+ var color = prop['chart.title.yaxis.color'];
1199
+
1200
+ if (typeof(prop['chart.title.yaxis.pos']) == 'number') {
1201
+ var yaxis_title_pos = prop['chart.title.yaxis.pos'] * gutterLeft;
1202
+ } else {
1203
+ var yaxis_title_pos = ((gutterLeft - 25) / gutterLeft) * gutterLeft;
1204
+ }
1205
+
1206
+ if (typeof prop['chart.title.yaxis.size'] === 'number') {
1207
+ size = prop['chart.title.yaxis.size'];
1208
+ }
1209
+
1210
+ if (typeof prop['chart.title.yaxis.font'] === 'string') {
1211
+ font = prop['chart.title.yaxis.font'];
1212
+ }
1213
+
1214
+ if ( prop['chart.title.yaxis.align'] == 'right'
1215
+ || prop['chart.title.yaxis.position'] == 'right'
1216
+ || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'right' && typeof prop['chart.title.yaxis.align'] === 'undefined' && typeof prop['chart.title.yaxis.position'] === 'undefined')
1217
+ ) {
1218
+
1219
+ angle = 90;
1220
+ yaxis_title_pos = prop['chart.title.yaxis.pos'] ? (ca.width - gutterRight) + (prop['chart.title.yaxis.pos'] * gutterRight) :
1221
+ ca.width - gutterRight + prop['chart.text.size'] + 5;
1222
+ } else {
1223
+ yaxis_title_pos = yaxis_title_pos;
1224
+ }
1225
+
1226
+ var y = ((ca.height - gutterTop - gutterBottom) / 2) + gutterTop;
1227
+
1228
+ // Specifically specified X/Y positions
1229
+ if (typeof prop['chart.title.yaxis.x'] === 'number') {
1230
+ yaxis_title_pos = prop['chart.title.yaxis.x'];
1231
+ }
1232
+
1233
+ if (typeof prop['chart.title.yaxis.y'] === 'number') {
1234
+ y = prop['chart.title.yaxis.y'];
1235
+ }
1236
+
1237
+ co.fillStyle = color;
1238
+ RG.text2(co, {
1239
+ 'font':font,
1240
+ 'size':size,
1241
+ 'x':yaxis_title_pos,
1242
+ 'y':y,
1243
+ 'valign':'center',
1244
+ 'halign':'center',
1245
+ 'angle':angle,
1246
+ 'bold':bold,
1247
+ 'text':prop['chart.title.yaxis'],
1248
+ 'tag':'title yaxis'
1249
+ });
1250
+ }
1251
+
1252
+ /**
1253
+ * If the background color is spec ified - draw that. It's a rectangle that fills the
1254
+ * entire area within the gutters
1255
+ */
1256
+ var bgcolor = prop['chart.background.color'];
1257
+ if (bgcolor) {
1258
+ co.fillStyle = bgcolor;
1259
+ co.fillRect(gutterLeft + 0.5, gutterTop + 0.5, ca.width - gutterLeft - gutterRight, ca.height - gutterTop - gutterBottom);
1260
+ }
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+
1269
+
1270
+
1271
+
1272
+
1273
+
1274
+
1275
+
1276
+
1277
+
1278
+
1279
+
1280
+ /**
1281
+ * Draw horizontal background bars
1282
+ */
1283
+ var numbars = (prop['chart.ylabels.count'] || 5);
1284
+ var barHeight = (ca.height - gutterBottom - gutterTop) / numbars;
1285
+
1286
+ co.beginPath();
1287
+ co.fillStyle = prop['chart.background.barcolor1'];
1288
+ co.strokeStyle = co.fillStyle;
1289
+ height = (ca.height - gutterBottom);
1290
+
1291
+ for (var i=0; i<numbars; i+=2) {
1292
+ co.rect(gutterLeft,
1293
+ (i * barHeight) + gutterTop,
1294
+ ca.width - gutterLeft - gutterRight,
1295
+ barHeight
1296
+ );
1297
+ }
1298
+ co.fill();
1299
+
1300
+
1301
+
1302
+ co.beginPath();
1303
+ co.fillStyle = prop['chart.background.barcolor2'];
1304
+ co.strokeStyle = co.fillStyle;
1305
+
1306
+ for (var i=1; i<numbars; i+=2) {
1307
+ co.rect(
1308
+ gutterLeft,
1309
+ (i * barHeight) + gutterTop,
1310
+ ca.width - gutterLeft - gutterRight,
1311
+ barHeight
1312
+ );
1313
+ }
1314
+
1315
+ co.fill();
1316
+
1317
+
1318
+
1319
+
1320
+
1321
+
1322
+
1323
+
1324
+
1325
+
1326
+
1327
+
1328
+
1329
+
1330
+
1331
+
1332
+
1333
+
1334
+
1335
+
1336
+
1337
+
1338
+
1339
+
1340
+ // Draw the background grid
1341
+ if (prop['chart.background.grid']) {
1342
+
1343
+ // If autofit is specified, use the .numhlines and .numvlines along with the width to work
1344
+ // out the hsize and vsize
1345
+ if (prop['chart.background.grid.autofit']) {
1346
+
1347
+ /**
1348
+ * Align the grid to the tickmarks
1349
+ */
1350
+ if (prop['chart.background.grid.autofit.align']) {
1351
+
1352
+ // Align the horizontal lines
1353
+ if (obj.type === 'hbar') {
1354
+ obj.set('chart.background.grid.autofit.numhlines', obj.data.length);
1355
+ }
1356
+
1357
+ // Align the vertical lines for the line
1358
+ if (obj.type === 'line') {
1359
+ if (typeof prop['chart.background.grid.autofit.numvlines'] === 'number') {
1360
+ // Nada
1361
+ } else if (prop['chart.labels'] && prop['chart.labels'].length) {
1362
+ obj.Set('chart.background.grid.autofit.numvlines', prop['chart.labels'].length - 1);
1363
+ } else {
1364
+ obj.Set('chart.background.grid.autofit.numvlines', obj.data[0].length - 1);
1365
+ }
1366
+ } else if (obj.type === 'waterfall') {
1367
+ obj.set(
1368
+ 'backgroundGridAutofitNumvlines',
1369
+ obj.data.length + (prop['chart.total'] ? 1 : 0)
1370
+ );
1371
+
1372
+
1373
+ // Align the vertical lines for the bar, Scatter
1374
+ } else if ( (
1375
+ obj.type === 'bar' ||
1376
+ obj.type === 'scatter'
1377
+ )
1378
+
1379
+ && (
1380
+ (prop['chart.labels'] && prop['chart.labels'].length)
1381
+ || obj.type === 'bar'
1382
+ )
1383
+ ) {
1384
+
1385
+ var len = (prop['chart.labels'] && prop['chart.labels'].length) || obj.data.length;
1386
+
1387
+
1388
+ obj.set({
1389
+ backgroundGridAutofitNumvlines: len
1390
+ });
1391
+
1392
+ // Gantt
1393
+ } else if (obj.type === 'gantt') {
1394
+
1395
+ if (typeof obj.get('chart.background.grid.autofit.numvlines') === 'number') {
1396
+ // Nothing to do here
1397
+ } else {
1398
+ obj.set('chart.background.grid.autofit.numvlines', prop['chart.xmax']);
1399
+ }
1400
+
1401
+ obj.set('chart.background.grid.autofit.numhlines', obj.data.length);
1402
+
1403
+ // HBar
1404
+ } else if (obj.type === 'hbar' && RG.isNull(prop['chart.background.grid.autofit.numhlines']) ) {
1405
+ obj.set('chart.background.grid.autofit.numhlines', obj.data.length);
1406
+ }
1407
+ }
1408
+
1409
+ var vsize = ((ca.width - gutterLeft - gutterRight)) / prop['chart.background.grid.autofit.numvlines'];
1410
+ var hsize = (ca.height - gutterTop - gutterBottom) / prop['chart.background.grid.autofit.numhlines'];
1411
+
1412
+ obj.Set('chart.background.grid.vsize', vsize);
1413
+ obj.Set('chart.background.grid.hsize', hsize);
1414
+ }
1415
+
1416
+ co.beginPath();
1417
+ co.lineWidth = prop['chart.background.grid.width'] ? prop['chart.background.grid.width'] : 1;
1418
+ co.strokeStyle = prop['chart.background.grid.color'];
1419
+
1420
+ // Dashed background grid
1421
+ if (prop['chart.background.grid.dashed'] && typeof co.setLineDash == 'function') {
1422
+ co.setLineDash([3,5]);
1423
+ }
1424
+
1425
+ // Dotted background grid
1426
+ if (prop['chart.background.grid.dotted'] && typeof co.setLineDash == 'function') {
1427
+ co.setLineDash([1,3]);
1428
+ }
1429
+
1430
+ co.beginPath();
1431
+
1432
+
1433
+ // Draw the horizontal lines
1434
+ if (prop['chart.background.grid.hlines']) {
1435
+ height = (ca.height - gutterBottom)
1436
+ var hsize = prop['chart.background.grid.hsize'];
1437
+ for (y=gutterTop; y<=height; y+=hsize) {
1438
+ context.moveTo(gutterLeft, ma.round(y));
1439
+ context.lineTo(ca.width - gutterRight, ma.round(y));
1440
+ }
1441
+ }
1442
+
1443
+ if (prop['chart.background.grid.vlines']) {
1444
+ // Draw the vertical lines
1445
+ var width = (ca.width - gutterRight)
1446
+ var vsize = prop['chart.background.grid.vsize'];
1447
+
1448
+ for (x=gutterLeft; x<=width; x+=vsize) {
1449
+ co.moveTo(ma.round(x), gutterTop);
1450
+ co.lineTo(ma.round(x), ca.height - gutterBottom);
1451
+ }
1452
+ }
1453
+
1454
+ if (prop['chart.background.grid.border']) {
1455
+ // Make sure a rectangle, the same colour as the grid goes around the graph
1456
+ co.strokeStyle = prop['chart.background.grid.color'];
1457
+ co.strokeRect(ma.round(gutterLeft), ma.round(gutterTop), ca.width - gutterLeft - gutterRight, ca.height - gutterTop - gutterBottom);
1458
+ }
1459
+ }
1460
+
1461
+ co.stroke();
1462
+
1463
+
1464
+
1465
+ // Necessary to ensure the gris drawn before continuing
1466
+ co.beginPath();
1467
+ co.closePath();
1468
+
1469
+
1470
+
1471
+ // If it's a bar and 3D variant, translate
1472
+ if (variant == '3d') {
1473
+ co.restore();
1474
+ }
1475
+
1476
+ // Reset the line dash
1477
+ if (typeof co.setLineDash == 'function') {
1478
+ co.setLineDash([1,0]);
1479
+ }
1480
+
1481
+ // Draw the title if one is set
1482
+ if ( typeof(prop['chart.title']) == 'string') {
1483
+
1484
+ if (obj.type == 'gantt') {
1485
+ gutterTop -= 10;
1486
+ }
1487
+
1488
+ RG.drawTitle(
1489
+ // Because of caching the obj variablee cannot be used here
1490
+ {context: co, canvas: ca, properties: prop},
1491
+ prop['chart.title'],
1492
+ gutterTop,
1493
+ null,
1494
+ prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2,
1495
+ {canvas: ca, context: co}
1496
+ );
1497
+ }
1498
+
1499
+ co.stroke();
1500
+ }
1501
+
1502
+ // Now a cached draw in newer browsers
1503
+ RG.ISOLD ? func(obj, obj.canvas, obj.context) : RG.cachedDraw(obj, obj.uid + '_background', func);
1504
+ };
1505
+
1506
+
1507
+
1508
+
1509
+ /**
1510
+ * Formats a number with thousand seperators so it's easier to read
1511
+ *
1512
+ * @param integer obj The chart object
1513
+ * @param integer num The number to format
1514
+ * @param string The (optional) string to prepend to the string
1515
+ * @param string The (optional) string to append to the string
1516
+ * @return string The formatted number
1517
+ */
1518
+ RG.numberFormat =
1519
+ RG.number_format = function (obj, num)
1520
+ {
1521
+ var ca = obj.canvas;
1522
+ var co = obj.context;
1523
+ var prop = obj.properties;
1524
+
1525
+ var i;
1526
+ var prepend = arguments[2] ? String(arguments[2]) : '';
1527
+ var append = arguments[3] ? String(arguments[3]) : '';
1528
+ var output = '';
1529
+ var decimal = '';
1530
+ var decimal_seperator = typeof prop['chart.scale.point'] == 'string' ? prop['chart.scale.point'] : '.';
1531
+ var thousand_seperator = typeof prop['chart.scale.thousand'] == 'string' ? prop['chart.scale.thousand'] : ',';
1532
+ RegExp.$1 = '';
1533
+ var i,j;
1534
+
1535
+ if (typeof prop['chart.scale.formatter'] === 'function') {
1536
+ return prop['chart.scale.formatter'](obj, num);
1537
+ }
1538
+
1539
+ // Ignore the preformatted version of "1e-2"
1540
+ if (String(num).indexOf('e') > 0) {
1541
+ return String(prepend + String(num) + append);
1542
+ }
1543
+
1544
+ // We need then number as a string
1545
+ num = String(num);
1546
+
1547
+ // Take off the decimal part - we re-append it later
1548
+ if (num.indexOf('.') > 0) {
1549
+ var tmp = num;
1550
+ num = num.replace(/\.(.*)/, ''); // The front part of the number
1551
+ decimal = tmp.replace(/(.*)\.(.*)/, '$2'); // The decimal part of the number
1552
+ }
1553
+
1554
+ // Thousand seperator
1555
+ //var seperator = arguments[1] ? String(arguments[1]) : ',';
1556
+ var seperator = thousand_seperator;
1557
+
1558
+ /**
1559
+ * Work backwards adding the thousand seperators
1560
+ */
1561
+ var foundPoint;
1562
+ for (i=(num.length - 1),j=0; i>=0; j++,i--) {
1563
+ var character = num.charAt(i);
1564
+
1565
+ if ( j % 3 == 0 && j != 0) {
1566
+ output += seperator;
1567
+ }
1568
+
1569
+ /**
1570
+ * Build the output
1571
+ */
1572
+ output += character;
1573
+ }
1574
+
1575
+ /**
1576
+ * Now need to reverse the string
1577
+ */
1578
+ var rev = output;
1579
+ output = '';
1580
+ for (i=(rev.length - 1); i>=0; i--) {
1581
+ output += rev.charAt(i);
1582
+ }
1583
+
1584
+ // Tidy up
1585
+ //output = output.replace(/^-,/, '-');
1586
+ if (output.indexOf('-' + prop['chart.scale.thousand']) == 0) {
1587
+ output = '-' + output.substr(('-' + prop['chart.scale.thousand']).length);
1588
+ }
1589
+
1590
+ // Reappend the decimal
1591
+ if (decimal.length) {
1592
+ output = output + decimal_seperator + decimal;
1593
+ decimal = '';
1594
+ RegExp.$1 = '';
1595
+ }
1596
+
1597
+ // Minor bugette
1598
+ if (output.charAt(0) == '-') {
1599
+ output = output.replace(/-/, '');
1600
+ prepend = '-' + prepend;
1601
+ }
1602
+
1603
+ return prepend + output + append;
1604
+ };
1605
+
1606
+
1607
+
1608
+
1609
+ /**
1610
+ * Draws horizontal coloured bars on something like the bar, line or scatter
1611
+ */
1612
+ RG.drawBars =
1613
+ RG.DrawBars = function (obj)
1614
+ {
1615
+ var prop = obj.properties;
1616
+ var co = obj.context;
1617
+ var ca = obj.canvas;
1618
+ var hbars = prop['chart.background.hbars'];
1619
+
1620
+ if (hbars === null) {
1621
+ return;
1622
+ }
1623
+
1624
+ /**
1625
+ * Draws a horizontal bar
1626
+ */
1627
+ co.beginPath();
1628
+
1629
+ for (i=0,len=hbars.length; i<len; ++i) {
1630
+
1631
+ var start = hbars[i][0];
1632
+ var length = hbars[i][1];
1633
+ var color = hbars[i][2];
1634
+
1635
+
1636
+ // Perform some bounds checking
1637
+ if(RG.is_null(start))start = obj.scale2.max
1638
+ if (start > obj.scale2.max) start = obj.scale2.max;
1639
+ if (RG.is_null(length)) length = obj.scale2.max - start;
1640
+ if (start + length > obj.scale2.max) length = obj.scale2.max - start;
1641
+ if (start + length < (-1 * obj.scale2.max) ) length = (-1 * obj.scale2.max) - start;
1642
+
1643
+ if (prop['chart.xaxispos'] == 'center' && start == obj.scale2.max && length < (obj.scale2.max * -2)) {
1644
+ length = obj.scale2.max * -2;
1645
+ }
1646
+
1647
+
1648
+ /**
1649
+ * Draw the bar
1650
+ */
1651
+ var x = prop['chart.gutter.left'];
1652
+ var y = obj.getYCoord(start);
1653
+ var w = ca.width - prop['chart.gutter.left'] - prop['chart.gutter.right'];
1654
+ var h = obj.getYCoord(start + length) - y;
1655
+
1656
+ // Accommodate Opera :-/
1657
+ if (RG.ISOPERA != -1 && prop['chart.xaxispos'] == 'center' && h < 0) {
1658
+ h *= -1;
1659
+ y = y - h;
1660
+ }
1661
+
1662
+ /**
1663
+ * Account for X axis at the top
1664
+ */
1665
+ if (prop['chart.xaxispos'] == 'top') {
1666
+ y = ca.height - y;
1667
+ h *= -1;
1668
+ }
1669
+
1670
+ co.fillStyle = color;
1671
+ co.fillRect(x, y, w, h);
1672
+ }
1673
+ /*
1674
+
1675
+
1676
+
1677
+
1678
+
1679
+ // If the X axis is at the bottom, and a negative max is given, warn the user
1680
+ if (obj.Get('chart.xaxispos') == 'bottom' && (hbars[i][0] < 0 || (hbars[i][1] + hbars[i][1] < 0)) ) {
1681
+ alert('[' + obj.type.toUpperCase() + ' (ID: ' + obj.id + ') BACKGROUND HBARS] You have a negative value in one of your background hbars values, whilst the X axis is in the center');
1682
+ }
1683
+
1684
+ var ystart = (obj.grapharea - (((hbars[i][0] - obj.scale2.min) / (obj.scale2.max - obj.scale2.min)) * obj.grapharea));
1685
+ //var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / (obj.scale2.max - obj.scale2.min)) * obj.grapharea;
1686
+ var height = obj.getYCoord(hbars[i][0]) - obj.getYCoord(hbars[i][1]);
1687
+
1688
+ // Account for the X axis being in the center
1689
+ if (obj.Get('chart.xaxispos') == 'center') {
1690
+ ystart /= 2;
1691
+ //height /= 2;
1692
+ }
1693
+
1694
+ ystart += obj.Get('chart.gutter.top')
1695
+
1696
+ var x = obj.Get('chart.gutter.left');
1697
+ var y = ystart - height;
1698
+ var w = obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right');
1699
+ var h = height;
1700
+
1701
+ // Accommodate Opera :-/
1702
+ if (navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0) {
1703
+ h *= -1;
1704
+ y = y - h;
1705
+ }
1706
+
1707
+ /**
1708
+ * Account for X axis at the top
1709
+ */
1710
+ //if (obj.Get('chart.xaxispos') == 'top') {
1711
+ // y = obj.canvas.height - y;
1712
+ // h *= -1;
1713
+ //}
1714
+
1715
+ //obj.context.fillStyle = hbars[i][2];
1716
+ //obj.context.fillRect(x, y, w, h);
1717
+ //}
1718
+ };
1719
+
1720
+
1721
+
1722
+
1723
+ /**
1724
+ * Draws in-graph labels.
1725
+ *
1726
+ * @param object obj The graph object
1727
+ */
1728
+ RG.drawInGraphLabels =
1729
+ RG.DrawInGraphLabels = function (obj)
1730
+ {
1731
+ var ca = obj.canvas;
1732
+ var co = obj.context;
1733
+ var prop = obj.properties;
1734
+ var labels = prop['chart.labels.ingraph'];
1735
+ var labels_processed = [];
1736
+
1737
+ // Defaults
1738
+ var fgcolor = 'black';
1739
+ var bgcolor = 'white';
1740
+ var direction = 1;
1741
+
1742
+ if (!labels) {
1743
+ return;
1744
+ }
1745
+
1746
+ /**
1747
+ * Preprocess the labels array. Numbers are expanded
1748
+ */
1749
+ for (var i=0,len=labels.length; i<len; i+=1) {
1750
+ if (typeof labels[i] === 'number') {
1751
+ for (var j=0; j<labels[i]; ++j) {
1752
+ labels_processed.push(null);
1753
+ }
1754
+ } else if (typeof labels[i] === 'string' || typeof labels[i] === 'object') {
1755
+ labels_processed.push(labels[i]);
1756
+
1757
+ } else {
1758
+ labels_processed.push('');
1759
+ }
1760
+ }
1761
+
1762
+ /**
1763
+ * Turn off any shadow
1764
+ */
1765
+ RG.NoShadow(obj);
1766
+
1767
+ if (labels_processed && labels_processed.length > 0) {
1768
+
1769
+ for (var i=0,len=labels_processed.length; i<len; i+=1) {
1770
+ if (labels_processed[i]) {
1771
+ var coords = obj.coords[i];
1772
+
1773
+ if (coords && coords.length > 0) {
1774
+ var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]);
1775
+ var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]);
1776
+ var length = typeof labels_processed[i][4] === 'number' ? labels_processed[i][4] : 25;
1777
+
1778
+ co.beginPath();
1779
+ co.fillStyle = 'black';
1780
+ co.strokeStyle = 'black';
1781
+
1782
+
1783
+ if (obj.type === 'bar') {
1784
+
1785
+ /**
1786
+ * X axis at the top
1787
+ */
1788
+ if (obj.Get('chart.xaxispos') == 'top') {
1789
+ length *= -1;
1790
+ }
1791
+
1792
+ if (prop['chart.variant'] == 'dot') {
1793
+ co.moveTo(ma.round(x), obj.coords[i][1] - 5);
1794
+ co.lineTo(ma.round(x), obj.coords[i][1] - 5 - length);
1795
+
1796
+ var text_x = ma.round(x);
1797
+ var text_y = obj.coords[i][1] - 5 - length;
1798
+
1799
+ } else if (prop['chart.variant'] == 'arrow') {
1800
+ co.moveTo(ma.round(x), obj.coords[i][1] - 5);
1801
+ co.lineTo(ma.round(x), obj.coords[i][1] - 5 - length);
1802
+
1803
+ var text_x = ma.round(x);
1804
+ var text_y = obj.coords[i][1] - 5 - length;
1805
+
1806
+ } else {
1807
+
1808
+ co.arc(ma.round(x), y, 2.5, 0, 6.28, 0);
1809
+ co.moveTo(ma.round(x), y);
1810
+ co.lineTo(ma.round(x), y - length);
1811
+
1812
+ var text_x = ma.round(x);
1813
+ var text_y = y - length;
1814
+ }
1815
+
1816
+ co.stroke();
1817
+ co.fill();
1818
+
1819
+
1820
+ } else if (obj.type == 'line') {
1821
+
1822
+ if (
1823
+ typeof labels_processed[i] == 'object' &&
1824
+ typeof labels_processed[i][3] == 'number' &&
1825
+ labels_processed[i][3] == -1
1826
+ ) {
1827
+
1828
+ co.moveTo(ma.round(x), y + 5);
1829
+ co.lineTo(ma.round(x), y + 5 + length);
1830
+
1831
+ co.stroke();
1832
+ co.beginPath();
1833
+
1834
+ // This draws the arrow
1835
+ co.moveTo(ma.round(x), y + 5);
1836
+ co.lineTo(ma.round(x) - 3, y + 10);
1837
+ co.lineTo(ma.round(x) + 3, y + 10);
1838
+ co.closePath();
1839
+
1840
+ var text_x = x;
1841
+ var text_y = y + 5 + length;
1842
+
1843
+ } else {
1844
+
1845
+ var text_x = x;
1846
+ var text_y = y - 5 - length;
1847
+
1848
+ co.moveTo(ma.round(x), y - 5);
1849
+ co.lineTo(ma.round(x), y - 5 - length);
1850
+
1851
+ co.stroke();
1852
+ co.beginPath();
1853
+
1854
+ // This draws the arrow
1855
+ co.moveTo(ma.round(x), y - 5);
1856
+ co.lineTo(ma.round(x) - 3, y - 10);
1857
+ co.lineTo(ma.round(x) + 3, y - 10);
1858
+ co.closePath();
1859
+ }
1860
+
1861
+ co.fill();
1862
+ }
1863
+
1864
+ // Taken out on the 10th Nov 2010 - unnecessary
1865
+ //var width = context.measureText(labels[i]).width;
1866
+
1867
+ co.beginPath();
1868
+
1869
+ // Fore ground color
1870
+ co.fillStyle = (typeof labels_processed[i] === 'object' && typeof labels_processed[i][1] === 'string') ? labels_processed[i][1] : 'black';
1871
+
1872
+ RG.Text2(obj,{'font':prop['chart.text.font'],
1873
+ 'size':prop['chart.text.size'],
1874
+ 'x':text_x,
1875
+ 'y':text_y,
1876
+ 'text': (typeof labels_processed[i] === 'object' && typeof labels_processed[i][0] === 'string') ? labels_processed[i][0] : labels_processed[i],
1877
+ 'valign': 'bottom',
1878
+ 'halign':'center',
1879
+ 'bounding':true,
1880
+ 'bounding.fill': (typeof labels_processed[i] === 'object' && typeof labels_processed[i][2] === 'string') ? labels_processed[i][2] : 'white',
1881
+ 'tag':'labels ingraph'
1882
+ });
1883
+ co.fill();
1884
+ }
1885
+ }
1886
+ }
1887
+ }
1888
+ };
1889
+
1890
+
1891
+
1892
+
1893
+ /**
1894
+ * This function "fills in" key missing properties that various implementations lack
1895
+ *
1896
+ * @param object e The event object
1897
+ */
1898
+ RG.fixEventObject =
1899
+ RG.FixEventObject = function (e)
1900
+ {
1901
+ if (RG.ISOLD) {
1902
+ var e = event;
1903
+
1904
+ e.pageX = (event.clientX + doc.body.scrollLeft);
1905
+ e.pageY = (event.clientY + doc.body.scrollTop);
1906
+ e.target = event.srcElement;
1907
+
1908
+ if (!doc.body.scrollTop && doc.documentElement.scrollTop) {
1909
+ e.pageX += parseInt(doc.documentElement.scrollLeft);
1910
+ e.pageY += parseInt(doc.documentElement.scrollTop);
1911
+ }
1912
+ }
1913
+
1914
+
1915
+ // Any browser that doesn't implement stopPropagation() (MSIE)
1916
+ if (!e.stopPropagation) {
1917
+ e.stopPropagation = function () {window.event.cancelBubble = true;}
1918
+ }
1919
+
1920
+ return e;
1921
+ };
1922
+
1923
+
1924
+
1925
+
1926
+ /**
1927
+ * Thisz function hides the crosshairs coordinates
1928
+ */
1929
+ RG.hideCrosshairCoords =
1930
+ RG.HideCrosshairCoords = function ()
1931
+ {
1932
+ var div = RG.Registry.Get('chart.coordinates.coords.div');
1933
+
1934
+ if ( div
1935
+ && div.style.opacity == 1
1936
+ && div.__object__.Get('chart.crosshairs.coords.fadeout')
1937
+ ) {
1938
+
1939
+ var style = RG.Registry.Get('chart.coordinates.coords.div').style;
1940
+
1941
+ setTimeout(function() {style.opacity = 0.9;}, 25);
1942
+ setTimeout(function() {style.opacity = 0.8;}, 50);
1943
+ setTimeout(function() {style.opacity = 0.7;}, 75);
1944
+ setTimeout(function() {style.opacity = 0.6;}, 100);
1945
+ setTimeout(function() {style.opacity = 0.5;}, 125);
1946
+ setTimeout(function() {style.opacity = 0.4;}, 150);
1947
+ setTimeout(function() {style.opacity = 0.3;}, 175);
1948
+ setTimeout(function() {style.opacity = 0.2;}, 200);
1949
+ setTimeout(function() {style.opacity = 0.1;}, 225);
1950
+ setTimeout(function() {style.opacity = 0;}, 250);
1951
+ setTimeout(function() {style.display = 'none';}, 275);
1952
+ }
1953
+ };
1954
+
1955
+
1956
+
1957
+
1958
+ /**
1959
+ * Draws the3D axes/background
1960
+ *
1961
+ * @param object obj The chart object
1962
+ */
1963
+ RG.draw3DAxes =
1964
+ RG.Draw3DAxes = function (obj)
1965
+ {
1966
+ var prop = obj.properties;
1967
+ var co = obj.context;
1968
+ var ca = obj.canvas;
1969
+
1970
+ var gutterLeft = obj.gutterLeft,
1971
+ gutterRight = obj.gutterRight,
1972
+ gutterTop = obj.gutterTop,
1973
+ gutterBottom = obj.gutterBottom,
1974
+ xaxispos = prop['chart.xaxispos'],
1975
+ graphArea = ca.height - gutterTop - gutterBottom,
1976
+ halfGraphArea = graphArea / 2,
1977
+ offsetx = prop['chart.variant.threed.offsetx'],
1978
+ offsety = prop['chart.variant.threed.offsety']
1979
+
1980
+ if (!prop['chart.noaxes'] && !prop['chart.noyaxis']) {
1981
+ RG.path(co, [
1982
+ 'b',
1983
+ // Y axis
1984
+ 'm', gutterLeft,gutterTop,
1985
+ 'l',gutterLeft + offsetx,gutterTop - offsety,
1986
+ 'l',gutterLeft + offsetx,ca.height - gutterBottom - offsety,
1987
+ 'l',gutterLeft,ca.height - gutterBottom
1988
+ ]);
1989
+ }
1990
+
1991
+ // X axis
1992
+ if (xaxispos === 'center') {
1993
+ RG.path(co, [
1994
+ 'm',gutterLeft,gutterTop + halfGraphArea,
1995
+ 'l',gutterLeft + offsetx,gutterTop + halfGraphArea - offsety,
1996
+
1997
+ 'l',ca.width - gutterRight + offsetx,gutterTop + halfGraphArea - offsety,
1998
+ 'l',ca.width - gutterRight,gutterTop + halfGraphArea,
1999
+ 'c','s','#aaa','f','#ddd'
2000
+ ]);
2001
+ } else {
2002
+ RG.path(co, [
2003
+ 'm',gutterLeft,ca.height - gutterBottom,
2004
+ 'l',gutterLeft + offsetx,ca.height - gutterBottom - offsety,
2005
+ 'l',ca.width - gutterRight + offsetx,ca.height - gutterBottom - offsety,
2006
+ 'l',ca.width - gutterRight,ca.height - gutterBottom,
2007
+ 'c','s','#aaa','f','#ddd'
2008
+ ]);
2009
+ }
2010
+ };
2011
+
2012
+
2013
+
2014
+
2015
+ /**
2016
+ * Draws a rectangle with curvy corners
2017
+ *
2018
+ * @param co object The context
2019
+ * @param x number The X coordinate (top left of the square)
2020
+ * @param y number The Y coordinate (top left of the square)
2021
+ * @param w number The width of the rectangle
2022
+ * @param h number The height of the rectangle
2023
+ * @param number The radius of the curved corners
2024
+ * @param boolean Whether the top left corner is curvy
2025
+ * @param boolean Whether the top right corner is curvy
2026
+ * @param boolean Whether the bottom right corner is curvy
2027
+ * @param boolean Whether the bottom left corner is curvy
2028
+ */
2029
+ RG.strokedCurvyRect = function (co, x, y, w, h)
2030
+ {
2031
+ // The corner radius
2032
+ var r = arguments[5] ? arguments[5] : 3;
2033
+
2034
+ // The corners
2035
+ var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
2036
+ var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
2037
+ var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
2038
+ var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
2039
+
2040
+ co.beginPath();
2041
+
2042
+ // Top left side
2043
+ co.moveTo(x + (corner_tl ? r : 0), y);
2044
+ co.lineTo(x + w - (corner_tr ? r : 0), y);
2045
+
2046
+ // Top right corner
2047
+ if (corner_tr) {
2048
+ co.arc(x + w - r, y + r, r, RG.PI + RG.HALFPI, RG.TWOPI, false);
2049
+ }
2050
+
2051
+ // Top right side
2052
+ co.lineTo(x + w, y + h - (corner_br ? r : 0) );
2053
+
2054
+ // Bottom right corner
2055
+ if (corner_br) {
2056
+ co.arc(x + w - r, y - r + h, r, RG.TWOPI, RG.HALFPI, false);
2057
+ }
2058
+
2059
+ // Bottom right side
2060
+ co.lineTo(x + (corner_bl ? r : 0), y + h);
2061
+
2062
+ // Bottom left corner
2063
+ if (corner_bl) {
2064
+ co.arc(x + r, y - r + h, r, RG.HALFPI, RG.PI, false);
2065
+ }
2066
+
2067
+ // Bottom left side
2068
+ co.lineTo(x, y + (corner_tl ? r : 0) );
2069
+
2070
+ // Top left corner
2071
+ if (corner_tl) {
2072
+ co.arc(x + r, y + r, r, RG.PI, RG.PI + RG.HALFPI, false);
2073
+ }
2074
+
2075
+ co.stroke();
2076
+ };
2077
+
2078
+
2079
+
2080
+
2081
+ /**
2082
+ * Draws a filled rectangle with curvy corners
2083
+ *
2084
+ * @param context object The context
2085
+ * @param x number The X coordinate (top left of the square)
2086
+ * @param y number The Y coordinate (top left of the square)
2087
+ * @param w number The width of the rectangle
2088
+ * @param h number The height of the rectangle
2089
+ * @param number The radius of the curved corners
2090
+ * @param boolean Whether the top left corner is curvy
2091
+ * @param boolean Whether the top right corner is curvy
2092
+ * @param boolean Whether the bottom right corner is curvy
2093
+ * @param boolean Whether the bottom left corner is curvy
2094
+ */
2095
+ RG.filledCurvyRect = function (co, x, y, w, h)
2096
+ {
2097
+ // The corner radius
2098
+ var r = arguments[5] ? arguments[5] : 3;
2099
+
2100
+ // The corners
2101
+ var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
2102
+ var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
2103
+ var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
2104
+ var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
2105
+
2106
+ co.beginPath();
2107
+
2108
+ // First draw the corners
2109
+
2110
+ // Top left corner
2111
+ if (corner_tl) {
2112
+ co.moveTo(x + r, y + r);
2113
+ co.arc(x + r, y + r, r, RG.PI, RG.PI + RG.HALFPI, false);
2114
+ } else {
2115
+ co.fillRect(x, y, r, r);
2116
+ }
2117
+
2118
+ // Top right corner
2119
+ if (corner_tr) {
2120
+ co.moveTo(x + w - r, y + r);
2121
+ co.arc(x + w - r, y + r, r, RG.PI + RG.HALFPI, 0, false);
2122
+ } else {
2123
+ co.moveTo(x + w - r, y);
2124
+ co.fillRect(x + w - r, y, r, r);
2125
+ }
2126
+
2127
+
2128
+ // Bottom right corner
2129
+ if (corner_br) {
2130
+ co.moveTo(x + w - r, y + h - r);
2131
+ co.arc(x + w - r, y - r + h, r, 0, RG.HALFPI, false);
2132
+ } else {
2133
+ co.moveTo(x + w - r, y + h - r);
2134
+ co.fillRect(x + w - r, y + h - r, r, r);
2135
+ }
2136
+
2137
+ // Bottom left corner
2138
+ if (corner_bl) {
2139
+ co.moveTo(x + r, y + h - r);
2140
+ co.arc(x + r, y - r + h, r, RG.HALFPI, RG.PI, false);
2141
+ } else {
2142
+ co.moveTo(x, y + h - r);
2143
+ co.fillRect(x, y + h - r, r, r);
2144
+ }
2145
+
2146
+ // Now fill it in
2147
+ co.fillRect(x + r, y, w - r - r, h);
2148
+ co.fillRect(x, y + r, r + 1, h - r - r);
2149
+ co.fillRect(x + w - r - 1, y + r, r + 1, h - r - r);
2150
+
2151
+ co.fill();
2152
+ };
2153
+
2154
+
2155
+
2156
+
2157
+ /**
2158
+ * Hides the zoomed canvas
2159
+ */
2160
+ RG.hideZoomedCanvas =
2161
+ RG.HideZoomedCanvas = function ()
2162
+ {
2163
+ var interval = 10;
2164
+ var frames = 15;
2165
+
2166
+ if (typeof RG.zoom_image === 'object') {
2167
+ var obj = RG.zoom_image.obj;
2168
+ var prop = obj.properties;
2169
+ } else {
2170
+ return;
2171
+ }
2172
+
2173
+ if (prop['chart.zoom.fade.out']) {
2174
+ for (var i=frames,j=1; i>=0; --i, ++j) {
2175
+ if (typeof RG.zoom_image === 'object') {
2176
+ setTimeout("RGraph.zoom_image.style.opacity = " + String(i / 10), j * interval);
2177
+ }
2178
+ }
2179
+
2180
+ if (typeof RG.zoom_background === 'object') {
2181
+ setTimeout("RGraph.zoom_background.style.opacity = " + String(i / frames), j * interval);
2182
+ }
2183
+ }
2184
+
2185
+ if (typeof RG.zoom_image === 'object') {
2186
+ setTimeout("RGraph.zoom_image.style.display = 'none'", prop['chart.zoom.fade.out'] ? (frames * interval) + 10 : 0);
2187
+ }
2188
+
2189
+ if (typeof RG.zoom_background === 'object') {
2190
+ setTimeout("RGraph.zoom_background.style.display = 'none'", prop['chart.zoom.fade.out'] ? (frames * interval) + 10 : 0);
2191
+ }
2192
+ };
2193
+
2194
+
2195
+
2196
+
2197
+ /**
2198
+ * Adds an event handler
2199
+ *
2200
+ * @param object obj The graph object
2201
+ * @param string event The name of the event, eg ontooltip
2202
+ * @param object func The callback function
2203
+ */
2204
+ RG.addCustomEventListener =
2205
+ RG.AddCustomEventListener = function (obj, name, func)
2206
+ {
2207
+ var RG = RGraph;
2208
+
2209
+ if (typeof RG.events[obj.uid] === 'undefined') {
2210
+ RG.events[obj.uid] = [];
2211
+ }
2212
+
2213
+ RG.events[obj.uid].push([obj, name, func]);
2214
+
2215
+ return RG.events[obj.uid].length - 1;
2216
+ };
2217
+
2218
+
2219
+
2220
+
2221
+ /**
2222
+ * Used to fire one of the RGraph custom events
2223
+ *
2224
+ * @param object obj The graph object that fires the event
2225
+ * @param string event The name of the event to fire
2226
+ */
2227
+ RG.fireCustomEvent =
2228
+ RG.FireCustomEvent = function (obj, name)
2229
+ {
2230
+ if (obj && obj.isRGraph) {
2231
+
2232
+ // New style of adding custom events
2233
+ if (obj[name]) {
2234
+ (obj[name])(obj);
2235
+ }
2236
+
2237
+ var uid = obj.uid;
2238
+
2239
+ if ( typeof uid === 'string'
2240
+ && typeof RG.events === 'object'
2241
+ && typeof RG.events[uid] === 'object'
2242
+ && RG.events[uid].length > 0) {
2243
+
2244
+ for(var j=0; j<RG.events[uid].length; ++j) {
2245
+ if (RG.events[uid][j] && RG.events[uid][j][1] == name) {
2246
+ RG.events[uid][j][2](obj);
2247
+ }
2248
+ }
2249
+ }
2250
+ }
2251
+ };
2252
+
2253
+
2254
+
2255
+
2256
+ /**
2257
+ * Clears all the custom event listeners that have been registered
2258
+ *
2259
+ * @param string Limits the clearing to this object ID
2260
+ */
2261
+ RGraph.removeAllCustomEventListeners =
2262
+ RGraph.RemoveAllCustomEventListeners = function ()
2263
+ {
2264
+ var id = arguments[0];
2265
+
2266
+ if (id && RG.events[id]) {
2267
+ RG.events[id] = [];
2268
+ } else {
2269
+ RG.events = [];
2270
+ }
2271
+ };
2272
+
2273
+
2274
+
2275
+
2276
+ /**
2277
+ * Clears a particular custom event listener
2278
+ *
2279
+ * @param object obj The graph object
2280
+ * @param number i This is the index that is return by .AddCustomEventListener()
2281
+ */
2282
+ RG.removeCustomEventListener =
2283
+ RG.RemoveCustomEventListener = function (obj, i)
2284
+ {
2285
+ if ( typeof RG.events === 'object'
2286
+ && typeof RG.events[obj.id] === 'object'
2287
+ && typeof RG.events[obj.id][i] === 'object') {
2288
+
2289
+ RG.events[obj.id][i] = null;
2290
+ }
2291
+ };
2292
+
2293
+
2294
+
2295
+
2296
+ /**
2297
+ * This draws the background
2298
+ *
2299
+ * @param object obj The graph object
2300
+ */
2301
+ RG.drawBackgroundImage =
2302
+ RG.DrawBackgroundImage = function (obj)
2303
+ {
2304
+ var prop = obj.properties;
2305
+ var ca = obj.canvas;
2306
+ var co = obj.context;
2307
+
2308
+ if (typeof prop['chart.background.image'] === 'string') {
2309
+ if (typeof ca.__rgraph_background_image__ === 'undefined') {
2310
+ var img = new Image();
2311
+ img.__object__ = obj;
2312
+ img.__canvas__ = ca;
2313
+ img.__context__ = co;
2314
+ img.src = obj.Get('chart.background.image');
2315
+
2316
+ ca.__rgraph_background_image__ = img;
2317
+ } else {
2318
+ img = ca.__rgraph_background_image__;
2319
+ }
2320
+
2321
+ // When the image has loaded - redraw the canvas
2322
+ img.onload = function ()
2323
+ {
2324
+ obj.__rgraph_background_image_loaded__ = true;
2325
+ RG.clear(ca);
2326
+ RG.redrawCanvas(ca);
2327
+ }
2328
+
2329
+ var gutterLeft = obj.gutterLeft;
2330
+ var gutterRight = obj.gutterRight;
2331
+ var gutterTop = obj.gutterTop;
2332
+ var gutterBottom = obj.gutterBottom;
2333
+ var stretch = prop['chart.background.image.stretch'];
2334
+ var align = prop['chart.background.image.align'];
2335
+
2336
+ // Handle chart.background.image.align
2337
+ if (typeof align === 'string') {
2338
+ if (align.indexOf('right') != -1) {
2339
+ var x = ca.width - (prop['chart.background.image.w'] || img.width) - gutterRight;
2340
+ } else {
2341
+ var x = gutterLeft;
2342
+ }
2343
+
2344
+ if (align.indexOf('bottom') != -1) {
2345
+ var y = ca.height - (prop['chart.background.image.h'] || img.height) - gutterBottom;
2346
+ } else {
2347
+ var y = gutterTop;
2348
+ }
2349
+ } else {
2350
+ var x = gutterLeft || 25;
2351
+ var y = gutterTop || 25;
2352
+ }
2353
+
2354
+ // X/Y coords take precedence over the align
2355
+ var x = typeof prop['chart.background.image.x'] === 'number' ? prop['chart.background.image.x'] : x;
2356
+ var y = typeof prop['chart.background.image.y'] === 'number' ? prop['chart.background.image.y'] : y;
2357
+ var w = stretch ? ca.width - gutterLeft - gutterRight : img.width;
2358
+ var h = stretch ? ca.height - gutterTop - gutterBottom : img.height;
2359
+
2360
+ /**
2361
+ * You can now specify the width and height of the image
2362
+ */
2363
+ if (typeof prop['chart.background.image.w'] === 'number') w = prop['chart.background.image.w'];
2364
+ if (typeof prop['chart.background.image.h'] === 'number') h = prop['chart.background.image.h'];
2365
+
2366
+ var oldAlpha = co.globalAlpha;
2367
+ co.globalAlpha = prop['chart.background.image.alpha'];
2368
+ co.drawImage(img,x,y,w, h);
2369
+ co.globalAlpha = oldAlpha;
2370
+ }
2371
+ };
2372
+
2373
+
2374
+
2375
+
2376
+ /**
2377
+ * This function determines wshether an object has tooltips or not
2378
+ *
2379
+ * @param object obj The chart object
2380
+ */
2381
+ RG.hasTooltips = function (obj)
2382
+ {
2383
+ var prop = obj.properties;
2384
+
2385
+ if (typeof prop['chart.tooltips'] == 'object' && prop['chart.tooltips']) {
2386
+ for (var i=0,len=prop['chart.tooltips'].length; i<len; ++i) {
2387
+ if (!RG.is_null(obj.Get('chart.tooltips')[i])) {
2388
+ return true;
2389
+ }
2390
+ }
2391
+ } else if (typeof prop['chart.tooltips'] === 'function') {
2392
+ return true;
2393
+ }
2394
+
2395
+ return false;
2396
+ };
2397
+
2398
+
2399
+
2400
+
2401
+ /**
2402
+ * This function creates a (G)UID which can be used to identify objects.
2403
+ *
2404
+ * @return string (g)uid The (G)UID
2405
+ */
2406
+ RG.createUID =
2407
+ RG.CreateUID = function ()
2408
+ {
2409
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)
2410
+ {
2411
+ var r = ma.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
2412
+ return v.toString(16);
2413
+ });
2414
+ };
2415
+
2416
+
2417
+
2418
+ /**
2419
+ * This is the new object registry, used to facilitate multiple objects per canvas.
2420
+ *
2421
+ * @param object obj The object to register
2422
+ */
2423
+ RG.ObjectRegistry.add =
2424
+ RG.ObjectRegistry.Add = function (obj)
2425
+ {
2426
+ var uid = obj.uid;
2427
+ var id = obj.canvas.id;
2428
+
2429
+ /**
2430
+ * Index the objects by UID
2431
+ */
2432
+ RG.ObjectRegistry.objects.byUID.push([uid, obj]);
2433
+
2434
+ /**
2435
+ * Index the objects by the canvas that they're drawn on
2436
+ */
2437
+ RG.ObjectRegistry.objects.byCanvasID.push([id, obj]);
2438
+ };
2439
+
2440
+
2441
+
2442
+
2443
+ /**
2444
+ * Remove an object from the object registry
2445
+ *
2446
+ * @param object obj The object to remove.
2447
+ */
2448
+ RG.ObjectRegistry.remove =
2449
+ RG.ObjectRegistry.Remove = function (obj)
2450
+ {
2451
+ var id = obj.id;
2452
+ var uid = obj.uid;
2453
+
2454
+ for (var i=0; i<RG.ObjectRegistry.objects.byUID.length; ++i) {
2455
+ if (RG.ObjectRegistry.objects.byUID[i] && RG.ObjectRegistry.objects.byUID[i][1].uid == uid) {
2456
+ RG.ObjectRegistry.objects.byUID[i] = null;
2457
+ }
2458
+ }
2459
+
2460
+
2461
+ for (var i=0; i<RG.ObjectRegistry.objects.byCanvasID.length; ++i) {
2462
+ if ( RG.ObjectRegistry.objects.byCanvasID[i]
2463
+ && RG.ObjectRegistry.objects.byCanvasID[i][1]
2464
+ && RG.ObjectRegistry.objects.byCanvasID[i][1].uid == uid) {
2465
+
2466
+ RG.ObjectRegistry.objects.byCanvasID[i] = null;
2467
+ }
2468
+ }
2469
+ };
2470
+
2471
+
2472
+
2473
+
2474
+ /**
2475
+ * Removes all objects from the ObjectRegistry. If either the ID of a canvas is supplied,
2476
+ * or the canvas itself, then only objects pertaining to that canvas are cleared.
2477
+ *
2478
+ * @param mixed Either a canvas object (as returned by document.getElementById()
2479
+ * or the ID of a canvas (ie a string)
2480
+ */
2481
+ RG.ObjectRegistry.clear =
2482
+ RG.ObjectRegistry.Clear = function ()
2483
+ {
2484
+ // If an ID is supplied restrict the learing to that
2485
+ if (arguments[0]) {
2486
+ var id = (typeof arguments[0] === 'object' ? arguments[0].id : arguments[0]);
2487
+ var objects = RG.ObjectRegistry.getObjectsByCanvasID(id);
2488
+
2489
+ for (var i=0,len=objects.length; i<len; ++i) {
2490
+ RG.ObjectRegistry.remove(objects[i]);
2491
+ }
2492
+
2493
+ } else {
2494
+
2495
+ RG.ObjectRegistry.objects = {};
2496
+ RG.ObjectRegistry.objects.byUID = [];
2497
+ RG.ObjectRegistry.objects.byCanvasID = [];
2498
+ }
2499
+ };
2500
+
2501
+
2502
+
2503
+
2504
+ /**
2505
+ * Lists all objects in the ObjectRegistry
2506
+ *
2507
+ * @param boolean ret Whether to return the list or alert() it
2508
+ */
2509
+ RGraph.ObjectRegistry.list =
2510
+ RGraph.ObjectRegistry.List = function ()
2511
+ {
2512
+ var list = [];
2513
+
2514
+ for (var i=0,len=RG.ObjectRegistry.objects.byUID.length; i<len; ++i) {
2515
+ if (RG.ObjectRegistry.objects.byUID[i]) {
2516
+ list.push(RG.ObjectRegistry.objects.byUID[i][1].type);
2517
+ }
2518
+ }
2519
+
2520
+ if (arguments[0]) {
2521
+ return list;
2522
+ } else {
2523
+ p(list);
2524
+ }
2525
+ };
2526
+
2527
+
2528
+
2529
+
2530
+ /**
2531
+ * Clears the ObjectRegistry of objects that are of a certain given type
2532
+ *
2533
+ * @param type string The type to clear
2534
+ */
2535
+ RG.ObjectRegistry.clearByType =
2536
+ RG.ObjectRegistry.ClearByType = function (type)
2537
+ {
2538
+ var objects = RG.ObjectRegistry.objects.byUID;
2539
+
2540
+ for (var i=0,len=objects.length; i<len; ++i) {
2541
+ if (objects[i]) {
2542
+ var uid = objects[i][0];
2543
+ var obj = objects[i][1];
2544
+
2545
+ if (obj && obj.type == type) {
2546
+ RG.ObjectRegistry.remove(obj);
2547
+ }
2548
+ }
2549
+ }
2550
+ };
2551
+
2552
+
2553
+
2554
+
2555
+ /**
2556
+ * This function provides an easy way to go through all of the objects that are held in the
2557
+ * Registry
2558
+ *
2559
+ * @param func function This function is run for every object. Its passed the object as an argument
2560
+ * @param string type Optionally, you can pass a type of object to look for
2561
+ */
2562
+ RG.ObjectRegistry.iterate =
2563
+ RG.ObjectRegistry.Iterate = function (func)
2564
+ {
2565
+ var objects = RGraph.ObjectRegistry.objects.byUID;
2566
+
2567
+ for (var i=0,len=objects.length; i<len; ++i) {
2568
+
2569
+ if (typeof arguments[1] === 'string') {
2570
+
2571
+ var types = arguments[1].split(/,/);
2572
+
2573
+ for (var j=0,len2=types.length; j<len2; ++j) {
2574
+ if (types[j] == objects[i][1].type) {
2575
+ func(objects[i][1]);
2576
+ }
2577
+ }
2578
+ } else {
2579
+ func(objects[i][1]);
2580
+ }
2581
+ }
2582
+ };
2583
+
2584
+
2585
+
2586
+
2587
+ /**
2588
+ * Retrieves all objects for a given canvas id
2589
+ *
2590
+ * @patarm id string The canvas ID to get objects for.
2591
+ */
2592
+ RG.ObjectRegistry.getObjectsByCanvasID = function (id)
2593
+ {
2594
+ var store = RG.ObjectRegistry.objects.byCanvasID;
2595
+ var ret = [];
2596
+
2597
+ // Loop through all of the objects and return the appropriate ones
2598
+ for (var i=0,len=store.length; i<len; ++i) {
2599
+ if (store[i] && store[i][0] == id ) {
2600
+ ret.push(store[i][1]);
2601
+ }
2602
+ }
2603
+
2604
+ return ret;
2605
+ };
2606
+
2607
+
2608
+
2609
+
2610
+ /**
2611
+ * Retrieves the relevant object based on the X/Y position.
2612
+ *
2613
+ * @param object e The event object
2614
+ * @return object The applicable (if any) object
2615
+ */
2616
+ RG.ObjectRegistry.getFirstObjectByXY =
2617
+ RG.ObjectRegistry.getObjectByXY = function (e)
2618
+ {
2619
+ var canvas = e.target;
2620
+ var ret = null;
2621
+ var objects = RG.ObjectRegistry.getObjectsByCanvasID(canvas.id);
2622
+
2623
+ for (var i=(objects.length - 1); i>=0; --i) {
2624
+
2625
+ var obj = objects[i].getObjectByXY(e);
2626
+
2627
+ if (obj) {
2628
+ return obj;
2629
+ }
2630
+ }
2631
+ };
2632
+
2633
+
2634
+
2635
+
2636
+ /**
2637
+ * Retrieves the relevant objects based on the X/Y position.
2638
+ * NOTE This function returns an array of objects
2639
+ *
2640
+ * @param object e The event object
2641
+ * @return An array of pertinent objects. Note the there may be only one object
2642
+ */
2643
+ RG.ObjectRegistry.getObjectsByXY = function (e)
2644
+ {
2645
+ var canvas = e.target;
2646
+ var ret = [];
2647
+ var objects = RG.ObjectRegistry.getObjectsByCanvasID(canvas.id);
2648
+
2649
+ // Retrieve objects "front to back"
2650
+ for (var i=(objects.length - 1); i>=0; --i) {
2651
+
2652
+ var obj = objects[i].getObjectByXY(e);
2653
+
2654
+ if (obj) {
2655
+ ret.push(obj);
2656
+ }
2657
+ }
2658
+
2659
+ return ret;
2660
+ };
2661
+
2662
+
2663
+
2664
+
2665
+ /**
2666
+ * Retrieves the object with the corresponding UID
2667
+ *
2668
+ * @param string uid The UID to get the relevant object for
2669
+ */
2670
+ RG.ObjectRegistry.getObjectByUID = function (uid)
2671
+ {
2672
+ var objects = RG.ObjectRegistry.objects.byUID;
2673
+
2674
+ for (var i=0,len=objects.length; i<len; ++i) {
2675
+ if (objects[i] && objects[i][1].uid == uid) {
2676
+ return objects[i][1];
2677
+ }
2678
+ }
2679
+ };
2680
+
2681
+
2682
+
2683
+
2684
+ /**
2685
+ * Brings a chart to the front of the ObjectRegistry by
2686
+ * removing it and then readding it at the end and then
2687
+ * redrawing the canvas
2688
+ *
2689
+ * @param object obj The object to bring to the front
2690
+ * @param boolean redraw Whether to redraw the canvas after the
2691
+ * object has been moved
2692
+ */
2693
+ RG.ObjectRegistry.bringToFront = function (obj)
2694
+ {
2695
+ var redraw = typeof arguments[1] === 'undefined' ? true : arguments[1];
2696
+
2697
+ RG.ObjectRegistry.remove(obj);
2698
+ RG.ObjectRegistry.add(obj);
2699
+
2700
+ if (redraw) {
2701
+ RG.redrawCanvas(obj.canvas);
2702
+ }
2703
+ };
2704
+
2705
+
2706
+
2707
+
2708
+ /**
2709
+ * Retrieves the objects that are the given type
2710
+ *
2711
+ * @param mixed canvas The canvas to check. It can either be the canvas object itself or just the ID
2712
+ * @param string type The type to look for
2713
+ * @return array An array of one or more objects
2714
+ */
2715
+ RG.ObjectRegistry.getObjectsByType = function (type)
2716
+ {
2717
+ var objects = RG.ObjectRegistry.objects.byUID;
2718
+ var ret = [];
2719
+
2720
+ for (var i=0,len=objects.length; i<len; ++i) {
2721
+
2722
+ if (objects[i] && objects[i][1] && objects[i][1].type && objects[i][1].type && objects[i][1].type == type) {
2723
+ ret.push(objects[i][1]);
2724
+ }
2725
+ }
2726
+
2727
+ return ret;
2728
+ };
2729
+
2730
+
2731
+
2732
+
2733
+ /**
2734
+ * Retrieves the FIRST object that matches the given type
2735
+ *
2736
+ * @param string type The type of object to look for
2737
+ * @return object The FIRST object that matches the given type
2738
+ */
2739
+ RG.ObjectRegistry.getFirstObjectByType = function (type)
2740
+ {
2741
+ var objects = RG.ObjectRegistry.objects.byUID;
2742
+
2743
+ for (var i=0,len=objects.length; i<len; ++i) {
2744
+ if (objects[i] && objects[i][1] && objects[i][1].type == type) {
2745
+ return objects[i][1];
2746
+ }
2747
+ }
2748
+
2749
+ return null;
2750
+ };
2751
+
2752
+
2753
+
2754
+
2755
+ /**
2756
+ * This takes centerx, centery, x and y coordinates and returns the
2757
+ * appropriate angle relative to the canvas angle system. Remember
2758
+ * that the canvas angle system starts at the EAST axis
2759
+ *
2760
+ * @param number cx The centerx coordinate
2761
+ * @param number cy The centery coordinate
2762
+ * @param number x The X coordinate (eg the mouseX if coming from a click)
2763
+ * @param number y The Y coordinate (eg the mouseY if coming from a click)
2764
+ * @return number The relevant angle (measured in in RADIANS)
2765
+ */
2766
+ RG.getAngleByXY = function (cx, cy, x, y)
2767
+ {
2768
+ var angle = ma.atan((y - cy) / (x - cx));
2769
+ angle = ma.abs(angle)
2770
+
2771
+ if (x >= cx && y >= cy) {
2772
+ angle += RG.TWOPI;
2773
+
2774
+ } else if (x >= cx && y < cy) {
2775
+ angle = (RG.HALFPI - angle) + (RG.PI + RG.HALFPI);
2776
+
2777
+ } else if (x < cx && y < cy) {
2778
+ angle += RG.PI;
2779
+
2780
+ } else {
2781
+ angle = RG.PI - angle;
2782
+ }
2783
+
2784
+ /**
2785
+ * Upper and lower limit checking
2786
+ */
2787
+ if (angle > RG.TWOPI) {
2788
+ angle -= RG.TWOPI;
2789
+ }
2790
+
2791
+ return angle;
2792
+ };
2793
+
2794
+
2795
+
2796
+
2797
+ /**
2798
+ * This function returns the distance between two points. In effect the
2799
+ * radius of an imaginary circle that is centered on x1 and y1. The name
2800
+ * of this function is derived from the word "Hypoteneuse", which in
2801
+ * trigonmetry is the longest side of a triangle
2802
+ *
2803
+ * @param number x1 The original X coordinate
2804
+ * @param number y1 The original Y coordinate
2805
+ * @param number x2 The target X coordinate
2806
+ * @param number y2 The target Y coordinate
2807
+ */
2808
+ RG.getHypLength = function (x1, y1, x2, y2)
2809
+ {
2810
+ var ret = ma.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
2811
+
2812
+ return ret;
2813
+ };
2814
+
2815
+
2816
+
2817
+
2818
+ /**
2819
+ * This function gets the end point (X/Y coordinates) of a given radius.
2820
+ * You pass it the center X/Y and the radius and this function will return
2821
+ * the endpoint X/Y coordinates.
2822
+ *
2823
+ * @param number cx The center X coord
2824
+ * @param number cy The center Y coord
2825
+ * @param number r The lrngth of the radius
2826
+ */
2827
+ RG.getRadiusEndPoint = function (cx, cy, angle, radius)
2828
+ {
2829
+ var x = cx + (ma.cos(angle) * radius);
2830
+ var y = cy + (ma.sin(angle) * radius);
2831
+
2832
+ return [x, y];
2833
+ };
2834
+
2835
+
2836
+
2837
+
2838
+ /**
2839
+ * This installs all of the event listeners
2840
+ *
2841
+ * @param object obj The chart object
2842
+ */
2843
+ RG.installEventListeners =
2844
+ RG.InstallEventListeners = function (obj)
2845
+ {
2846
+ var prop = obj.properties;
2847
+
2848
+ /**
2849
+ * Don't attempt to install event listeners for older versions of MSIE
2850
+ */
2851
+ if (RG.ISOLD) {
2852
+ return;
2853
+ }
2854
+
2855
+ /**
2856
+ * If this function exists, then the dynamic file has been included.
2857
+ */
2858
+ if (RG.installCanvasClickListener) {
2859
+
2860
+ RG.installWindowMousedownListener(obj);
2861
+ RG.installWindowMouseupListener(obj);
2862
+ RG.installCanvasMousemoveListener(obj);
2863
+ RG.installCanvasMouseupListener(obj);
2864
+ RG.installCanvasMousedownListener(obj);
2865
+ RG.installCanvasClickListener(obj);
2866
+
2867
+ } else if ( RG.hasTooltips(obj)
2868
+ || prop['chart.adjustable']
2869
+ || prop['chart.annotatable']
2870
+ || prop['chart.contextmenu']
2871
+ || prop['chart.resizable']
2872
+ || prop['chart.key.interactive']
2873
+ || prop['chart.events.click']
2874
+ || prop['chart.events.mousemove']
2875
+ || typeof obj.onclick === 'function'
2876
+ || typeof obj.onmousemove === 'function'
2877
+ ) {
2878
+
2879
+ alert('[RGRAPH] You appear to have used dynamic features but not included the file: RGraph.common.dynamic.js');
2880
+ }
2881
+ };
2882
+
2883
+
2884
+
2885
+
2886
+ /**
2887
+ * Loosly mimicks the PHP function print_r();
2888
+ */
2889
+ RG.pr = function (obj)
2890
+ {
2891
+ var indent = (arguments[2] ? arguments[2] : ' ');
2892
+ var str = '';
2893
+
2894
+ var counter = typeof arguments[3] == 'number' ? arguments[3] : 0;
2895
+
2896
+ if (counter >= 5) {
2897
+ return '';
2898
+ }
2899
+
2900
+ switch (typeof obj) {
2901
+
2902
+ case 'string': str += obj + ' (' + (typeof obj) + ', ' + obj.length + ')'; break;
2903
+ case 'number': str += obj + ' (' + (typeof obj) + ')'; break;
2904
+ case 'boolean': str += obj + ' (' + (typeof obj) + ')'; break;
2905
+ case 'function': str += 'function () {}'; break;
2906
+ case 'undefined': str += 'undefined'; break;
2907
+ case 'null': str += 'null'; break;
2908
+
2909
+ case 'object':
2910
+ // In case of null
2911
+ if (RGraph.is_null(obj)) {
2912
+ str += indent + 'null\n';
2913
+ } else {
2914
+ str += indent + 'Object {' + '\n'
2915
+ for (j in obj) {
2916
+ str += indent + ' ' + j + ' => ' + RGraph.pr(obj[j], true, indent + ' ', counter + 1) + '\n';
2917
+ }
2918
+ str += indent + '}';
2919
+ }
2920
+ break;
2921
+
2922
+
2923
+ default:
2924
+ str += 'Unknown type: ' + typeof obj + '';
2925
+ break;
2926
+ }
2927
+
2928
+
2929
+ /**
2930
+ * Finished, now either return if we're in a recursed call, or alert()
2931
+ * if we're not.
2932
+ */
2933
+ if (!arguments[1]) {
2934
+ alert(str);
2935
+ }
2936
+
2937
+ return str;
2938
+ };
2939
+
2940
+
2941
+
2942
+
2943
+ /**
2944
+ * Produces a dashed line
2945
+ *
2946
+ * @param object co The 2D context
2947
+ * @param number x1 The start X coordinate
2948
+ * @param number y1 The start Y coordinate
2949
+ * @param number x2 The end X coordinate
2950
+ * @param number y2 The end Y coordinate
2951
+ */
2952
+ RG.dashedLine =
2953
+ RG.DashedLine = function(co, x1, y1, x2, y2)
2954
+ {
2955
+ /**
2956
+ * This is the size of the dashes
2957
+ */
2958
+ var size = 5;
2959
+
2960
+ /**
2961
+ * The optional fifth argument can be the size of the dashes
2962
+ */
2963
+ if (typeof arguments[5] === 'number') {
2964
+ size = arguments[5];
2965
+ }
2966
+
2967
+ var dx = x2 - x1;
2968
+ var dy = y2 - y1;
2969
+ var num = ma.floor(ma.sqrt((dx * dx) + (dy * dy)) / size);
2970
+
2971
+ var xLen = dx / num;
2972
+ var yLen = dy / num;
2973
+
2974
+ var count = 0;
2975
+
2976
+ do {
2977
+ (count % 2 == 0 && count > 0) ? co.lineTo(x1, y1) : co.moveTo(x1, y1);
2978
+
2979
+ x1 += xLen;
2980
+ y1 += yLen;
2981
+ } while(count++ <= num);
2982
+ };
2983
+
2984
+
2985
+
2986
+
2987
+ /**
2988
+ * Makes an AJAX call. It calls the given callback (a function) when ready
2989
+ *
2990
+ * @param string url The URL to retrieve
2991
+ * @param function callback A function that is called when the response is ready, there's an example below
2992
+ * called "myCallback".
2993
+ */
2994
+ RG.AJAX = function (url, callback)
2995
+ {
2996
+ // Mozilla, Safari, ...
2997
+ if (window.XMLHttpRequest) {
2998
+ var httpRequest = new XMLHttpRequest();
2999
+
3000
+ // MSIE
3001
+ } else if (window.ActiveXObject) {
3002
+ var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
3003
+ }
3004
+
3005
+ httpRequest.onreadystatechange = function ()
3006
+ {
3007
+ if (this.readyState == 4 && this.status == 200) {
3008
+ this.__user_callback__ = callback;
3009
+ this.__user_callback__(this.responseText);
3010
+ }
3011
+ }
3012
+
3013
+ httpRequest.open('GET', url, true);
3014
+ httpRequest.send();
3015
+ };
3016
+
3017
+
3018
+
3019
+
3020
+ /**
3021
+ * Makes an AJAX POST request. It calls the given callback (a function) when ready
3022
+ *
3023
+ * @param string url The URL to retrieve
3024
+ * @param object data The POST data
3025
+ * @param function callback A function that is called when the response is ready, there's an example below
3026
+ * called "myCallback".
3027
+ */
3028
+ RG.AJAX.POST = function (url, data, callback)
3029
+ {
3030
+ // Used when building the POST string
3031
+ var crumbs = [];
3032
+
3033
+
3034
+
3035
+
3036
+
3037
+
3038
+ // Mozilla, Safari, ...
3039
+ if (window.XMLHttpRequest) {
3040
+ var httpRequest = new XMLHttpRequest();
3041
+
3042
+ // MSIE
3043
+ } else if (window.ActiveXObject) {
3044
+ var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
3045
+ }
3046
+
3047
+
3048
+
3049
+
3050
+
3051
+ httpRequest.onreadystatechange = function ()
3052
+ {
3053
+ if (this.readyState == 4 && this.status == 200) {
3054
+ this.__user_callback__ = callback;
3055
+ this.__user_callback__(this.responseText);
3056
+ }
3057
+ }
3058
+
3059
+ httpRequest.open('POST', url, true);
3060
+ httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
3061
+
3062
+ for (i in data) {
3063
+ if (typeof i == 'string') {
3064
+ crumbs.push(i + '=' + encodeURIComponent(data[i]));
3065
+ }
3066
+ }
3067
+
3068
+ httpRequest.send(crumbs.join('&'));
3069
+ };
3070
+
3071
+
3072
+
3073
+
3074
+ /**
3075
+ * Uses the above function but calls the call back passing a number as its argument
3076
+ *
3077
+ * @param url string The URL to fetch
3078
+ * @param callback function Your callback function (which is passed the number as an argument)
3079
+ */
3080
+ RG.AJAX.getNumber = function (url, callback)
3081
+ {
3082
+ RG.AJAX(url, function ()
3083
+ {
3084
+ var num = parseFloat(this.responseText);
3085
+
3086
+ callback(num);
3087
+ });
3088
+ };
3089
+
3090
+
3091
+
3092
+
3093
+ /**
3094
+ * Uses the above function but calls the call back passing a string as its argument
3095
+ *
3096
+ * @param url string The URL to fetch
3097
+ * @param callback function Your callback function (which is passed the string as an argument)
3098
+ */
3099
+ RG.AJAX.getString = function (url, callback)
3100
+ {
3101
+ RG.AJAX(url, function ()
3102
+ {
3103
+ var str = String(this.responseText);
3104
+
3105
+ callback(str);
3106
+ });
3107
+ };
3108
+
3109
+
3110
+
3111
+
3112
+ /**
3113
+ * Uses the above function but calls the call back passing JSON (ie a JavaScript object ) as its argument
3114
+ *
3115
+ * @param url string The URL to fetch
3116
+ * @param callback function Your callback function (which is passed the JSON object as an argument)
3117
+ */
3118
+ RG.AJAX.getJSON = function (url, callback)
3119
+ {
3120
+ RG.AJAX(url, function ()
3121
+ {
3122
+ var json = eval('(' + this.responseText + ')');
3123
+
3124
+ callback(json);
3125
+ });
3126
+ };
3127
+
3128
+
3129
+
3130
+
3131
+ /**
3132
+ * Uses the above RGraph.AJAX function but calls the call back passing an array as its argument.
3133
+ * Useful if you're retrieving CSV data
3134
+ *
3135
+ * @param url string The URL to fetch
3136
+ * @param callback function Your callback function (which is passed the CSV/array as an argument)
3137
+ */
3138
+ RG.AJAX.getCSV = function (url, callback)
3139
+ {
3140
+ var seperator = arguments[2] ? arguments[2] : ',';
3141
+
3142
+ RG.AJAX(url, function ()
3143
+ {
3144
+ var regexp = new RegExp(seperator);
3145
+ var arr = this.responseText.split(regexp);
3146
+
3147
+ // Convert the strings to numbers
3148
+ for (var i=0,len=arr.length;i<len;++i) {
3149
+ arr[i] = parseFloat(arr[i]);
3150
+ }
3151
+
3152
+ callback(arr);
3153
+ });
3154
+ };
3155
+
3156
+
3157
+
3158
+
3159
+ /**
3160
+ * Rotates the canvas
3161
+ *
3162
+ * @param object canvas The canvas to rotate
3163
+ * @param int x The X coordinate about which to rotate the canvas
3164
+ * @param int y The Y coordinate about which to rotate the canvas
3165
+ * @param int angle The angle(in RADIANS) to rotate the canvas by
3166
+ */
3167
+ RG.rotateCanvas =
3168
+ RG.RotateCanvas = function (ca, x, y, angle)
3169
+ {
3170
+ var co = ca.getContext('2d');
3171
+
3172
+ co.translate(x, y);
3173
+ co.rotate(angle);
3174
+ co.translate(0 - x, 0 - y);
3175
+ };
3176
+
3177
+
3178
+
3179
+
3180
+ /**
3181
+ * Measures text by creating a DIV in the document and adding the relevant text to it.
3182
+ * Then checking the .offsetWidth and .offsetHeight.
3183
+ *
3184
+ * @param string text The text to measure
3185
+ * @param bool bold Whether the text is bold or not
3186
+ * @param string font The font to use
3187
+ * @param size number The size of the text (in pts)
3188
+ * @return array A two element array of the width and height of the text
3189
+ */
3190
+ RG.measureText =
3191
+ RG.MeasureText = function (text, bold, font, size)
3192
+ {
3193
+ // Add the sizes to the cache as adding DOM elements is costly and causes slow downs
3194
+ if (typeof RGraph.measuretext_cache === 'undefined') {
3195
+ RGraph.measuretext_cache = [];
3196
+ }
3197
+
3198
+ var str = text + ':' + bold + ':' + font + ':' + size;
3199
+ if (typeof RGraph.measuretext_cache == 'object' && RGraph.measuretext_cache[str]) {
3200
+ return RGraph.measuretext_cache[str];
3201
+ }
3202
+
3203
+ if (!RGraph.measuretext_cache['text-div']) {
3204
+ var div = document.createElement('DIV');
3205
+ div.style.position = 'absolute';
3206
+ div.style.top = '-100px';
3207
+ div.style.left = '-100px';
3208
+ document.body.appendChild(div);
3209
+
3210
+ // Now store the newly created DIV
3211
+ RGraph.measuretext_cache['text-div'] = div;
3212
+
3213
+ } else if (RGraph.measuretext_cache['text-div']) {
3214
+ var div = RGraph.measuretext_cache['text-div'];
3215
+ }
3216
+
3217
+ div.innerHTML = text.replace(/\r\n/g, '<br />');
3218
+ div.style.fontFamily = font;
3219
+ div.style.fontWeight = bold ? 'bold' : 'normal';
3220
+ div.style.fontSize = (size || 12) + 'pt';
3221
+
3222
+ var size = [div.offsetWidth, div.offsetHeight];
3223
+
3224
+ //document.body.removeChild(div);
3225
+ RGraph.measuretext_cache[str] = size;
3226
+
3227
+ return size;
3228
+ };
3229
+
3230
+
3231
+
3232
+
3233
+ /* New text function. Accepts two arguments:
3234
+ * o obj - The chart object
3235
+ * o opt - An object/hash/map of properties. This can consist of:
3236
+ * x The X coordinate (REQUIRED)
3237
+ * y The Y coordinate (REQUIRED)
3238
+ * text The text to show (REQUIRED)
3239
+ * font The font to use
3240
+ * size The size of the text (in pt)
3241
+ * italic Whether the text should be italic or not
3242
+ * bold Whether the text shouldd be bold or not
3243
+ * marker Whether to show a marker that indicates the X/Y coordinates
3244
+ * valign The vertical alignment
3245
+ * halign The horizontal alignment
3246
+ * bounding Whether to draw a bounding box for the text
3247
+ * boundingStroke The strokeStyle of the bounding box
3248
+ * boundingFill The fillStyle of the bounding box
3249
+ */
3250
+ RG.text2 =
3251
+ RG.Text2 = function (obj, opt)
3252
+ {
3253
+ /**
3254
+ * An RGraph object can be given, or a string or the 2D rendering context
3255
+ * The coords are placed on the obj.coordsText variable ONLY if it's an RGraph object. The function
3256
+ * still returns the cooords though in all cases.
3257
+ */
3258
+ if (obj && obj.isRGraph) {
3259
+ var obj = obj;
3260
+ var co = obj.context;
3261
+ var ca = obj.canvas;
3262
+ } else if (typeof obj == 'string') {
3263
+ var ca = document.getElementById(obj);
3264
+ var co = ca.getContext('2d');
3265
+ var obj = ca.__object__;
3266
+ } else if (typeof obj.getContext === 'function') {
3267
+ var ca = obj;
3268
+ var co = ca.getContext('2d');
3269
+ var obj = ca.__object__;
3270
+ } else if (obj.toString().indexOf('CanvasRenderingContext2D') != -1 || RGraph.ISIE8 && obj.moveTo) {
3271
+ var co = obj;
3272
+ var ca = obj.canvas;
3273
+ var obj = ca.__object__;
3274
+
3275
+ // IE7/8
3276
+ } else if (RG.ISOLD && obj.fillText) {
3277
+ var co = obj;
3278
+ var ca = obj.canvas;
3279
+ var obj = ca.__object__;
3280
+ }
3281
+
3282
+ var x = opt.x;
3283
+ var y = opt.y;
3284
+ var originalX = x;
3285
+ var originalY = y;
3286
+ var text = opt.text;
3287
+ var text_multiline = typeof text === 'string' ? text.split(/\r?\n/g) : '';
3288
+ var numlines = text_multiline.length;
3289
+ var font = opt.font ? opt.font : 'Arial';
3290
+ var size = opt.size ? opt.size : 10;
3291
+ var size_pixels = size * 1.5;
3292
+ var bold = opt.bold;
3293
+ var italic = opt.italic;
3294
+ var halign = opt.halign ? opt.halign : 'left';
3295
+ var valign = opt.valign ? opt.valign : 'bottom';
3296
+ var tag = typeof opt.tag == 'string' && opt.tag.length > 0 ? opt.tag : '';
3297
+ var marker = opt.marker;
3298
+ var angle = opt.angle || 0;
3299
+
3300
+
3301
+
3302
+
3303
+
3304
+
3305
+
3306
+
3307
+
3308
+
3309
+
3310
+
3311
+
3312
+
3313
+
3314
+
3315
+
3316
+
3317
+
3318
+
3319
+
3320
+
3321
+
3322
+
3323
+ /**
3324
+ * Changed the name of boundingFill/boundingStroke - this allows you to still use those names
3325
+ */
3326
+ if (typeof opt.boundingFill === 'string') opt['bounding.fill'] = opt.boundingFill;
3327
+ if (typeof opt.boundingStroke === 'string') opt['bounding.stroke'] = opt.boundingStroke;
3328
+
3329
+ var bounding = opt.bounding;
3330
+ var bounding_stroke = opt['bounding.stroke'] ? opt['bounding.stroke'] : 'black';
3331
+ var bounding_fill = opt['bounding.fill'] ? opt['bounding.fill'] : 'rgba(255,255,255,0.7)';
3332
+ var bounding_shadow = opt['bounding.shadow'];
3333
+ var bounding_shadow_color = opt['bounding.shadow.color'] || '#ccc';
3334
+ var bounding_shadow_blur = opt['bounding.shadow.blur'] || 3;
3335
+ var bounding_shadow_offsetx = opt['bounding.shadow.offsetx'] || 3;
3336
+ var bounding_shadow_offsety = opt['bounding.shadow.offsety'] || 3;
3337
+ var bounding_linewidth = opt['bounding.linewidth'] || 1;
3338
+
3339
+
3340
+
3341
+ /**
3342
+ * Initialize the return value to an empty object
3343
+ */
3344
+ var ret = {};
3345
+
3346
+ //
3347
+ // Color
3348
+ //
3349
+ if (typeof opt.color === 'string') {
3350
+ var orig_fillstyle = co.fillStyle;
3351
+ co.fillStyle = opt.color;
3352
+ }
3353
+
3354
+
3355
+
3356
+ /**
3357
+ * The text arg must be a string or a number
3358
+ */
3359
+ if (typeof text == 'number') {
3360
+ text = String(text);
3361
+ }
3362
+
3363
+ if (typeof text !== 'string') {
3364
+ return;
3365
+ }
3366
+
3367
+
3368
+
3369
+ /**
3370
+ * This facilitates vertical text
3371
+ */
3372
+ if (angle != 0) {
3373
+ co.save();
3374
+ co.translate(x, y);
3375
+ co.rotate((ma.PI / 180) * angle)
3376
+ x = 0;
3377
+ y = 0;
3378
+ }
3379
+
3380
+
3381
+
3382
+ /**
3383
+ * Set the font
3384
+ */
3385
+ co.font = (opt.italic ? 'italic ' : '') + (opt.bold ? 'bold ' : '') + size + 'pt ' + font;
3386
+
3387
+
3388
+
3389
+ /**
3390
+ * Measure the width/height. This must be done AFTER the font has been set
3391
+ */
3392
+ var width=0;
3393
+ for (var i=0; i<numlines; ++i) {
3394
+ width = ma.max(width, co.measureText(text_multiline[i]).width);
3395
+ }
3396
+ var height = size_pixels * numlines;
3397
+
3398
+
3399
+
3400
+
3401
+ /**
3402
+ * Accommodate old MSIE 7/8
3403
+ */
3404
+ //if (document.all && RGraph.ISOLD) {
3405
+ //y += 2;
3406
+ //}
3407
+
3408
+
3409
+
3410
+ /**
3411
+ * If marker is specified draw a marker at the X/Y coordinates
3412
+ */
3413
+ if (opt.marker) {
3414
+ var marker_size = 10;
3415
+ var strokestyle = co.strokeStyle;
3416
+ co.beginPath();
3417
+ co.strokeStyle = 'red';
3418
+ co.moveTo(x, y - marker_size);
3419
+ co.lineTo(x, y + marker_size);
3420
+ co.moveTo(x - marker_size, y);
3421
+ co.lineTo(x + marker_size, y);
3422
+ co.stroke();
3423
+ co.strokeStyle = strokestyle;
3424
+ }
3425
+
3426
+
3427
+
3428
+ /**
3429
+ * Set the horizontal alignment
3430
+ */
3431
+ if (halign == 'center') {
3432
+ co.textAlign = 'center';
3433
+ var boundingX = x - 2 - (width / 2);
3434
+ } else if (halign == 'right') {
3435
+ co.textAlign = 'right';
3436
+ var boundingX = x - 2 - width;
3437
+ } else {
3438
+ co.textAlign = 'left';
3439
+ var boundingX = x - 2;
3440
+ }
3441
+
3442
+
3443
+ /**
3444
+ * Set the vertical alignment
3445
+ */
3446
+ if (valign == 'center') {
3447
+
3448
+ co.textBaseline = 'middle';
3449
+ // Move the text slightly
3450
+ y -= 1;
3451
+
3452
+ y -= ((numlines - 1) / 2) * size_pixels;
3453
+ var boundingY = y - (size_pixels / 2) - 2;
3454
+
3455
+ } else if (valign == 'top') {
3456
+ co.textBaseline = 'top';
3457
+
3458
+ var boundingY = y - 2;
3459
+
3460
+ } else {
3461
+
3462
+ co.textBaseline = 'bottom';
3463
+
3464
+ // Move the Y coord if multiline text
3465
+ if (numlines > 1) {
3466
+ y -= ((numlines - 1) * size_pixels);
3467
+ }
3468
+
3469
+ var boundingY = y - size_pixels - 2;
3470
+ }
3471
+
3472
+ var boundingW = width + 4;
3473
+ var boundingH = height + 4;
3474
+
3475
+
3476
+
3477
+ /**
3478
+ * Draw a bounding box if required
3479
+ */
3480
+ if (bounding) {
3481
+
3482
+ var pre_bounding_linewidth = co.lineWidth;
3483
+ var pre_bounding_strokestyle = co.strokeStyle;
3484
+ var pre_bounding_fillstyle = co.fillStyle;
3485
+ var pre_bounding_shadowcolor = co.shadowColor;
3486
+ var pre_bounding_shadowblur = co.shadowBlur;
3487
+ var pre_bounding_shadowoffsetx = co.shadowOffsetX;
3488
+ var pre_bounding_shadowoffsety = co.shadowOffsetY;
3489
+
3490
+ co.lineWidth = bounding_linewidth;
3491
+ co.strokeStyle = bounding_stroke;
3492
+ co.fillStyle = bounding_fill;
3493
+
3494
+ if (bounding_shadow) {
3495
+ co.shadowColor = bounding_shadow_color;
3496
+ co.shadowBlur = bounding_shadow_blur;
3497
+ co.shadowOffsetX = bounding_shadow_offsetx;
3498
+ co.shadowOffsetY = bounding_shadow_offsety;
3499
+ }
3500
+
3501
+ //obj.context.strokeRect(boundingX, boundingY, width + 6, (size_pixels * numlines) + 4);
3502
+ //obj.context.fillRect(boundingX, boundingY, width + 6, (size_pixels * numlines) + 4);
3503
+ co.strokeRect(boundingX, boundingY, boundingW, boundingH);
3504
+ co.fillRect(boundingX, boundingY, boundingW, boundingH);
3505
+
3506
+ // Reset the linewidth,colors and shadow to it's original setting
3507
+ co.lineWidth = pre_bounding_linewidth;
3508
+ co.strokeStyle = pre_bounding_strokestyle;
3509
+ co.fillStyle = pre_bounding_fillstyle;
3510
+ co.shadowColor = pre_bounding_shadowcolor
3511
+ co.shadowBlur = pre_bounding_shadowblur
3512
+ co.shadowOffsetX = pre_bounding_shadowoffsetx
3513
+ co.shadowOffsetY = pre_bounding_shadowoffsety
3514
+ }
3515
+
3516
+
3517
+
3518
+ /**
3519
+ * Draw the text
3520
+ */
3521
+ if (numlines > 1) {
3522
+ for (var i=0; i<numlines; ++i) {
3523
+ co.fillText(text_multiline[i], x, y + (size_pixels * i));
3524
+ }
3525
+ } else {
3526
+ co.fillText(text, x + 0.5, y + 0.5);
3527
+ }
3528
+
3529
+
3530
+
3531
+ /**
3532
+ * If the text is at 90 degrees restore() the canvas - getting rid of the rotation
3533
+ * and the translate that we did
3534
+ */
3535
+ if (angle != 0) {
3536
+ if (angle == 90) {
3537
+ if (halign == 'left') {
3538
+ if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
3539
+ if (valign == 'center') {boundingX = originalX - (height / 2) - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
3540
+ if (valign == 'top') {boundingX = originalX - height - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
3541
+
3542
+ } else if (halign == 'center') {
3543
+ if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - (width / 2) - 2; boundingW = height + 4; boundingH = width + 4;}
3544
+ if (valign == 'center') {boundingX = originalX - (height / 2) - 2; boundingY = originalY - (width / 2) - 2; boundingW = height + 4; boundingH = width + 4;}
3545
+ if (valign == 'top') {boundingX = originalX - height - 2; boundingY = originalY - (width / 2) - 2; boundingW = height + 4; boundingH = width + 4;}
3546
+
3547
+ } else if (halign == 'right') {
3548
+ if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
3549
+ if (valign == 'center') {boundingX = originalX - (height / 2) - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
3550
+ if (valign == 'top') {boundingX = originalX - height - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
3551
+ }
3552
+
3553
+ } else if (angle == 180) {
3554
+
3555
+ if (halign == 'left') {
3556
+ if (valign == 'bottom') {boundingX = originalX - width - 2; boundingY = originalY - 2; boundingW = width + 4; boundingH = height + 4;}
3557
+ if (valign == 'center') {boundingX = originalX - width - 2; boundingY = originalY - (height / 2) - 2; boundingW = width + 4; boundingH = height + 4;}
3558
+ if (valign == 'top') {boundingX = originalX - width - 2; boundingY = originalY - height - 2; boundingW = width + 4; boundingH = height + 4;}
3559
+
3560
+ } else if (halign == 'center') {
3561
+ if (valign == 'bottom') {boundingX = originalX - (width / 2) - 2; boundingY = originalY - 2; boundingW = width + 4; boundingH = height + 4;}
3562
+ if (valign == 'center') {boundingX = originalX - (width / 2) - 2; boundingY = originalY - (height / 2) - 2; boundingW = width + 4; boundingH = height + 4;}
3563
+ if (valign == 'top') {boundingX = originalX - (width / 2) - 2; boundingY = originalY - height - 2; boundingW = width + 4; boundingH = height + 4;}
3564
+
3565
+ } else if (halign == 'right') {
3566
+ if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - 2; boundingW = width + 4; boundingH = height + 4;}
3567
+ if (valign == 'center') {boundingX = originalX - 2; boundingY = originalY - (height / 2) - 2; boundingW = width + 4; boundingH = height + 4;}
3568
+ if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - height - 2; boundingW = width + 4; boundingH = height + 4;}
3569
+ }
3570
+
3571
+ } else if (angle == 270) {
3572
+
3573
+ if (halign == 'left') {
3574
+ if (valign == 'bottom') {boundingX = originalX - height - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
3575
+ if (valign == 'center') {boundingX = originalX - (height / 2) - 4; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
3576
+ if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
3577
+
3578
+ } else if (halign == 'center') {
3579
+ if (valign == 'bottom') {boundingX = originalX - height - 2; boundingY = originalY - (width/2) - 2; boundingW = height + 4; boundingH = width + 4;}
3580
+ if (valign == 'center') {boundingX = originalX - (height/2) - 4; boundingY = originalY - (width/2) - 2; boundingW = height + 4; boundingH = width + 4;}
3581
+ if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - (width/2) - 2; boundingW = height + 4; boundingH = width + 4;}
3582
+
3583
+ } else if (halign == 'right') {
3584
+ if (valign == 'bottom') {boundingX = originalX - height - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
3585
+ if (valign == 'center') {boundingX = originalX - (height/2) - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
3586
+ if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
3587
+ }
3588
+ }
3589
+
3590
+ co.restore();
3591
+ }
3592
+
3593
+
3594
+
3595
+
3596
+ /**
3597
+ * Reset the text alignment so that text rendered after this text function is not affected
3598
+ */
3599
+ co.textBaseline = 'alphabetic';
3600
+ co.textAlign = 'left';
3601
+
3602
+
3603
+
3604
+
3605
+
3606
+ /**
3607
+ * Fill the ret variable with details of the text
3608
+ */
3609
+ ret.x = boundingX;
3610
+ ret.y = boundingY;
3611
+ ret.width = boundingW;
3612
+ ret.height = boundingH
3613
+ ret.object = obj;
3614
+ ret.text = text;
3615
+ ret.tag = tag;
3616
+
3617
+
3618
+
3619
+ /**
3620
+ * Save and then return the details of the text (but oly
3621
+ * if it's an RGraph object that was given)
3622
+ */
3623
+ if (obj && obj.isRGraph && obj.coordsText) {
3624
+ obj.coordsText.push(ret);
3625
+ }
3626
+
3627
+ //
3628
+ // Restore the original fillstyle
3629
+ //
3630
+ if (typeof orig_fillstyle === 'string') {
3631
+ co.fillStyle = orig_fillstyle;
3632
+ }
3633
+
3634
+ return ret;
3635
+ };
3636
+
3637
+
3638
+
3639
+
3640
+ /**
3641
+ * Takes a sequential index abd returns the group/index variation of it. Eg if you have a
3642
+ * sequential index from a grouped bar chart this function can be used to convert that into
3643
+ * an appropriate group/index combination
3644
+ *
3645
+ * @param nindex number The sequential index
3646
+ * @param data array The original data (which is grouped)
3647
+ * @return The group/index information
3648
+ */
3649
+ RG.sequentialIndexToGrouped = function (index, data)
3650
+ {
3651
+ var group = 0;
3652
+ var grouped_index = 0;
3653
+
3654
+ while (--index >= 0) {
3655
+
3656
+ if (RG.is_null(data[group])) {
3657
+ group++;
3658
+ grouped_index = 0;
3659
+ continue;
3660
+ }
3661
+
3662
+ // Allow for numbers as well as arrays in the dataset
3663
+ if (typeof data[group] == 'number') {
3664
+ group++
3665
+ grouped_index = 0;
3666
+ continue;
3667
+ }
3668
+
3669
+
3670
+ grouped_index++;
3671
+
3672
+ if (grouped_index >= data[group].length) {
3673
+ group++;
3674
+ grouped_index = 0;
3675
+ }
3676
+ }
3677
+
3678
+ return [group, grouped_index];
3679
+ };
3680
+
3681
+
3682
+
3683
+
3684
+ /**
3685
+ * This function highlights a rectangle
3686
+ *
3687
+ * @param object obj The chart object
3688
+ * @param number shape The coordinates of the rect to highlight
3689
+ */
3690
+ RG.Highlight.rect =
3691
+ RG.Highlight.Rect = function (obj, shape)
3692
+ {
3693
+ var ca = obj.canvas;
3694
+ var co = obj.context;
3695
+ var prop = obj.properties;
3696
+
3697
+ if (prop['chart.tooltips.highlight']) {
3698
+
3699
+
3700
+ // Safari seems to need this
3701
+ co.lineWidth = 1;
3702
+
3703
+ /**
3704
+ * Draw a rectangle on the canvas to highlight the appropriate area
3705
+ */
3706
+ co.beginPath();
3707
+
3708
+ co.strokeStyle = prop['chart.highlight.stroke'];
3709
+ co.fillStyle = prop['chart.highlight.fill'];
3710
+
3711
+ co.rect(shape['x'],shape['y'],shape['width'],shape['height']);
3712
+ //co.fillRect(shape['x'],shape['y'],shape['width'],shape['height']);
3713
+ co.stroke();
3714
+ co.fill();
3715
+ }
3716
+ };
3717
+
3718
+
3719
+
3720
+
3721
+ /**
3722
+ * This function highlights a point
3723
+ *
3724
+ * @param object obj The chart object
3725
+ * @param number shape The coordinates of the rect to highlight
3726
+ */
3727
+ RG.Highlight.point =
3728
+ RG.Highlight.Point = function (obj, shape)
3729
+ {
3730
+ var prop = obj.properties;
3731
+ var ca = obj.canvas;
3732
+ var co = obj.context;
3733
+
3734
+ if (prop['chart.tooltips.highlight']) {
3735
+
3736
+ /**
3737
+ * Draw a rectangle on the canvas to highlight the appropriate area
3738
+ */
3739
+ co.beginPath();
3740
+ co.strokeStyle = prop['chart.highlight.stroke'];
3741
+ co.fillStyle = prop['chart.highlight.fill'];
3742
+ var radius = prop['chart.highlight.point.radius'] || 2;
3743
+ co.arc(shape['x'],shape['y'],radius, 0, RG.TWOPI, 0);
3744
+ co.stroke();
3745
+ co.fill();
3746
+ }
3747
+ };
3748
+
3749
+
3750
+
3751
+
3752
+ /**
3753
+ * This is the same as Date.parse - though a little more flexible.
3754
+ *
3755
+ * @param string str The date string to parse
3756
+ * @return Returns the same thing as Date.parse
3757
+ */
3758
+ RG.parseDate = function (str)
3759
+ {
3760
+ str = RG.trim(str);
3761
+
3762
+ // Allow for: now (just the word "now")
3763
+ if (str === 'now') {
3764
+ str = (new Date()).toString();
3765
+ }
3766
+
3767
+ // Allow for: 2013-11-22 12:12:12 or 2013/11/22 12:12:12
3768
+ if (str.match(/^(\d\d\d\d)(-|\/)(\d\d)(-|\/)(\d\d)( |T)(\d\d):(\d\d):(\d\d)$/)) {
3769
+ str = RegExp.$1 + '-' + RegExp.$3 + '-' + RegExp.$5 + 'T' + RegExp.$7 + ':' + RegExp.$8 + ':' + RegExp.$9;
3770
+ }
3771
+
3772
+ // Allow for: 2013-11-22
3773
+ if (str.match(/^\d\d\d\d-\d\d-\d\d$/)) {
3774
+ str = str.replace(/-/g, '/');
3775
+ }
3776
+
3777
+ // Allow for: 12:09:44 (time only using todays date)
3778
+ if (str.match(/^\d\d:\d\d:\d\d$/)) {
3779
+
3780
+ var dateObj = new Date();
3781
+ var date = dateObj.getDate();
3782
+ var month = dateObj.getMonth() + 1;
3783
+ var year = dateObj.getFullYear();
3784
+
3785
+ // Pad the date/month with a zero if it's not two characters
3786
+ if (String(month).length === 1) month = '0' + month;
3787
+ if (String(date).length === 1) date = '0' + date;
3788
+
3789
+ str = (year + '/' + month + '/' + date) + ' ' + str;
3790
+ }
3791
+
3792
+ return Date.parse(str);
3793
+ };
3794
+
3795
+
3796
+
3797
+
3798
+ /**
3799
+ * Reset all of the color values to their original values
3800
+ *
3801
+ * @param object
3802
+ */
3803
+ RG.resetColorsToOriginalValues = function (obj)
3804
+ {
3805
+ if (obj.original_colors) {
3806
+ // Reset the colors to their original values
3807
+ for (var j in obj.original_colors) {
3808
+ if (typeof j === 'string' && j.substr(0,6) === 'chart.') {
3809
+ obj.properties[j] = RG.arrayClone(obj.original_colors[j]);
3810
+ }
3811
+ }
3812
+ }
3813
+
3814
+
3815
+
3816
+ /**
3817
+ * If the function is present on the object to reset specific colors - use that
3818
+ */
3819
+ if (typeof obj.resetColorsToOriginalValues === 'function') {
3820
+ obj.resetColorsToOriginalValues();
3821
+ }
3822
+
3823
+
3824
+
3825
+ // Reset the colorsParsed flag so that they're parsed for gradients again
3826
+ obj.colorsParsed = false;
3827
+ };
3828
+
3829
+
3830
+
3831
+
3832
+ /**
3833
+ * This function is a short-cut for the canvas path syntax (which can be rather verbose)
3834
+ *
3835
+ * @param mixed obj This can either be the 2D context or an RGraph object
3836
+ * @param array path The path details
3837
+ */
3838
+ RG.path =
3839
+ RG.Path = function (obj, path)
3840
+ {
3841
+ /**
3842
+ * Allow either the RGraph object or the context to be used as the first argument
3843
+ */
3844
+ if (obj.isRGraph && typeof obj.type === 'string') {
3845
+ var co = obj.context;
3846
+ } else {
3847
+ var co = obj;
3848
+ obj = obj.canvas.__object__;
3849
+ }
3850
+
3851
+ /**
3852
+ * If the Path information has been passed as a string - split it up
3853
+ */
3854
+ if (typeof path == 'string') {
3855
+ path = path.split(/ +/);
3856
+ }
3857
+
3858
+ /**
3859
+ * Go through the path information
3860
+ */
3861
+ for (var i=0,len=path.length; i<len; i+=1) {
3862
+
3863
+ var op = path[i];
3864
+
3865
+ // 100,100,50,0,Math.PI * 1.5, false
3866
+ switch (op) {
3867
+ case 'b':co.beginPath();break;
3868
+ case 'c':co.closePath();break;
3869
+ case 'm':co.moveTo(parseFloat(path[i+1]),parseFloat(path[i+2]));i+=2;break;
3870
+ case 'l':co.lineTo(parseFloat(path[i+1]),parseFloat(path[i+2]));i+=2;break;
3871
+ case 's':if(path[i+1])co.strokeStyle=obj.parseSingleColorForGradient(path[i+1]);co.stroke();i++;break;
3872
+ case 'f':if(path[i+1]){co.fillStyle = obj.parseSingleColorForGradient(path[i+1]);}co.fill();i++;break;
3873
+ case 'qc':co.quadraticCurveTo(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
3874
+ case 'bc':co.bezierCurveTo(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]),parseFloat(path[i+5]),parseFloat(path[i+6]));i+=6;break;
3875
+ case 'r':co.rect(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
3876
+ case 'a':co.arc(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]),parseFloat(path[i+5]),path[i+6]==='true'||path[i+6]===true?true:false);i+=6;break;
3877
+ case 'at':co.arcTo(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]),parseFloat(path[i+5]));i+=5;break;
3878
+ case 'lw':co.lineWidth=parseFloat(path[i+1]);i++;break;
3879
+ case 'lj':co.lineJoin=path[i+1];i++;break;
3880
+ case 'lc':co.lineCap=path[i+1];i++;break;
3881
+ case 'sc':co.shadowColor=path[i+1];i++;break;
3882
+ case 'sb':co.shadowBlur=parseFloat(path[i+1]);i++;break;
3883
+ case 'sx':co.shadowOffsetX=parseFloat(path[i+1]);i++;break;
3884
+ case 'sy':co.shadowOffsetY=parseFloat(path[i+1]);i++;break;
3885
+ case 'fu':(path[i+1])(obj);i++;break;
3886
+ case 'fs':co.fillStyle=obj.parseSingleColorForGradient(path[i+1]);i++;break;
3887
+ case 'ss':co.strokeStyle=obj.parseSingleColorForGradient(path[i+1]);i++;break;
3888
+ case 'fr':co.fillRect(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
3889
+ case 'sr':co.strokeRect(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
3890
+ case 'cl':co.clip();break;
3891
+ case 'ct':co.save();co.beginPath();RG.path(co, path[i+1]);co.clip();i++;break;
3892
+ case 'sa':co.save();break;
3893
+ case 'rs':co.restore();break;
3894
+ }
3895
+ }
3896
+ };
3897
+
3898
+
3899
+
3900
+ /**
3901
+ * Creates a Linear gradient
3902
+ *
3903
+ * @param object obj The chart object
3904
+ * @param number x1 The start X coordinate
3905
+ * @param number x2 The end X coordinate
3906
+ * @param number y1 The start Y coordinate
3907
+ * @param number y2 The end Y coordinate
3908
+ * @param string color1 The start color
3909
+ * @param string color2 The end color
3910
+ */
3911
+ RG.linearGradient =
3912
+ RG.LinearGradient = function (obj, x1, y1, x2, y2, color1, color2)
3913
+ {
3914
+ var gradient = obj.context.createLinearGradient(x1, y1, x2, y2);
3915
+ var numColors=arguments.length-5;
3916
+
3917
+ for (var i=5; i<arguments.length; ++i) {
3918
+
3919
+ var color = arguments[i];
3920
+ var stop = (i - 5) / (numColors - 1);
3921
+
3922
+ gradient.addColorStop(stop, color);
3923
+ }
3924
+
3925
+ return gradient;
3926
+ };
3927
+
3928
+
3929
+
3930
+
3931
+ /**
3932
+ * Creates a Radial gradient
3933
+ *
3934
+ * @param object obj The chart object
3935
+ * @param number x1 The start X coordinate
3936
+ * @param number x2 The end X coordinate
3937
+ * @param number y1 The start Y coordinate
3938
+ * @param number y2 The end Y coordinate
3939
+ * @param string color1 The start color
3940
+ * @param string color2 The end color
3941
+ */
3942
+ RG.radialGradient =
3943
+ RG.RadialGradient = function(obj, x1, y1, r1, x2, y2, r2, color1, color2)
3944
+ {
3945
+ var gradient = obj.context.createRadialGradient(x1, y1, r1, x2, y2, r2);
3946
+ var numColors = arguments.length-7;
3947
+
3948
+ for(var i=7; i<arguments.length; ++i) {
3949
+
3950
+ var color = arguments[i];
3951
+ var stop = (i-7) / (numColors-1);
3952
+
3953
+ gradient.addColorStop(stop, color);
3954
+ }
3955
+
3956
+ return gradient;
3957
+ };
3958
+
3959
+
3960
+
3961
+
3962
+ /**
3963
+ * Adds an event listener to RGraphs internal array so that RGraph can track them.
3964
+ * This DOESN'T add the event listener to the canvas/window.
3965
+ *
3966
+ * 5/1/14 TODO Used in the tooltips file, but is it necessary any more?
3967
+ */
3968
+ RG.addEventListener =
3969
+ RG.AddEventListener = function (id, e, func)
3970
+ {
3971
+ var type = arguments[3] ? arguments[3] : 'unknown';
3972
+
3973
+ RG.Registry.get('chart.event.handlers').push([id,e,func,type]);
3974
+ };
3975
+
3976
+
3977
+
3978
+
3979
+ /**
3980
+ * Clears event listeners that have been installed by RGraph
3981
+ *
3982
+ * @param string id The ID of the canvas to clear event listeners for - or 'window' to clear
3983
+ * the event listeners attached to the window
3984
+ */
3985
+ RG.clearEventListeners =
3986
+ RG.ClearEventListeners = function(id)
3987
+ {
3988
+ if (id && id == 'window') {
3989
+
3990
+ window.removeEventListener('mousedown', window.__rgraph_mousedown_event_listener_installed__, false);
3991
+ window.removeEventListener('mouseup', window.__rgraph_mouseup_event_listener_installed__, false);
3992
+
3993
+ } else {
3994
+
3995
+ var canvas = document.getElementById(id);
3996
+
3997
+ canvas.removeEventListener('mouseup', canvas.__rgraph_mouseup_event_listener_installed__, false);
3998
+ canvas.removeEventListener('mousemove', canvas.__rgraph_mousemove_event_listener_installed__, false);
3999
+ canvas.removeEventListener('mousedown', canvas.__rgraph_mousedown_event_listener_installed__, false);
4000
+ canvas.removeEventListener('click', canvas.__rgraph_click_event_listener_installed__, false);
4001
+ }
4002
+ };
4003
+
4004
+
4005
+
4006
+
4007
+ /**
4008
+ * Hides the annotating palette. It's here because it can be called
4009
+ * from code other than the annotating code.
4010
+ */
4011
+ RG.hidePalette =
4012
+ RG.HidePalette = function ()
4013
+ {
4014
+ var div = RG.Registry.get('palette');
4015
+
4016
+ if(typeof div == 'object' && div) {
4017
+
4018
+ div.style.visibility = 'hidden';
4019
+ div.style.display = 'none';
4020
+
4021
+ RG.Registry.set('palette', null);
4022
+ }
4023
+ };
4024
+
4025
+
4026
+
4027
+
4028
+ /**
4029
+ * Generates a random number between the minimum and maximum
4030
+ *
4031
+ * @param number min The minimum value
4032
+ * @param number max The maximum value
4033
+ * @param number OPTIONAL Number of decimal places
4034
+ */
4035
+ RG.random = function (min, max)
4036
+ {
4037
+ var dp = arguments[2] ? arguments[2] : 0;
4038
+ var r = ma.random();
4039
+
4040
+ return Number((((max - min) * r) + min).toFixed(dp));
4041
+ };
4042
+
4043
+
4044
+
4045
+
4046
+ /**
4047
+ *
4048
+ */
4049
+ RG.random.array = function (num, min, max)
4050
+ {
4051
+ var arr = [];
4052
+
4053
+ for(var i=0; i<num; i+=1) {
4054
+ arr.push(RG.random(min,max));
4055
+ }
4056
+
4057
+ return arr;
4058
+ };
4059
+
4060
+
4061
+
4062
+
4063
+ /**
4064
+ * Turns off shadow by setting blur to zero, the offsets to zero and the color to transparent black.
4065
+ *
4066
+ * @param object obj The chart object
4067
+ */
4068
+ RG.noShadow =
4069
+ RG.NoShadow = function (obj)
4070
+ {
4071
+ var co = obj.context;
4072
+
4073
+ co.shadowColor = 'rgba(0,0,0,0)';
4074
+ co.shadowBlur = 0;
4075
+ co.shadowOffsetX = 0;
4076
+ co.shadowOffsetY = 0;
4077
+ };
4078
+
4079
+
4080
+
4081
+
4082
+ /**
4083
+ * Sets the various shadow properties
4084
+ *
4085
+ * @param object obj The chart object
4086
+ * @param string color The color of the shadow
4087
+ * @param number offsetx The offsetX value for the shadow
4088
+ * @param number offsety The offsetY value for the shadow
4089
+ * @param number blur The blurring value for the shadow
4090
+ */
4091
+ RG.setShadow =
4092
+ RG.SetShadow = function (obj, color, offsetx, offsety, blur)
4093
+ {
4094
+ var co = obj.context;
4095
+
4096
+ co.shadowColor = color;
4097
+ co.shadowOffsetX = offsetx;
4098
+ co.shadowOffsetY = offsety;
4099
+ co.shadowBlur = blur;
4100
+
4101
+ };
4102
+
4103
+
4104
+
4105
+
4106
+ /**
4107
+ * Sets an object in the RGraph registry
4108
+ *
4109
+ * @param string name The name of the value to set
4110
+ */
4111
+ RG.Registry.set =
4112
+ RG.Registry.Set = function (name, value)
4113
+ {
4114
+ // Convert uppercase letters to dot+lower case letter
4115
+ name = name.replace(/([A-Z])/g, function (str)
4116
+ {
4117
+ return '.' + String(RegExp.$1).toLowerCase();
4118
+ });
4119
+
4120
+ // Ensure there is the chart. prefix
4121
+ if (name.substr(0,6) !== 'chart.') {
4122
+ name = 'chart.' + name;
4123
+ }
4124
+
4125
+ RG.Registry.store[name] = value;
4126
+
4127
+ return value;
4128
+ };
4129
+
4130
+
4131
+
4132
+
4133
+ /**
4134
+ * Gets an object from the RGraph registry
4135
+ *
4136
+ * @param string name The name of the value to fetch
4137
+ */
4138
+ RG.Registry.get =
4139
+ RG.Registry.Get = function (name)
4140
+ {
4141
+ // Convert uppercase letters to dot+lower case letter
4142
+ name = name.replace(/([A-Z])/g, function (str)
4143
+ {
4144
+ return '.' + String(RegExp.$1).toLowerCase();
4145
+ });
4146
+
4147
+ // Ensure there is the chart. prefix
4148
+ if (name.substr(0,6) !== 'chart.') {
4149
+ name = 'chart.' + name;
4150
+ }
4151
+
4152
+
4153
+ return RG.Registry.store[name];
4154
+ };
4155
+
4156
+
4157
+
4158
+
4159
+ /**
4160
+ * Converts the given number of degrees to radians. Angles in canvas are measured in radians
4161
+ *
4162
+ * @param number deg The value to convert
4163
+ */
4164
+ RG.degrees2Radians = function (deg)
4165
+ {
4166
+ return deg * (RG.PI / 180);
4167
+ };
4168
+
4169
+
4170
+
4171
+
4172
+ /**
4173
+ * Generates logs for... ...log charts
4174
+ *
4175
+ * @param number n The number to generate the log for
4176
+ * @param number base The base to use
4177
+ */
4178
+ RG.log = function (n,base)
4179
+ {
4180
+ return ma.log(n) / (base ? ma.log(base) : 1);
4181
+ };
4182
+
4183
+
4184
+
4185
+
4186
+ /**
4187
+ * Determines if the given object is an array or not
4188
+ *
4189
+ * @param mixed obj The variable to test
4190
+ */
4191
+ RG.isArray =
4192
+ RG.is_array = function (obj)
4193
+ {
4194
+ if (obj && obj.constructor) {
4195
+ var pos = obj.constructor.toString().indexOf('Array');
4196
+ } else {
4197
+ return false;
4198
+ }
4199
+
4200
+ return obj != null &&
4201
+ typeof pos === 'number' &&
4202
+ pos > 0 &&
4203
+ pos < 20;
4204
+ };
4205
+
4206
+
4207
+
4208
+
4209
+ /**
4210
+ * Removes white-space from the start aqnd end of a string
4211
+ *
4212
+ * @param string str The string to trim
4213
+ */
4214
+ RG.trim = function (str)
4215
+ {
4216
+ return RG.ltrim(RG.rtrim(str));
4217
+ };
4218
+
4219
+
4220
+
4221
+
4222
+ /**
4223
+ * Trims the white-space from the start of a string
4224
+ *
4225
+ * @param string str The string to trim
4226
+ */
4227
+ RG.ltrim = function (str)
4228
+ {
4229
+ return str.replace(/^(\s|\0)+/, '');
4230
+ };
4231
+
4232
+
4233
+
4234
+
4235
+ /**
4236
+ * Trims the white-space off of the end of a string
4237
+ *
4238
+ * @param string str The string to trim
4239
+ */
4240
+ RG.rtrim = function (str)
4241
+ {
4242
+ return str.replace(/(\s|\0)+$/, '');
4243
+ };
4244
+
4245
+
4246
+
4247
+ /**
4248
+ * Returns true/false as to whether the given variable is null or not
4249
+ *
4250
+ * @param mixed arg The argument to check
4251
+ */
4252
+ RG.isNull =
4253
+ RG.is_null = function (arg)
4254
+ {
4255
+ // must BE DOUBLE EQUALS - NOT TRIPLE
4256
+ if (arg == null || typeof arg === 'object' && !arg) {
4257
+ return true;
4258
+ }
4259
+
4260
+ return false;
4261
+ };
4262
+
4263
+
4264
+
4265
+
4266
+ /**
4267
+ * This function facilitates a very limited way of making your charts
4268
+ * whilst letting the rest of page continue - using the setTimeout function
4269
+ *
4270
+ * @param function func The function to run that creates the chart
4271
+ */
4272
+ RG.async =
4273
+ RG.Async = function (func)
4274
+ {
4275
+ return setTimeout(func, arguments[1] ? arguments[1] : 1);
4276
+ };
4277
+
4278
+
4279
+
4280
+
4281
+ /**
4282
+ * Resets (more than just clears) the canvas and clears any pertinent objects
4283
+ * from the ObjectRegistry
4284
+ *
4285
+ * @param object ca The canvas object (as returned by document.getElementById() ).
4286
+ */
4287
+ RG.reset =
4288
+ RG.Reset = function (ca)
4289
+ {
4290
+ ca.width = ca.width;
4291
+
4292
+ RG.ObjectRegistry.clear(ca);
4293
+
4294
+ ca.__rgraph_aa_translated__ = false;
4295
+
4296
+
4297
+
4298
+
4299
+
4300
+ //
4301
+ // Clear any text objects that are in the cache
4302
+ //
4303
+ //var len = (ca.id + '-text-').length;
4304
+
4305
+ //for (i in RG.cache) {
4306
+ //
4307
+ // var value = RG.cache[i];
4308
+
4309
+ // if (i.substr(0, len) === (ca.id + '-text-') && typeof value === 'object' && value) {
4310
+ // RG.cache[i].parentNode.removeChild(RG.cache[i]);
4311
+ // RG.cache[i] = null;
4312
+ // }
4313
+ //}
4314
+ };
4315
+
4316
+
4317
+
4318
+
4319
+ /**
4320
+ * NOT USED ANY MORE
4321
+ */
4322
+ RG.att = function (ca)
4323
+ {
4324
+ }
4325
+
4326
+
4327
+
4328
+ /**
4329
+ * This function is due to be removed.
4330
+ *
4331
+ * @param string id The ID of what can be either the canvas tag or a DIV tag
4332
+ */
4333
+ RG.getCanvasTag = function (id)
4334
+ {
4335
+ id = typeof id === 'object' ? id.id : id;
4336
+ var canvas = doc.getElementById(id);
4337
+
4338
+ return [id, canvas];
4339
+ };
4340
+
4341
+
4342
+
4343
+
4344
+ /**
4345
+ * A wrapper function that encapsulate requestAnimationFrame
4346
+ *
4347
+ * @param function func The animation function
4348
+ */
4349
+ RG.Effects.updateCanvas =
4350
+ RG.Effects.UpdateCanvas = function (func)
4351
+ {
4352
+ win.requestAnimationFrame = win.requestAnimationFrame
4353
+ || win.webkitRequestAnimationFrame
4354
+ || win.msRequestAnimationFrame
4355
+ || win.mozRequestAnimationFrame
4356
+ || (function (func){setTimeout(func, 16.666);});
4357
+
4358
+ win.requestAnimationFrame(func);
4359
+ };
4360
+
4361
+
4362
+
4363
+
4364
+ /**
4365
+ * This function returns an easing multiplier for effects so they eas out towards the
4366
+ * end of the effect.
4367
+ *
4368
+ * @param number frames The total number of frames
4369
+ * @param number frame The frame number
4370
+ */
4371
+ RG.Effects.getEasingMultiplier = function (frames, frame)
4372
+ {
4373
+ return ma.pow(ma.sin((frame / frames) * RG.HALFPI), 3);
4374
+ };
4375
+
4376
+
4377
+
4378
+
4379
+ /**
4380
+ * This function converts an array of strings to an array of numbers. Its used by the meter/gauge
4381
+ * style charts so that if you want you can pass in a string. It supports various formats:
4382
+ *
4383
+ * '45.2'
4384
+ * '-45.2'
4385
+ * ['45.2']
4386
+ * ['-45.2']
4387
+ * '45.2,45.2,45.2' // A CSV style string
4388
+ *
4389
+ * @param number frames The string or array to parse
4390
+ */
4391
+ RG.stringsToNumbers = function (str)
4392
+ {
4393
+ // An optional seperator to use intead of a comma
4394
+ var sep = arguments[1] || ',';
4395
+
4396
+
4397
+ // If it's already a number just return it
4398
+ if (typeof str === 'number') {
4399
+ return str;
4400
+ }
4401
+
4402
+
4403
+
4404
+
4405
+
4406
+ if (typeof str === 'string') {
4407
+ if (str.indexOf(sep) != -1) {
4408
+ str = str.split(sep);
4409
+ } else {
4410
+ str = parseFloat(str);
4411
+ }
4412
+ }
4413
+
4414
+
4415
+
4416
+
4417
+
4418
+ if (typeof str === 'object') {
4419
+ for (var i=0,len=str.length; i<len; i+=1) {
4420
+ str[i] = parseFloat(str[i]);
4421
+ }
4422
+ }
4423
+
4424
+ return str;
4425
+ };
4426
+
4427
+
4428
+
4429
+
4430
+ /**
4431
+ * Drawing cache function. This function creates an off-screen canvas and draws [wwhatever] to it
4432
+ * and then subsequent calls use that instead of repeatedly drawing the same thing.
4433
+ *
4434
+ * @param object obj The graph object
4435
+ * @param string id An ID string used to identify the relevant entry in the cache
4436
+ * @param function func The drawing function. This will be called to do the draw.
4437
+ */
4438
+ RG.cachedDraw = function (obj, id, func)
4439
+ {
4440
+ //If the cache entry xists - just copy it across to the main canvas
4441
+ if (!RG.cache[id]) {
4442
+
4443
+ RG.cache[id] = {};
4444
+ RG.cache[id].object = obj;
4445
+ RG.cache[id].canvas = document.createElement('canvas');
4446
+ RG.cache[id].canvas.setAttribute('width', obj.canvas.width);
4447
+ RG.cache[id].canvas.setAttribute('height', obj.canvas.height);
4448
+ RG.cache[id].canvas.setAttribute('id', 'background_cached_canvas' + obj.canvas.id);
4449
+
4450
+ //Add MSIE support
4451
+ if (typeof G_vmlCanvasManager === 'object' && G_vmlCanvasManager.initElement) {
4452
+ G_vmlCanvasManager.initElement(RG.cache[id].canvas);
4453
+ }
4454
+
4455
+ RG.cache[id].context = RG.cache[id].canvas.getContext('2d');
4456
+
4457
+ // Antialiasing on the cache canvas
4458
+ RG.cache[id].context.translate(0.5,0.5);
4459
+
4460
+ // Call the function
4461
+ func(obj, RG.cache[id].canvas, RG.cache[id].context);
4462
+ }
4463
+
4464
+ // Now copy the contents of the cached canvas over to the main one.
4465
+ // The coordinates are -0.5 because of the anti-aliasing effect in
4466
+ // use on the main canvas
4467
+ obj.context.drawImage(RG.cache[id].canvas,-0.5,-0.5);
4468
+ };
4469
+
4470
+
4471
+
4472
+
4473
+ /**
4474
+ * The function that runs through the supplied configuration and
4475
+ * converts it to the RGraph stylee.
4476
+ *
4477
+ * @param object conf The config
4478
+ * @param object The settings for the object
4479
+ */
4480
+ RG.parseObjectStyleConfig = function (obj, config)
4481
+ {
4482
+ /**
4483
+ * The recursion function
4484
+ */
4485
+ var recurse = function (obj, config, name, settings)
4486
+ {
4487
+ var i;
4488
+
4489
+ for (key in config) {
4490
+
4491
+ // Allow for functions in the configuration. Run them immediately
4492
+ if (key.match(/^exec[0-9]*$/)) {
4493
+ config[key](obj, settings);
4494
+ continue;
4495
+ }
4496
+
4497
+ var isObject = false; // Default value
4498
+ var isArray = false; // Default value
4499
+ var value = config[key];
4500
+
4501
+ // Change caps to dots. Eg textSize => text.size
4502
+ while(key.match(/([A-Z])/)) {
4503
+ key = key.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
4504
+ }
4505
+
4506
+ if (!RG.isNull(value) && value.constructor) {
4507
+ isObject = value.constructor.toString().indexOf('Object') > 0;
4508
+ isArray = value.constructor.toString().indexOf('Array') > 0;
4509
+ }
4510
+
4511
+ if (isObject && !isArray) {
4512
+ recurse(obj, config[key], name + '.' + key, settings);
4513
+
4514
+ } else if (key === 'self') {
4515
+ settings[name] = value;
4516
+
4517
+ } else {
4518
+ settings[name + '.' + key] = value;
4519
+ }
4520
+ }
4521
+
4522
+ return settings;
4523
+ };
4524
+
4525
+
4526
+
4527
+
4528
+ /**
4529
+ * Go through the settings that we've been given
4530
+ */
4531
+ var settings = recurse(obj, config, 'chart', {});
4532
+
4533
+ /**
4534
+ * Go through the settings and set them on the object
4535
+ */
4536
+ for (key in settings) {
4537
+ if (typeof key === 'string') {
4538
+ obj.set(key, settings[key]);
4539
+ }
4540
+ }
4541
+ };
4542
+
4543
+
4544
+
4545
+
4546
+ /**
4547
+ * This function is a short-cut for the canvas path syntax (which can be rather
4548
+ * verbose). You can read a description of it (which details all of the
4549
+ * various options) on the RGraph blog (www.rgraph.net/blog). The function is
4550
+ * added to the CanvasRenderingContext2D object so it becomes a context function.
4551
+ *
4552
+ * So you can use it like these examples show:
4553
+ *
4554
+ * 1. RG.pa2(context, 'b r 0 0 50 50 f red');
4555
+ * 2. RG.pa2(context, 'b a 50 50 50 0 3.14 false f red');
4556
+ * 3. RG.pa2(context, 'b m 5 100 bc 5 0 100 0 100 100 s red');
4557
+ * 4. RG.pa2(context, 'b m 5 100 at 50 0 95 100 50 s red');
4558
+ * 5. RG.pa2(context, 'sa b r 0 0 50 50 c b r 5 5 590 240 f red rs');
4559
+ * 6. RG.pa2(context, 'ld [2,6] ldo 4 b r 5 5 590 240 f red');
4560
+ * 7. RG.pa2(context, 'ga 0.25 b r 5 5 590 240 f red');
4561
+ *
4562
+ * @param array p The path details
4563
+ */
4564
+ RG.path2 = function (co, p)
4565
+ {
4566
+ if (typeof p === 'string') {
4567
+
4568
+ // Clear leading and trailing whitespace
4569
+ p = p.trim();
4570
+
4571
+
4572
+
4573
+
4574
+ // Allow for % placeholder substitution
4575
+ if (p.indexOf('%') !== -1) {
4576
+
4577
+ p = p.split(/%/);
4578
+
4579
+ for (var i=1; i<p.length; i+=1) {
4580
+ p[i] = arguments[i+1].toString() + ' ' + p[i];
4581
+ }
4582
+
4583
+ p = p.join(' ');
4584
+ }
4585
+
4586
+
4587
+
4588
+
4589
+ // Split up the path
4590
+ p = p.split(/ +/);
4591
+ }
4592
+
4593
+ // Collapse args that are in single or double quotes
4594
+ p = collapseQuoted(p);
4595
+
4596
+
4597
+ // Go through the path information
4598
+ for (var i=0,len=p.length; i<len; i+=1) {
4599
+
4600
+ switch (p[i]) {
4601
+ case 'b':co.beginPath();break;
4602
+ case 'c':co.closePath();break;
4603
+ case 'm':co.moveTo(parseFloat(p[i+1]),parseFloat(p[i+2]));i+=2;break;
4604
+ case 'l':co.lineTo(parseFloat(p[i+1]),parseFloat(p[i+2]));i+=2;break;
4605
+ case 's':if(p[i+1])co.strokeStyle=p[i+1];co.stroke();i++;break;
4606
+ case 'f':if(p[i+1]){co.fillStyle=p[i+1];}co.fill();i++;break;
4607
+ case 'qc':co.quadraticCurveTo(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
4608
+ case 'bc':co.bezierCurveTo(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]));i+=6;break;
4609
+ case 'r':co.rect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
4610
+ case 'a':co.arc(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),p[i+6]==='true'||p[i+6]===true||p[i+6]===1||p[i+6]==='1'?true:false);i+=6;break;
4611
+ case 'at':co.arcTo(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]));i+=5;break;
4612
+ case 'lw':co.lineWidth=parseFloat(p[i+1]);i++;break;
4613
+ case 'e':co.ellipse(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]),parseFloat(p[i+7]),p[i+8] === 'true' ? true : false);i+=8;break;
4614
+ case 'lj':co.lineJoin=p[i+1];i++;break;
4615
+ case 'lc':co.lineCap=p[i+1];i++;break;
4616
+ case 'sc':co.shadowColor=p[i+1];i++;break;
4617
+ case 'sb':co.shadowBlur=parseFloat(p[i+1]);i++;break;
4618
+ case 'sx':co.shadowOffsetX=parseFloat(p[i+1]);i++;break;
4619
+ case 'sy':co.shadowOffsetY=parseFloat(p[i+1]);i++;break;
4620
+ case 'fs':co.fillStyle=p[i+1];i++;break;
4621
+ case 'ss':co.strokeStyle=p[i+1];i++;break;
4622
+ case 'fr':co.fillRect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
4623
+ case 'sr':co.strokeRect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
4624
+ case 'cl':co.clip();break;
4625
+ case 'sa':co.save();break;
4626
+ case 'rs':co.restore();break;
4627
+ case 'tr':co.translate(parseFloat(p[i+1]),parseFloat(p[i+2]));i+=2;break;
4628
+ case 'sl':co.scale(parseFloat(p[i+1]), parseFloat(p[i+2]));i+=2;break;
4629
+ case 'ro':co.rotate(parseFloat(p[i+1]));i++;break;
4630
+ case 'tf':co.transform(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]));i+=6;break;
4631
+ case 'stf':co.setTransform(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]));i+=6;break;
4632
+ case 'cr':co.clearRect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
4633
+ case 'ld':var parts = p[i+1].split(/,/);for (var j=0;j<parts.length; j++){parts[j] = parts[j].replace(/^\[/, '');parts[j] = parts[j].replace(/\]$/, '');}co.setLineDash(parts);i+=1;break;
4634
+ case 'ldo':co.lineDashOffset=p[i+1];i++;break;
4635
+ case 'fo':co.font=p[i+1];i++;break;
4636
+ case 'ft':co.fillText(p[i+1], parseFloat(p[i+2]), parseFloat(p[i+3]));i+=3;break;
4637
+ case 'st':co.strokeText(p[i+1], parseFloat(p[i+2]), parseFloat(p[i+3]));i+=3;break;
4638
+ case 'ta':co.textAlign=p[i+1];i++;break;
4639
+ case 'tbl':co.textBaseline=p[i+1];i++;break;
4640
+ case 'ga':co.globalAlpha=parseFloat(p[i+1]);i++;break;
4641
+ case 'gco':co.globalCompositeOperation=p[i+1];i++;break;
4642
+
4643
+ // Empty option - ignore it
4644
+ case '':break;
4645
+
4646
+ // Unknown option
4647
+ default: alert('[ERROR] Unknown option: ' + p[i]);
4648
+ }
4649
+ }
4650
+
4651
+ //
4652
+ // This function looks for quoted args and collapses them
4653
+ //
4654
+ function collapseQuoted (arr)
4655
+ {
4656
+ var buffer = '', quote, index,out = [],quotes = ['"', "'"];
4657
+
4658
+ for (var j=0; j<quotes.length; j+=1) {
4659
+ for (var i=0; i<arr.length; i+=1) {
4660
+
4661
+ // Start and close quotes in the same part
4662
+ if (arr[i].match('/^' + quotes[j] + '/') && arr[i].match('/' + quotes[j] + '$/')) {
4663
+ arr[i] = arr[i].substr(1, arr[i].length - 2);
4664
+ }
4665
+
4666
+ // Start quoted part
4667
+ if (buffer.length === 0 && arr[i].indexOf(quotes[j]) !== -1) {
4668
+ buffer = arr[i].substr(arr[i].indexOf(quotes[j]) + 1, arr[i].length);
4669
+ quote = quotes[j];
4670
+ index = i;
4671
+
4672
+ // End quoted part
4673
+ } else if (quote && arr[i].indexOf(quote) !== -1) {
4674
+
4675
+ buffer = buffer + ' ' + arr[i].substr(0, arr[i].length - 1);
4676
+ arr[index] = buffer;
4677
+ arr[i] = '';
4678
+ quote = '';
4679
+ buffer = '';
4680
+
4681
+ // Add to quoted part
4682
+ } else if (quote) {
4683
+ buffer += ' ' + arr[i];
4684
+ arr[i] = '';
4685
+ }
4686
+ }
4687
+ }
4688
+
4689
+ // Trim the array down
4690
+ for (i=0; i<arr.length; i+=1) {
4691
+ if (arr[i].length) {
4692
+ out.push(arr[i]);
4693
+ }
4694
+ }
4695
+
4696
+ return out;
4697
+ }
4698
+ };
4699
+
4700
+
4701
+
4702
+
4703
+ //
4704
+ // Wraps the canvas in a DIV to allow DOM text to be used
4705
+ //
4706
+ // NOT USED ANY MORE
4707
+ //
4708
+ RG.wrap = function () {};
4709
+
4710
+
4711
+
4712
+
4713
+ // End module pattern
4714
+ })(window, document);
4715
+
4716
+
4717
+
4718
+
4719
+ /**
4720
+ * Uses the alert() function to show the structure of the given variable
4721
+ *
4722
+ * @param mixed v The variable to print/alert the structure of
4723
+ */
4724
+ window.$p = function (v)
4725
+ {
4726
+ RGraph.pr(arguments[0], arguments[1], arguments[3]);
4727
+ };
4728
+
4729
+
4730
+
4731
+
4732
+ /**
4733
+ * A shorthand for the default alert() function
4734
+ */
4735
+ window.$a = function (v)
4736
+ {
4737
+ alert(v);
4738
+ };
4739
+
4740
+
4741
+
4742
+
4743
+ /**
4744
+ * Short-hand for console.log
4745
+ *
4746
+ * @param mixed v The variable to log to the console
4747
+ */
4748
+ window.$cl = function (v)
4749
+ {
4750
+ return console.log(v);
4751
+ };