rgraph-rails 4.62 → 4.64

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -4
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/vendor/assets/javascripts/RGraph.bar.js +240 -3742
  5. data/vendor/assets/javascripts/RGraph.bipolar.js +165 -2005
  6. data/vendor/assets/javascripts/RGraph.common.annotate.js +35 -395
  7. data/vendor/assets/javascripts/RGraph.common.context.js +30 -595
  8. data/vendor/assets/javascripts/RGraph.common.core.js +418 -5359
  9. data/vendor/assets/javascripts/RGraph.common.csv.js +20 -276
  10. data/vendor/assets/javascripts/RGraph.common.deprecated.js +35 -450
  11. data/vendor/assets/javascripts/RGraph.common.dynamic.js +88 -1395
  12. data/vendor/assets/javascripts/RGraph.common.effects.js +90 -1545
  13. data/vendor/assets/javascripts/RGraph.common.key.js +52 -753
  14. data/vendor/assets/javascripts/RGraph.common.resizing.js +37 -563
  15. data/vendor/assets/javascripts/RGraph.common.sheets.js +29 -352
  16. data/vendor/assets/javascripts/RGraph.common.tooltips.js +32 -450
  17. data/vendor/assets/javascripts/RGraph.common.zoom.js +14 -219
  18. data/vendor/assets/javascripts/RGraph.cornergauge.js +71 -0
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +34 -570
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +33 -544
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +51 -755
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +37 -645
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +36 -633
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +35 -514
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +37 -559
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +33 -548
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +36 -664
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +50 -812
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +51 -856
  30. data/vendor/assets/javascripts/RGraph.fuel.js +58 -964
  31. data/vendor/assets/javascripts/RGraph.funnel.js +55 -984
  32. data/vendor/assets/javascripts/RGraph.gantt.js +77 -1354
  33. data/vendor/assets/javascripts/RGraph.gauge.js +85 -1421
  34. data/vendor/assets/javascripts/RGraph.hbar.js +162 -2788
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +80 -1401
  36. data/vendor/assets/javascripts/RGraph.line.js +249 -4248
  37. data/vendor/assets/javascripts/RGraph.meter.js +74 -1280
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +19 -301
  39. data/vendor/assets/javascripts/RGraph.odo.js +71 -1264
  40. data/vendor/assets/javascripts/RGraph.pie.js +137 -2288
  41. data/vendor/assets/javascripts/RGraph.radar.js +110 -1847
  42. data/vendor/assets/javascripts/RGraph.rose.js +108 -1977
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +80 -1432
  44. data/vendor/assets/javascripts/RGraph.scatter.js +172 -3163
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +60 -1120
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +66 -1735
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +21 -246
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +255 -3937
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +20 -276
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +68 -1303
  51. data/vendor/assets/javascripts/RGraph.svg.common.key.js +19 -205
  52. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +29 -352
  53. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +22 -273
  54. data/vendor/assets/javascripts/RGraph.svg.funnel.js +32 -0
  55. data/vendor/assets/javascripts/RGraph.svg.hbar.js +59 -1400
  56. data/vendor/assets/javascripts/RGraph.svg.line.js +70 -1580
  57. data/vendor/assets/javascripts/RGraph.svg.pie.js +55 -1131
  58. data/vendor/assets/javascripts/RGraph.svg.radar.js +57 -1502
  59. data/vendor/assets/javascripts/RGraph.svg.rose.js +66 -1817
  60. data/vendor/assets/javascripts/RGraph.svg.scatter.js +58 -1261
  61. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +28 -865
  62. data/vendor/assets/javascripts/RGraph.svg.waterfall.js +45 -1252
  63. data/vendor/assets/javascripts/RGraph.thermometer.js +63 -1136
  64. data/vendor/assets/javascripts/RGraph.vprogress.js +83 -1470
  65. data/vendor/assets/javascripts/RGraph.waterfall.js +83 -1347
  66. metadata +5 -4
  67. data/vendor/assets/javascripts/financial-data.js +0 -1067
@@ -1,3164 +1,173 @@
1
- // version: 2017-05-08
2
- /**
3
- * o--------------------------------------------------------------------------------o
4
- * | This file is part of the RGraph package - you can learn more at: |
5
- * | |
6
- * | http://www.rgraph.net |
7
- * | |
8
- * | RGraph is licensed under the Open Source MIT license. That means that it's |
9
- * | totally free to use! |
10
- * o--------------------------------------------------------------------------------o
11
- */
12
1
 
13
- RGraph = window.RGraph || {isRGraph: true};
14
-
15
-
16
-
17
-
18
- /**
19
- * The scatter graph constructor
20
- *
21
- * @param object canvas The cxanvas object
22
- * @param array data The chart data
23
- */
24
- RGraph.Scatter = function (conf)
25
- {
26
- /**
27
- * Allow for object config style
28
- */
29
- if ( typeof conf === 'object'
30
- && typeof conf.data === 'object'
31
- && typeof conf.id === 'string') {
32
-
33
- var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
34
-
35
- this.data = new Array(conf.data.length);
36
-
37
- // Store the data set(s)
38
- this.data = RGraph.arrayClone(conf.data);
39
-
40
-
41
- // Account for just one dataset being given
42
- if (typeof conf.data === 'object' && typeof conf.data[0] === 'object' && (typeof conf.data[0][0] === 'number' || typeof conf.data[0][0] === 'string')) {
43
- var tmp = RGraph.arrayClone(conf.data);
44
- conf.data = new Array();
45
- conf.data[0] = RGraph.arrayClone(tmp);
46
-
47
- this.data = RGraph.arrayClone(conf.data);
48
- }
49
-
50
- } else {
51
-
52
- var conf = {id: conf};
53
- conf.data = arguments[1];
54
-
55
-
56
- this.data = [];
57
-
58
- // Handle multiple datasets being given as one argument
59
- if (arguments[1][0] && arguments[1][0][0] && typeof arguments[1][0][0] == 'object') {
60
- // Store the data set(s)
61
- for (var i=0; i<arguments[1].length; ++i) {
62
- this.data[i] = RGraph.arrayClone(arguments[1][i]);
63
- }
64
-
65
- // Handle multiple data sets being supplied as seperate arguments
66
- } else {
67
-
68
- // Store the data set(s)
69
- for (var i=1; i<arguments.length; ++i) {
70
- this.data[i - 1] = RGraph.arrayClone(arguments[i]);
71
- }
72
- }
73
- }
74
-
75
-
76
-
77
-
78
-
79
- // First, if there's only been a single passed to us, convert it to
80
- // the multiple dataset format
81
- if (!RGraph.isArray(this.data[0][0])) {
82
- this.data = [this.data];
83
- }
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
- // If necessary convert X/Y values passed as strings to numbers
92
- for (var i=0,len=this.data.length; i<len; ++i) { // Datasets
93
- for (var j=0,len2=this.data[i].length; j<len2; ++j) { // Points
94
-
95
- // Handle the conversion of X values
96
- if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][0] === 'string') {
97
- if (this.data[i][j][0].match(/^[.0-9]+$/)) {
98
- this.data[i][j][0] = parseFloat(this.data[i][j][0]);
99
- } else if (this.data[i][j][0] === '') {
100
- this.data[i][j][0] = 0;
101
- }
102
- }
103
-
104
- // Handle the conversion of Y values
105
- if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][1] === 'string') {
106
- if (this.data[i][j][1].match(/[.0-9]+/)) {
107
- this.data[i][j][1] = parseFloat(this.data[i][j][1]);
108
- } else if (this.data[i][j][1] === '') {
109
- this.data[i][j][1] = 0;
110
- }
111
- }
112
- }
113
- }
114
-
115
-
116
- this.id = conf.id;
117
- this.canvas = document.getElementById(this.id);
118
- this.canvas.__object__ = this;
119
- this.context = this.canvas.getContext ? this.canvas.getContext('2d') : null;
120
- this.max = 0;
121
- this.coords = [];
122
- this.type = 'scatter';
123
- this.isRGraph = true;
124
- this.uid = RGraph.CreateUID();
125
- this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
126
- this.colorsParsed = false;
127
- this.coordsText = [];
128
- this.original_colors = [];
129
- this.firstDraw = true; // After the first draw this will be false
130
-
131
-
132
-
133
-
134
- // Handle multiple datasets being given as one argument
135
- //if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0]) == 'object') {
136
- // // Store the data set(s)
137
- // for (var i=0; i<arguments[1].length; ++i) {
138
- // this.data[i] = arguments[1][i];
139
- // }
140
-
141
- // Handle multiple data sets being supplied as seperate arguments
142
- //} else {
143
- // Store the data set(s)
144
- //for (var i=1; i<arguments.length; ++i) {
145
- // this.data[i - 1] = arguments[i];
146
- //}
147
- //}
148
-
149
-
150
- // Various config properties
151
- this.properties = {
152
- 'chart.background.barcolor1': 'rgba(0,0,0,0)',
153
- 'chart.background.barcolor2': 'rgba(0,0,0,0)',
154
- 'chart.background.grid': true,
155
- 'chart.background.grid.width': 1,
156
- 'chart.background.grid.color': '#ddd',
157
- 'chart.background.grid.hsize': 20,
158
- 'chart.background.grid.vsize': 20,
159
- 'chart.background.hbars': null,
160
- 'chart.background.vbars': null,
161
- 'chart.background.grid.vlines': true,
162
- 'chart.background.grid.hlines': true,
163
- 'chart.background.grid.border': true,
164
- 'chart.background.grid.autofit':true,
165
- 'chart.background.grid.autofit.align': true,
166
- 'chart.background.grid.autofit.numhlines': 5,
167
- 'chart.background.grid.autofit.numvlines': 20,
168
- 'chart.background.image': null,
169
- 'chart.background.image.stretch': true,
170
- 'chart.background.image.x': null,
171
- 'chart.background.image.y': null,
172
- 'chart.background.image.w': null,
173
- 'chart.background.image.h': null,
174
- 'chart.background.image.align': null,
175
- 'chart.background.color': null,
176
-
177
- 'chart.text.size': 12,
178
- 'chart.text.angle': 0,
179
- 'chart.text.color': 'black',
180
- 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
181
- 'chart.text.accessible': true,
182
- 'chart.text.accessible.overflow': 'visible',
183
- 'chart.text.accessible.pointerevents': true,
184
-
185
- 'chart.tooltips': [], // Default must be an empty array
186
- 'chart.tooltips.effect': 'fade',
187
- 'chart.tooltips.event': 'onmousemove',
188
- 'chart.tooltips.hotspot': 3,
189
- 'chart.tooltips.css.class': 'RGraph_tooltip',
190
- 'chart.tooltips.highlight': true,
191
- 'chart.tooltips.coords.page': false,
192
-
193
- 'chart.units.pre': '',
194
- 'chart.units.post': '',
195
- 'chart.numyticks': 10,
196
- 'chart.tickmarks': 'cross',
197
-
198
- 'chart.tickmarks.image.halign': 'center',
199
- 'chart.tickmarks.image.valign': 'center',
200
- 'chart.tickmarks.image.offsetx': 0,
201
- 'chart.tickmarks.image.offsety': 0,
202
- 'chart.ticksize': 5,
203
- 'chart.numxticks': true,
204
- 'chart.xaxis': true,
205
-
206
- 'chart.gutter.left': 25,
207
- 'chart.gutter.right': 25,
208
- 'chart.gutter.top': 25,
209
- 'chart.gutter.bottom': 30,
210
-
211
- 'chart.colors.bubble.graduated':true,
212
- 'chart.xmin': 0,
213
- 'chart.xmax': 0,
214
- 'chart.ymax': null,
215
- 'chart.ymin': 0,
216
- 'chart.scale.decimals': 0,
217
- 'chart.scale.point': '.',
218
- 'chart.scale.thousand': ',',
219
- 'chart.scale.zerostart': true,
220
- 'chart.title': '',
221
- 'chart.title.background': null,
222
- 'chart.title.hpos': null,
223
- 'chart.title.vpos': null,
224
- 'chart.title.bold': true,
225
- 'chart.title.font': null,
226
- 'chart.title.xaxis': '',
227
- 'chart.title.xaxis.bold': true,
228
- 'chart.title.xaxis.size': null,
229
- 'chart.title.xaxis.font': null,
230
- 'chart.title.xaxis.color': null,
231
- 'chart.title.yaxis': '',
232
- 'chart.title.yaxis.bold': true,
233
- 'chart.title.yaxis.size': null,
234
- 'chart.title.yaxis.font': null,
235
- 'chart.title.yaxis.color': null,
236
- 'chart.title.xaxis.pos': null,
237
- 'chart.title.yaxis.pos': null,
238
- 'chart.title.yaxis.x': null,
239
- 'chart.title.yaxis.y': null,
240
- 'chart.title.xaxis.x': null,
241
- 'chart.title.xaxis.y': null,
242
- 'chart.title.x': null,
243
- 'chart.title.y': null,
244
- 'chart.title.halign': null,
245
- 'chart.title.valign': null,
246
- 'chart.labels': [],
247
- 'chart.labels.bold': false,
248
- 'chart.labels.color': null,
249
- 'chart.labels.ingraph': null,
250
- 'chart.labels.above': false,
251
- 'chart.labels.above.size': 8,
252
- 'chart.labels.above.decimals': 0,
253
- 'chart.labels.offsetx': 0,
254
- 'chart.labels.offsety': 0,
255
- 'chart.ylabels.offsetx': 0,
256
- 'chart.ylabels.offsety': 0,
257
- 'chart.ylabels': true,
258
- 'chart.ylabels.count': 5,
259
- 'chart.ylabels.invert': false,
260
- 'chart.ylabels.specific': null,
261
- 'chart.ylabels.inside': false,
262
- 'chart.contextmenu': null,
263
- 'chart.defaultcolor': 'black',
264
- 'chart.xaxispos': 'bottom',
265
- 'chart.yaxispos': 'left',
266
- 'chart.crosshairs': false,
267
- 'chart.crosshairs.color': '#333',
268
- 'chart.crosshairs.linewidth': 1,
269
- 'chart.crosshairs.coords': false,
270
- 'chart.crosshairs.coords.fixed':true,
271
- 'chart.crosshairs.coords.fadeout':false,
272
- 'chart.crosshairs.coords.labels.x': 'X',
273
- 'chart.crosshairs.coords.labels.y': 'Y',
274
- 'chart.crosshairs.hline': true,
275
- 'chart.crosshairs.vline': true,
276
- 'chart.annotatable': false,
277
- 'chart.annotate.color': 'black',
278
- 'chart.line': false,
279
- 'chart.line.linewidth': 1,
280
- 'chart.line.colors': ['green', 'red'],
281
- 'chart.line.shadow.color': 'rgba(0,0,0,0)',
282
- 'chart.line.shadow.blur': 2,
283
- 'chart.line.shadow.offsetx': 3,
284
- 'chart.line.shadow.offsety': 3,
285
- 'chart.line.stepped': false,
286
- 'chart.line.visible': true,
287
- 'chart.noaxes': false,
288
- 'chart.noyaxis': false,
289
- 'chart.key': null,
290
- 'chart.key.background': 'white',
291
- 'chart.key.position': 'graph',
292
- 'chart.key.halign': 'right',
293
- 'chart.key.shadow': false,
294
- 'chart.key.shadow.color': '#666',
295
- 'chart.key.shadow.blur': 3,
296
- 'chart.key.shadow.offsetx': 2,
297
- 'chart.key.shadow.offsety': 2,
298
- 'chart.key.position.gutter.boxed': false,
299
- 'chart.key.position.x': null,
300
- 'chart.key.position.y': null,
301
-
302
- 'chart.key.interactive': false,
303
- 'chart.key.interactive.highlight.chart.fill': 'rgba(255,0,0,0.9)',
304
- 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
305
-
306
- 'chart.key.color.shape': 'square',
307
- 'chart.key.rounded': true,
308
- 'chart.key.linewidth': 1,
309
- 'chart.key.colors': null,
310
- 'chart.key.text.color': 'black',
311
- 'chart.axis.color': 'black',
312
- 'chart.zoom.factor': 1.5,
313
- 'chart.zoom.fade.in': true,
314
- 'chart.zoom.fade.out': true,
315
- 'chart.zoom.hdir': 'right',
316
- 'chart.zoom.vdir': 'down',
317
- 'chart.zoom.frames': 25,
318
- 'chart.zoom.delay': 16.666,
319
- 'chart.zoom.shadow': true,
320
- 'chart.zoom.background': true,
321
- 'chart.zoom.action': 'zoom',
322
- 'chart.boxplot.width': 1,
323
- 'chart.boxplot.capped': true,
324
- 'chart.resizable': false,
325
- 'chart.resize.handle.background': null,
326
- 'chart.xmin': 0,
327
- 'chart.labels.specific.align': 'left',
328
- 'chart.xscale': false,
329
- 'chart.xscale.units.pre': '',
330
- 'chart.xscale.units.post': '',
331
- 'chart.xscale.numlabels': 10,
332
- 'chart.xscale.formatter': null,
333
- 'chart.xscale.decimals': 0,
334
- 'chart.xscale.thousand': ',',
335
- 'chart.xscale.point': '.',
336
- 'chart.noendxtick': false,
337
- 'chart.noendytick': true,
338
- 'chart.events.mousemove': null,
339
- 'chart.events.click': null,
340
- 'chart.highlight.stroke': 'rgba(0,0,0,0)',
341
- 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
342
- 'chart.clearto': 'rgba(0,0,0,0)',
343
- 'chart.animation.trace': false,
344
- 'chart.animation.trace.clip': 1
345
- }
346
-
347
- /**
348
- * This allows the data points to be given as dates as well as numbers. Formats supported by RGraph.parseDate() are accepted.
349
- *
350
- * ALSO: unrelated but this loop is also used to convert null values to an
351
- * empty array
352
- */
353
- for (var i=0; i<this.data.length; ++i) {
354
- for (var j=0; j<this.data[i].length; ++j) {
355
-
356
- // Convert null data points to an empty erray
357
- if ( RGraph.isNull(this.data[i][j]) ) {
358
- this.data[i][j] = [];
359
- }
360
-
361
- // Allow for the X point to be dates
362
- if (this.data[i][j] && typeof(this.data[i][j][0]) == 'string') {
363
- this.data[i][j][0] = RGraph.parseDate(this.data[i][j][0]);
364
- }
365
- }
366
- }
367
-
368
-
369
- /**
370
- * Now make the data_arr array - all the data as one big array
371
- */
372
- this.data_arr = [];
373
-
374
- for (var i=0; i<this.data.length; ++i) {
375
- for (var j=0; j<this.data[i].length; ++j) {
376
- this.data_arr.push(this.data[i][j]);
377
- }
378
- }
379
-
380
- // Create the $ objects so that they can be used
381
- for (var i=0; i<this.data_arr.length; ++i) {
382
- this['$' + i] = {}
383
- }
384
-
385
-
386
- // Check for support
387
- if (!this.canvas) {
388
- alert('[SCATTER] No canvas support');
389
- return;
390
- }
391
-
392
-
393
- /**
394
- * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
395
- * done already
396
- */
397
- if (!this.canvas.__rgraph_aa_translated__) {
398
- this.context.translate(0.5,0.5);
399
-
400
- this.canvas.__rgraph_aa_translated__ = true;
401
- }
402
-
403
-
404
-
405
- // Short variable names
406
- var RG = RGraph,
407
- ca = this.canvas,
408
- co = ca.getContext('2d'),
409
- prop = this.properties,
410
- pa2 = RG.path2,
411
- win = window,
412
- doc = document,
413
- ma = Math
414
-
415
-
416
-
417
- /**
418
- * "Decorate" the object with the generic effects if the effects library has been included
419
- */
420
- if (RG.Effects && typeof RG.Effects.decorate === 'function') {
421
- RG.Effects.decorate(this);
422
- }
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
- /**
432
- * A simple setter
433
- *
434
- * @param string name The name of the property to set
435
- * @param string value The value of the property
436
- */
437
- this.set =
438
- this.Set = function (name)
439
- {
440
- var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
441
-
442
- /**
443
- * the number of arguments is only one and it's an
444
- * object - parse it for configuration data and return.
445
- */
446
- if (arguments.length === 1 && typeof name === 'object') {
447
- RG.parseObjectStyleConfig(this, name);
448
- return this;
449
- }
450
-
451
-
452
-
453
- /**
454
- * This should be done first - prepend the propertyy name with "chart." if necessary
455
- */
456
- if (name.substr(0,6) != 'chart.') {
457
- name = 'chart.' + name;
458
- }
459
-
460
-
461
-
462
-
463
- // Convert uppercase letters to dot+lower case letter
464
- while(name.match(/([A-Z])/)) {
465
- name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
466
- }
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
- /**
475
- * BC for chart.xticks
476
- */
477
- if (name == 'chart.xticks') {
478
- name == 'chart.numxticks';
479
- }
480
-
481
- /**
482
- * This is here because the key expects a name of "chart.colors"
483
- */
484
- if (name == 'chart.line.colors') {
485
- prop['chart.colors'] = value;
486
- }
487
-
488
- /**
489
- * Allow compatibility with older property names
490
- */
491
- if (name == 'chart.tooltip.hotspot') {
492
- name = 'chart.tooltips.hotspot';
493
- }
494
-
495
-
496
- /**
497
- * chart.yaxispos should be left or right
498
- */
499
- if (name == 'chart.yaxispos' && value != 'left' && value != 'right') {
500
- alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '" + value + "' Changing it to left");
501
- value = 'left';
502
- }
503
-
504
- /**
505
- * Check for xaxispos
506
- */
507
- if (name == 'chart.xaxispos' ) {
508
- if (value != 'bottom' && value != 'center') {
509
- alert('[SCATTER] (' + this.id + ') chart.xaxispos should be center or bottom. Tried to set it to: ' + value + ' Changing it to center');
510
- value = 'center';
511
- }
512
- }
513
-
514
- /**
515
- * Compatibility for chart.noxaxisoption
516
- */
517
- if (name == 'chart.noxaxis' ) {
518
- name = 'chart.xaxis';
519
- value = !value;
520
- }
521
- prop[name.toLowerCase()] = value;
522
-
523
- return this;
524
- };
525
-
526
-
527
-
528
-
529
-
530
-
531
-
532
-
533
- /**
534
- * A simple getter
535
- *
536
- * @param string name The name of the property to set
537
- */
538
- this.get =
539
- this.Get = function (name)
540
- {
541
- /**
542
- * This should be done first - prepend the property name with "chart." if necessary
543
- */
544
- if (name.substr(0,6) != 'chart.') {
545
- name = 'chart.' + name;
546
- }
547
-
548
- // Convert uppercase letters to dot+lower case letter
549
- while(name.match(/([A-Z])/)) {
550
- name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
551
- }
552
-
553
- return prop[name];
554
- };
555
-
556
-
557
-
558
-
559
-
560
-
561
-
562
-
563
- /**
564
- * The function you call to draw the Scatter chart
565
- */
566
- this.draw =
567
- this.Draw = function ()
568
- {
569
- // MUST be the first thing done!
570
- if (typeof prop['chart.background.image'] === 'string') {
571
- RG.DrawBackgroundImage(this);
572
- }
573
-
574
-
575
- /**
576
- * Fire the onbeforedraw event
577
- */
578
- RG.fireCustomEvent(this, 'onbeforedraw');
579
-
580
-
581
- /**
582
- * Parse the colors. This allows for simple gradient syntax
583
- */
584
- if (!this.colorsParsed) {
585
- this.parseColors();
586
-
587
- // Don't want to do this again
588
- this.colorsParsed = true;
589
- }
590
-
591
-
592
-
593
-
594
- /**
595
- * Stop this growing uncontrollably
596
- */
597
- this.coordsText = [];
598
-
599
-
600
-
601
-
602
- /**
603
- * This is new in May 2011 and facilitates indiviual gutter settings,
604
- * eg chart.gutter.left
605
- */
606
- this.gutterLeft = prop['chart.gutter.left'];
607
- this.gutterRight = prop['chart.gutter.right'];
608
- this.gutterTop = prop['chart.gutter.top'];
609
- this.gutterBottom = prop['chart.gutter.bottom'];
610
-
611
- // Go through all the data points and see if a tooltip has been given
612
- this.hasTooltips = false;
613
- var overHotspot = false;
614
-
615
- // Reset the coords array
616
- this.coords = [];
617
-
618
- /**
619
- * This facilitates the xmax, xmin and X values being dates
620
- */
621
- if (typeof(prop['chart.xmin']) == 'string') prop['chart.xmin'] = RG.parseDate(prop['chart.xmin']);
622
- if (typeof(prop['chart.xmax']) == 'string') prop['chart.xmax'] = RG.parseDate(prop['chart.xmax']);
623
-
624
- /**
625
- * Look for tooltips and populate chart.tooltips
626
- *
627
- * NB 26/01/2011 Updated so that chart.tooltips is ALWAYS populated
628
- */
629
- if (!RGraph.ISOLD) {
630
- this.Set('chart.tooltips', []);
631
- for (var i=0,len=this.data.length; i<len; i+=1) {
632
- for (var j =0,len2=this.data[i].length;j<len2; j+=1) {
633
-
634
- if (this.data[i][j] && this.data[i][j][3]) {
635
- prop['chart.tooltips'].push(this.data[i][j][3]);
636
- this.hasTooltips = true;
637
- } else {
638
- prop['chart.tooltips'].push(null);
639
- }
640
- }
641
- }
642
- }
643
-
644
- // Reset the maximum value
645
- this.max = 0;
646
-
647
- // Work out the maximum Y value
648
- //if (prop['chart.ymax'] && prop['chart.ymax'] > 0) {
649
- if (typeof prop['chart.ymax'] === 'number') {
650
-
651
- this.max = prop['chart.ymax'];
652
- this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
653
-
654
-
655
- this.scale2 = RG.getScale2(this, {
656
- 'max':this.max,
657
- 'min':this.min,
658
- 'strict':true,
659
- 'scale.thousand':prop['chart.scale.thousand'],
660
- 'scale.point':prop['chart.scale.point'],
661
- 'scale.decimals':prop['chart.scale.decimals'],
662
- 'ylabels.count':prop['chart.ylabels.count'],
663
- 'scale.round':prop['chart.scale.round'],
664
- 'units.pre': prop['chart.units.pre'],
665
- 'units.post': prop['chart.units.post']
666
- });
667
-
668
- this.max = this.scale2.max;
669
- this.min = this.scale2.min;
670
- var decimals = prop['chart.scale.decimals'];
671
-
672
- } else {
673
-
674
- var i = 0;
675
- var j = 0;
676
-
677
- for (i=0,len=this.data.length; i<len; i+=1) {
678
- for (j=0,len2=this.data[i].length; j<len2; j+=1) {
679
- if (!RG.isNull(this.data[i][j]) && this.data[i][j][1] != null) {
680
- this.max = Math.max(this.max, typeof(this.data[i][j][1]) == 'object' ? RG.array_max(this.data[i][j][1]) : Math.abs(this.data[i][j][1]));
681
- }
682
- }
683
- }
684
-
685
- this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
686
-
687
- this.scale2 = RG.getScale2(this, {
688
- 'max':this.max,
689
- 'min':this.min,
690
- 'scale.thousand':prop['chart.scale.thousand'],
691
- 'scale.point':prop['chart.scale.point'],
692
- 'scale.decimals':prop['chart.scale.decimals'],
693
- 'ylabels.count':prop['chart.ylabels.count'],
694
- 'scale.round':prop['chart.scale.round'],
695
- 'units.pre': prop['chart.units.pre'],
696
- 'units.post': prop['chart.units.post']
697
- });
698
-
699
- this.max = this.scale2.max;
700
- this.min = this.scale2.min;
701
- }
702
-
703
- this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
704
-
705
-
706
-
707
- // Progressively Draw the chart
708
- RG.background.Draw(this);
709
-
710
- /**
711
- * Draw any horizontal bars that have been specified
712
- */
713
- if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length) {
714
- RG.DrawBars(this);
715
- }
716
-
717
- /**
718
- * Draw any vertical bars that have been specified
719
- */
720
- if (prop['chart.background.vbars'] && prop['chart.background.vbars'].length) {
721
- this.DrawVBars();
722
- }
723
-
724
- if (!prop['chart.noaxes']) {
725
- this.DrawAxes();
726
- }
727
-
728
- this.DrawLabels();
729
-
730
- // Clip the canvas so that the trace2 effect is facilitated
731
- if (prop['chart.animation.trace']) {
732
- co.save();
733
- co.beginPath();
734
- co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
735
- co.clip();
736
- }
737
-
738
- for(i=0; i<this.data.length; ++i) {
739
- this.DrawMarks(i);
740
-
741
- // Set the shadow
742
- co.shadowColor = prop['chart.line.shadow.color'];
743
- co.shadowOffsetX = prop['chart.line.shadow.offsetx'];
744
- co.shadowOffsetY = prop['chart.line.shadow.offsety'];
745
- co.shadowBlur = prop['chart.line.shadow.blur'];
746
-
747
- this.DrawLine(i);
748
-
749
- // Turn the shadow off
750
- RG.NoShadow(this);
751
- }
752
-
753
-
754
- if (prop['chart.line']) {
755
- for (var i=0,len=this.data.length;i<len; i+=1) {
756
- this.DrawMarks(i); // Call this again so the tickmarks appear over the line
757
- }
758
- }
759
-
760
- if (prop['chart.animation.trace']) {
761
- co.restore();
762
- }
763
-
764
-
765
-
766
- /**
767
- * Setup the context menu if required
768
- */
769
- if (prop['chart.contextmenu']) {
770
- RG.ShowContext(this);
771
- }
772
-
773
-
774
-
775
- /**
776
- * Draw the key if necessary
777
- */
778
- if (prop['chart.key'] && prop['chart.key'].length) {
779
- RG.DrawKey(this, prop['chart.key'], prop['chart.line.colors']);
780
- }
781
-
782
-
783
- /**
784
- * Draw " above" labels if enabled
785
- */
786
- if (prop['chart.labels.above']) {
787
- this.DrawAboveLabels();
788
- }
789
-
790
- /**
791
- * Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js
792
- */
793
- this.DrawInGraphLabels(this);
794
-
795
-
796
- /**
797
- * This function enables resizing
798
- */
799
- if (prop['chart.resizable']) {
800
- RG.AllowResizing(this);
801
- }
802
-
803
-
804
- /**
805
- * This installs the event listeners
806
- */
807
- RG.InstallEventListeners(this);
808
-
809
-
810
- /**
811
- * Fire the onfirstdraw event
812
- */
813
- if (this.firstDraw) {
814
- RG.fireCustomEvent(this, 'onfirstdraw');
815
- this.firstDraw = false;
816
- this.firstDrawFunc();
817
- }
818
-
819
-
820
-
821
- /**
822
- * Fire the RGraph ondraw event
823
- */
824
- RG.FireCustomEvent(this, 'ondraw');
825
-
826
-
827
- return this;
828
- }
829
-
830
-
831
-
832
-
833
-
834
-
835
-
836
-
837
- /**
838
- * Draws the axes of the scatter graph
839
- */
840
- this.drawAxes =
841
- this.DrawAxes = function ()
842
- {
843
- var graphHeight = ca.height - this.gutterTop - this.gutterBottom;
844
-
845
- co.beginPath();
846
- co.strokeStyle = prop['chart.axis.color'];
847
- co.lineWidth = (prop['chart.axis.linewidth'] || 1) + 0.001; // Strange Chrome bug
848
-
849
- // Draw the Y axis
850
- if (prop['chart.noyaxis'] == false) {
851
- if (prop['chart.yaxispos'] == 'left') {
852
- co.moveTo(this.gutterLeft, this.gutterTop);
853
- co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
854
- } else {
855
- co.moveTo(ca.width - this.gutterRight, this.gutterTop);
856
- co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
857
- }
858
- }
859
-
860
-
861
- // Draw the X axis
862
- if (prop['chart.xaxis']) {
863
- if (prop['chart.xaxispos'] == 'center') {
864
- co.moveTo(this.gutterLeft, ma.round(this.gutterTop + ((ca.height - this.gutterTop - this.gutterBottom) / 2)));
865
- co.lineTo(ca.width - this.gutterRight, ma.round(this.gutterTop + ((ca.height - this.gutterTop - this.gutterBottom) / 2)));
866
- } else {
867
-
868
- var y = this.getYCoord(this.scale2.min > 0 ? this.scale2.min : 0);
869
-
870
- co.moveTo(this.gutterLeft, y);
871
- co.lineTo(ca.width - this.gutterRight, y);
872
- }
873
- }
874
-
875
- // Draw the Y tickmarks
876
- if (prop['chart.noyaxis'] === false) {
877
-
878
- var numyticks = prop['chart.numyticks'];
879
-
880
- //for (y=this.gutterTop; y < ca.height - this.gutterBottom + (prop['chart.xaxispos'] == 'center' ? 1 : 0) ; y+=(graphHeight / numyticks)) {
881
- for (i=0; i<numyticks; ++i) {
882
-
883
- var y = ((ca.height - this.gutterTop - this.gutterBottom) / numyticks) * i;
884
- y = y + this.gutterTop;
885
-
886
- if (prop['chart.xaxispos'] == 'center' && i == (numyticks / 2)) {
887
- continue;
888
- }
889
-
890
- if (prop['chart.yaxispos'] == 'left') {
891
- co.moveTo(this.gutterLeft, ma.round(y));
892
- co.lineTo(this.gutterLeft - 3, ma.round(y));
893
- } else {
894
- co.moveTo(ca.width - this.gutterRight +3, Math.round(y));
895
- co.lineTo(ca.width - this.gutterRight, Math.round(y));
896
- }
897
- }
898
-
899
- /**
900
- * Draw the end Y tickmark if the X axis is in the centre
901
- */
902
- if (prop['chart.numyticks'] > 0) {
903
- if (prop['chart.xaxispos'] == 'center' && prop['chart.yaxispos'] == 'left') {
904
- co.moveTo(this.gutterLeft, ma.round(ca.height - this.gutterBottom));
905
- co.lineTo(this.gutterLeft - 3, ma.round(ca.height - this.gutterBottom));
906
- } else if (prop['chart.xaxispos'] == 'center') {
907
- co.moveTo(ca.width - this.gutterRight + 3, ma.round(ca.height - this.gutterBottom));
908
- co.lineTo(ca.width - this.gutterRight, ma.round(ca.height - this.gutterBottom));
909
- }
910
- }
911
-
912
- /**
913
- * Draw an extra tick if the X axis isn't being shown
914
- */
915
- if (prop['chart.xaxis'] === false && prop['chart.yaxispos'] === 'left') {
916
- co.moveTo(this.gutterLeft, ma.round(ca.height - this.gutterBottom));
917
- co.lineTo(this.gutterLeft - 3, ma.round(ca.height - this.gutterBottom));
918
- } else if (prop['chart.xaxis'] === false && prop['chart.yaxispos'] === 'right') {
919
- co.moveTo(ca.width - this.gutterRight, ma.round(ca.height - this.gutterBottom));
920
- co.lineTo(ca.width - this.gutterRight + 3, ma.round(ca.height - this.gutterBottom));
921
- }
922
-
923
-
924
-
925
-
926
-
927
- //
928
- // If: the X axis is offset
929
- // the Y axis is being shown
930
- // there are Y tickmarks
931
- //
932
- if (
933
- prop['chart.xaxispos'] === 'bottom'
934
- && prop['chart.numyticks'] > 0
935
- ) {
936
-
937
- if (prop['chart.yaxispos'] == 'left') {
938
- co.moveTo(this.gutterLeft, ma.round(this.getYCoord(prop['chart.ymin'])));
939
- co.lineTo(this.gutterLeft - 3, ma.round(this.getYCoord(prop['chart.ymin'])));
940
- } else {
941
- co.moveTo(ca.width - this.gutterRight +3, ma.round(this.getYCoord(prop['chart.ymin'])));
942
- co.lineTo(ca.width - this.gutterRight, ma.round(this.getYCoord(prop['chart.ymin'])));
943
- }
944
- }
945
- }
946
-
947
-
948
-
949
-
950
-
951
-
952
-
953
-
954
- /**
955
- * Draw the X tickmarks
956
- */
957
- if (prop['chart.numxticks'] > 0 && prop['chart.xaxis']) {
958
-
959
- var x = 0,
960
- y = this.getYCoord(prop['chart.ylabels.invert'] ? this.scale2.max : (this.scale2.max > 0 && this.scale2.min > 0 ? this.scale2.min : 0) ) - 3,
961
- size = 3;
962
-
963
- if (prop['chart.ymin'] === 0 && prop['chart.xaxispos'] === 'bottom') {
964
- y += 3;
965
- }
966
-
967
- if (this.scale2.max > 0 && this.scale2.min > 0) {
968
- y += 3;
969
- }
970
-
971
- this.xTickGap = (prop['chart.labels'] && prop['chart.labels'].length) ? ((ca.width - this.gutterLeft - this.gutterRight ) / prop['chart.labels'].length) : (ca.width - this.gutterLeft - this.gutterRight) / 10;
972
-
973
- /**
974
- * This allows the number of X tickmarks to be specified
975
- */
976
- if (typeof(prop['chart.numxticks']) == 'number') {
977
- this.xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
978
- }
979
-
980
-
981
- for (x=(this.gutterLeft + (prop['chart.yaxispos'] == 'left' && prop['chart.noyaxis'] == false ? this.xTickGap : 0) );
982
- x <= (ca.width - this.gutterRight - (prop['chart.yaxispos'] == 'left' || prop['chart.noyaxis'] == true ? -1 : 1));
983
- x += this.xTickGap) {
984
-
985
- if (prop['chart.yaxispos'] == 'left' && prop['chart.noendxtick'] == true && x == (ca.width - this.gutterRight) ) {
986
- continue;
987
- } else if (prop['chart.yaxispos'] == 'right' && prop['chart.noendxtick'] == true && x == this.gutterLeft) {
988
- continue;
989
- }
990
-
991
- co.moveTo(ma.round(x), y);
992
- co.lineTo(ma.round(x), y + (prop['chart.xaxispos'] === 'center' || prop['chart.ymin'] < 0 ? size * 2 : size));
993
- }
994
- }
995
-
996
- co.stroke();
997
-
998
- /**
999
- * Reset the linewidth back to one
1000
- */
1001
- co.lineWidth = 1;
1002
- };
1003
-
1004
-
1005
-
1006
-
1007
-
1008
-
1009
-
1010
-
1011
- /**
1012
- * Draws the labels on the scatter graph
1013
- */
1014
- this.drawLabels =
1015
- this.DrawLabels = function ()
1016
- {
1017
- co.fillStyle = prop['chart.text.color'];
1018
-
1019
- var font = prop['chart.text.font'],
1020
- xMin = prop['chart.xmin'],
1021
- xMax = prop['chart.xmax'],
1022
- yMax = this.scale2.max,
1023
- yMin = prop['chart.ymin'] ? prop['chart.ymin'] : 0,
1024
- text_size = prop['chart.text.size'],
1025
- units_pre = prop['chart.units.pre'],
1026
- units_post = prop['chart.units.post'],
1027
- numYLabels = prop['chart.ylabels.count'],
1028
- invert = prop['chart.ylabels.invert'],
1029
- inside = prop['chart.ylabels.inside'],
1030
- context = co,
1031
- canvas = ca,
1032
- boxed = false,
1033
- offsetx = prop['chart.ylabels.offsetx'],
1034
- offsety = prop['chart.ylabels.offsety']
1035
-
1036
- this.halfTextHeight = text_size / 2;
1037
-
1038
-
1039
- this.halfGraphHeight = (ca.height - this.gutterTop - this.gutterBottom) / 2;
1040
-
1041
- /**
1042
- * Draw the Y yaxis labels, be it at the top or center
1043
- */
1044
- if (prop['chart.ylabels']) {
1045
-
1046
- var xPos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1047
- var align = prop['chart.yaxispos'] == 'right' ? 'left' : 'right';
1048
-
1049
- /**
1050
- * Now change the two things above if chart.ylabels.inside is specified
1051
- */
1052
- if (inside) {
1053
- if (prop['chart.yaxispos'] == 'left') {
1054
- xPos = prop['chart.gutter.left'] + 5;
1055
- align = 'left';
1056
- boxed = true;
1057
- } else {
1058
- xPos = ca.width - prop['chart.gutter.right'] - 5;
1059
- align = 'right';
1060
- boxed = true;
1061
- }
1062
- }
1063
-
1064
- if (prop['chart.xaxispos'] == 'center') {
1065
-
1066
-
1067
- /**
1068
- * Specific Y labels
1069
- */
1070
- if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific'] != null && prop['chart.ylabels.specific'].length) {
1071
-
1072
- var labels = prop['chart.ylabels.specific'];
1073
-
1074
- if (prop['chart.ymin'] > 0) {
1075
- labels = [];
1076
- for (var i=0; i<(prop['chart.ylabels.specific'].length - 1); ++i) {
1077
- labels.push(prop['chart.ylabels.specific'][i]);
1078
- }
1079
- }
1080
-
1081
- for (var i=0; i<labels.length; ++i) {
1082
- var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) );
1083
- RG.Text2(this, {
1084
- 'font':font,
1085
- 'size':text_size,
1086
- 'x':xPos + offsetx,
1087
- 'y':y + offsety,
1088
- 'text':labels[i],
1089
- 'valign':'center',
1090
- 'halign':align,
1091
- 'bounding':boxed,
1092
- 'tag': 'labels.specific'
1093
- });
1094
- }
1095
-
1096
- var reversed_labels = RG.array_reverse(labels);
1097
-
1098
- for (var i=0; i<reversed_labels.length; ++i) {
1099
- var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) );
1100
- RG.Text2(this, {
1101
- 'font':font,
1102
- 'size':text_size,
1103
- 'x':xPos + offsetx,
1104
- 'y':y + offsety,
1105
- 'text':reversed_labels[i],
1106
- 'valign':'center',
1107
- 'halign':align,
1108
- 'bounding':boxed,
1109
- 'tag': 'labels.specific'
1110
- });
1111
- }
1112
-
1113
- /**
1114
- * Draw the center label if chart.ymin is specified
1115
- */
1116
- if (prop['chart.ymin'] != 0) {
1117
- RG.Text2(this, {
1118
- 'font':font,
1119
- 'size':text_size,
1120
- 'x':xPos + offsetx,
1121
- 'y':(this.grapharea / 2) + this.gutterTop + offsety,
1122
- 'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length - 1],
1123
- 'valign':'center',
1124
- 'halign':align,
1125
- 'bounding':boxed,
1126
- 'tag': 'labels.specific'
1127
- });
1128
- }
1129
- }
1130
-
1131
-
1132
- if (!prop['chart.ylabels.specific'] && typeof numYLabels == 'number') {
1133
-
1134
- /**
1135
- * Draw the top half
1136
- */
1137
- for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
1138
-
1139
- //var value = ((this.max - this.min)/ numYLabels) * (i+1);
1140
- //value = (invert ? this.max - value : value);
1141
- //if (!invert) value += this.min;
1142
- //value = value.toFixed(prop['chart.scale.decimals']);
1143
-
1144
- if (!invert) {
1145
- RG.Text2(this, {
1146
- 'font':font,
1147
- 'size': text_size,
1148
- 'x': xPos + offsetx,
1149
- 'y': this.gutterTop + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
1150
- 'valign': 'center',
1151
- 'halign':align,
1152
- 'bounding': boxed,
1153
- 'boundingFill': 'white',
1154
- 'text': this.scale2.labels[i],
1155
- 'tag': 'scale'
1156
- });
1157
- } else {
1158
- RG.Text2(this, {
1159
- 'font':font,
1160
- 'size': text_size,
1161
- 'x': xPos + offsetx,
1162
- 'y': this.gutterTop + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
1163
- 'valign': 'center',
1164
- 'halign':align,
1165
- 'bounding': boxed,
1166
- 'boundingFill': 'white',
1167
- 'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
1168
- 'tag': 'scale'
1169
- });
1170
- }
1171
- }
1172
-
1173
- /**
1174
- * Draw the bottom half
1175
- */
1176
- for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
1177
-
1178
- //var value = (((this.max - this.min)/ numYLabels) * i) + this.min;
1179
- // value = (invert ? value : this.max - (value - this.min)).toFixed(prop['chart.scale.decimals']);
1180
-
1181
- if (!invert) {
1182
- RG.Text2(this, {
1183
- 'font':font,
1184
- 'size': text_size,
1185
- 'x': xPos + offsetx,
1186
- 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
1187
- 'valign': 'center',
1188
- 'halign':align,
1189
- 'bounding': boxed,
1190
- 'boundingFill': 'white',
1191
- 'text': '-' + this.scale2.labels[len - (i+1)],
1192
- 'tag': 'scale'
1193
- });
1194
- } else {
1195
-
1196
- // This ensures that the center label isn't drawn twice
1197
- if (i == (len - 1)&& invert) {
1198
- continue;
1199
- }
1200
-
1201
- RG.Text2(this, {
1202
- 'font':font,
1203
- 'size': text_size,
1204
- 'x': xPos + offsetx,
1205
- 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
1206
- 'valign': 'center',
1207
- 'halign':align,
1208
- 'bounding': boxed,
1209
- 'boundingFill': 'white',
1210
- 'text': '-' + this.scale2.labels[i],
1211
- 'tag': 'scale'
1212
- });
1213
- }
1214
- }
1215
-
1216
-
1217
-
1218
-
1219
- // If ymin is specified draw that
1220
- if (!invert && (yMin > 0 || prop['chart.scale.zerostart'])) {
1221
-
1222
- RG.text2(this, {
1223
- font:font,
1224
- size: text_size,
1225
- x: xPos + offsetx,
1226
- y: this.gutterTop + this.halfGraphHeight + offsety,
1227
- valign: 'center',
1228
- halign:align,
1229
- bounding: boxed,
1230
- boundingFill: 'white',
1231
- text: RG.numberFormat(this, yMin.toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1232
- tag: 'scale'
1233
- });
1234
- }
1235
-
1236
- if (invert) {
1237
- RG.text2(this, {
1238
- 'font':font,
1239
- 'size': text_size,
1240
- 'x': xPos + offsetx,
1241
- 'y': this.gutterTop + offsety,
1242
- 'valign': 'center',
1243
- 'halign':align,
1244
- 'bounding': boxed,
1245
- 'boundingFill': 'white',
1246
- 'text': RG.number_format(this, yMin.toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1247
- 'tag': 'scale'
1248
- });
1249
-
1250
- RG.text2(this, {
1251
- 'font':font,
1252
- 'size': text_size,
1253
- 'x': xPos + offsetx,
1254
- 'y': this.gutterTop + (this.halfGraphHeight * 2) + offsety,
1255
- 'valign': 'center',
1256
- 'halign':align,
1257
- 'bounding': boxed,
1258
- 'boundingFill': 'white',
1259
- 'text': '-' + RG.numberFormat(this, yMin.toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1260
- 'tag': 'scale'
1261
- });
1262
- }
1263
- }
1264
-
1265
- // X axis at the bottom
1266
- } else {
1267
-
1268
- var xPos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1269
- var align = prop['chart.yaxispos'] == 'right' ? 'left' : 'right';
1270
-
1271
- if (inside) {
1272
- if (prop['chart.yaxispos'] == 'left') {
1273
- xPos = prop['chart.gutter.left'] + 5;
1274
- align = 'left';
1275
- boxed = true;
1276
- } else {
1277
- xPos = ca.width - this.gutterRight - 5;
1278
- align = 'right';
1279
- boxed = true;
1280
- }
1281
- }
1282
-
1283
- /**
1284
- * Specific Y labels
1285
- */
1286
- if (typeof prop['chart.ylabels.specific'] == 'object' && prop['chart.ylabels.specific']) {
1287
-
1288
- var labels = prop['chart.ylabels.specific'];
1289
-
1290
- // Lose the last label
1291
- if (prop['chart.ymin'] > 9999) {
1292
- labels = [];
1293
- for (var i=0; i<(prop['chart.ylabels.specific'].length - 1); ++i) {
1294
- labels.push(prop['chart.ylabels.specific'][i]);
1295
- }
1296
- }
1297
-
1298
- for (var i=0,len=labels.length; i<len; i+=1) {
1299
-
1300
- var y = this.gutterTop + (i * (this.grapharea / (len - 1)) );
1301
-
1302
- RG.Text2(this, {
1303
- 'font':font,
1304
- 'size':text_size,
1305
- 'x':xPos + offsetx,
1306
- 'y':y + offsety,
1307
- 'text':labels[i],
1308
- 'halign':align,
1309
- 'valign':'center',
1310
- 'bounding':boxed,
1311
- 'tag': 'scale'
1312
- });
1313
- }
1314
-
1315
- /**
1316
- * X axis at the bottom with a scale
1317
- */
1318
- } else {
1319
-
1320
- if (typeof(numYLabels) == 'number') {
1321
-
1322
- if (invert) {
1323
-
1324
- for (var i=0; i<numYLabels; ++i) {
1325
-
1326
- //var value = ((this.max - this.min)/ numYLabels) * i;
1327
- // value = value.toFixed(prop['chart.scale.decimals']);
1328
- var interval = (ca.height - this.gutterTop - this.gutterBottom) / numYLabels;
1329
-
1330
- RG.Text2(this, {
1331
- 'font':font,
1332
- 'size': text_size,
1333
- 'x': xPos + offsetx,
1334
- 'y': this.gutterTop + ((i+1) * interval) + offsety,
1335
- 'valign': 'center',
1336
- 'halign':align,
1337
- 'bounding': boxed,
1338
- 'boundingFill': 'white',
1339
- 'text': this.scale2.labels[i],
1340
- 'tag': 'scale'
1341
- });
1342
- }
1343
-
1344
-
1345
- // No X axis being shown and there's no ymin. If ymin IS set its added further down
1346
- if (!prop['chart.xaxis'] && !prop['chart.ymin']) {
1347
-
1348
- RG.Text2(this, {
1349
- 'font':font,
1350
- 'size': text_size,
1351
- 'x': xPos + offsetx,
1352
- 'y': this.gutterTop + offsety,
1353
- 'valign': 'center',
1354
- 'halign':align,
1355
- 'bounding': boxed,
1356
- 'boundingFill': 'white',
1357
- 'text': RG.numberFormat(this, (this.min).toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1358
- 'tag': 'scale'
1359
- });
1360
- }
1361
-
1362
- } else {
1363
-
1364
- for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
1365
-
1366
- //var value = ((this.max - this.min)/ numYLabels) * (i+1);
1367
- // value = (invert ? this.max - value : value);
1368
- // if (!invert) value += this.min;
1369
- // value = value.toFixed(prop['chart.scale.decimals']);
1370
-
1371
- RG.Text2(this, {
1372
- 'font':font,
1373
- 'size': text_size,
1374
- 'x': xPos + offsetx,
1375
- 'y': this.gutterTop + this.grapharea - (((i + 1)/this.scale2.labels.length) * this.grapharea) + offsety,
1376
- 'valign': 'center',
1377
- 'halign':align,
1378
- 'bounding': boxed,
1379
- 'boundingFill': 'white',
1380
- 'text': this.scale2.labels[i],
1381
- 'tag': 'scale'
1382
- });
1383
- }
1384
-
1385
- if (!prop['chart.xaxis'] && prop['chart.ymin'] == 0) {
1386
- RG.text2(this, {
1387
- font:font,
1388
- size: text_size,
1389
- x: xPos + offsetx,
1390
- y: ca.height - this.gutterBottom + offsety,
1391
- valign: 'center',
1392
- halign:align,
1393
- boundin: boxed,
1394
- boundingFill: 'white',
1395
- text: RG.numberFormat(this, (0).toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1396
- tag: 'scale'
1397
- });
1398
- }
1399
- }
1400
- }
1401
-
1402
- if ( (prop['chart.ymin'] || prop['chart.scale.zerostart']) && !invert) {
1403
- RG.text2(this, {
1404
- 'font':font,
1405
- 'size': text_size,
1406
- 'x': xPos + offsetx,
1407
- 'y': ca.height - this.gutterBottom + offsety,
1408
- 'valign': 'center',
1409
- 'halign':align,
1410
- 'bounding': boxed,
1411
- 'boundingFill': 'white',
1412
- 'text': RG.numberFormat(this, prop['chart.ymin'].toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1413
- 'tag': 'scale'
1414
- });
1415
-
1416
- } else if (invert) {
1417
- RG.text2(this, {
1418
- 'font':font,
1419
- 'size': text_size,
1420
- 'x': xPos + offsetx,
1421
- 'y': this.gutterTop + offsety,
1422
- 'valign': 'center',
1423
- 'halign':align,
1424
- 'bounding': boxed,
1425
- 'boundingFill': 'white',
1426
- 'text': RG.numberFormat(this, prop['chart.ymin'].toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1427
- 'tag': 'scale'
1428
- });
1429
- }
1430
- }
1431
- }
1432
- }
1433
-
1434
-
1435
-
1436
-
1437
- /**
1438
- * Draw an X scale
1439
- */
1440
- if (prop['chart.xscale']) {
1441
-
1442
- var numXLabels = prop['chart.xscale.numlabels'],
1443
- y = ca.height - this.gutterBottom + 5 + (text_size / 2),
1444
- units_pre_x = prop['chart.xscale.units.pre'],
1445
- units_post_x = prop['chart.xscale.units.post'],
1446
- decimals = prop['chart.xscale.decimals'],
1447
- point = prop['chart.xscale.point'],
1448
- thousand = prop['chart.xscale.thousand'],
1449
- color = prop['chart.labels.color'],
1450
- bold = prop['chart.labels.bold'],
1451
- offsetx = prop['chart.labels.offsetx'],
1452
- offsety = prop['chart.labels.offsety']
1453
-
1454
-
1455
- if (!prop['chart.xmax']) {
1456
-
1457
- var xmax = 0;
1458
- var xmin = prop['chart.xmin'];
1459
-
1460
- for (var ds=0,len=this.data.length; ds<len; ds+=1) {
1461
- for (var point=0,len2=this.data[ds].length; point<len2; point+=1) {
1462
- xmax = Math.max(xmax, this.data[ds][point][0]);
1463
- }
1464
- }
1465
- } else {
1466
- xmax = prop['chart.xmax'];
1467
- xmin = prop['chart.xmin']
1468
- }
1469
-
1470
- this.xscale2 = RG.getScale2(this, {
1471
- 'max':xmax,
1472
- 'min': xmin,
1473
- 'scale.decimals': decimals,
1474
- 'scale.point': point,
1475
- 'scale.thousand': thousand,
1476
- 'units.pre': units_pre_x,
1477
- 'units.post': units_post_x,
1478
- 'ylabels.count': numXLabels,
1479
- 'strict': true
1480
- });
1481
-
1482
- this.Set('chart.xmax', this.xscale2.max);
1483
- var interval = (ca.width - this.gutterLeft - this.gutterRight) / this.xscale2.labels.length;
1484
-
1485
- for (var i=0,len=this.xscale2.labels.length; i<len; i+=1) {
1486
-
1487
- var num = ( (prop['chart.xmax'] - prop['chart.xmin']) * ((i+1) / numXLabels)) + (xmin || 0),
1488
- x = this.gutterLeft + ((i+1) * interval),
1489
-
1490
- // Repeated a few lines down
1491
- text = typeof prop['chart.xscale.formatter'] === 'function' ? String(prop['chart.xscale.formatter'](this, num)) : this.xscale2.labels[i];
1492
-
1493
- RG.text2(this, {
1494
- 'color': color,
1495
- 'font':font,
1496
- 'size': text_size,
1497
- 'bold': bold,
1498
- 'x': x + offsetx,
1499
- 'y': y + offsety,
1500
- 'valign': 'center',
1501
- 'halign':'center',
1502
- 'text':text,
1503
- 'tag': 'xscale'
1504
- });
1505
- }
1506
-
1507
- // If the Y axis is on the right hand side - draw the left most X label
1508
- // ** Always added now **
1509
-
1510
- // Repeated a few lines up
1511
- var text = typeof prop['chart.xscale.formatter'] === 'function' ? String(prop['chart.xscale.formatter'](this, prop['chart.xmin'])) : String(prop['chart.xmin']);
1512
-
1513
- RG.text2(this, {
1514
- 'color': color,
1515
- 'font':font,
1516
- 'size': text_size,
1517
- 'bold':bold,
1518
- 'x': this.gutterLeft + offsetx,
1519
- 'y': y + offsety,
1520
- 'valign': 'center',
1521
- 'halign':'center',
1522
- 'text': text,
1523
- 'tag': 'xscale'
1524
- });
1525
-
1526
- /**
1527
- * Draw X labels
1528
- */
1529
- } else {
1530
-
1531
- // Put the text on the X axis
1532
- var graphArea = ca.width - this.gutterLeft - this.gutterRight;
1533
- var xInterval = graphArea / prop['chart.labels'].length;
1534
- var xPos = this.gutterLeft;
1535
- var yPos = (ca.height - this.gutterBottom) + 3;
1536
- var labels = prop['chart.labels'];
1537
- var color = prop['chart.labels.color'];
1538
- var bold = prop['chart.labels.bold'];
1539
- var offsetx = prop['chart.labels.offsetx'];
1540
- var offsety = prop['chart.labels.offsety'];
1541
-
1542
- /**
1543
- * Text angle
1544
- */
1545
- var angle = 0;
1546
- var valign = 'top';
1547
- var halign = 'center';
1548
-
1549
- if (prop['chart.text.angle'] > 0) {
1550
- angle = -1 * prop['chart.text.angle'];
1551
- valign = 'center';
1552
- halign = 'right';
1553
- yPos += 10;
1554
- }
1555
-
1556
- for (i=0; i<labels.length; ++i) {
1557
-
1558
- if (typeof(labels[i]) == 'object') {
1559
-
1560
- if (prop['chart.labels.specific.align'] == 'center') {
1561
- var rightEdge = 0;
1562
-
1563
- if (labels[i+1] && labels[i+1][1]) {
1564
- rightEdge = labels[i+1][1];
1565
- } else {
1566
- rightEdge = prop['chart.xmax'];
1567
- }
1568
-
1569
- var offset = (this.getXCoord(rightEdge) - this.getXCoord(labels[i][1])) / 2;
1570
-
1571
- } else {
1572
- var offset = 5;
1573
- }
1574
-
1575
-
1576
- RG.text2(this, {
1577
- 'color': color,
1578
- 'font':font,
1579
- 'size': prop['chart.text.size'],
1580
- 'bold': bold,
1581
- 'x': this.getXCoord(labels[i][1]) + offset + offsetx,
1582
- 'y': yPos + offsety,
1583
- 'valign': valign,
1584
- 'halign':angle != 0 ? 'right' : (prop['chart.labels.specific.align'] == 'center' ? 'center' : 'left'),
1585
- 'text':String(labels[i][0]),
1586
- 'angle':angle,
1587
- 'marker':false,
1588
- 'tag': 'labels.specific'
1589
- });
1590
-
1591
- /**
1592
- * Draw the gray indicator line
1593
- */
1594
- co.beginPath();
1595
- co.strokeStyle = '#bbb';
1596
- co.moveTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom);
1597
- co.lineTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom + 20);
1598
- co.stroke();
1599
-
1600
- } else {
1601
-
1602
- RG.text2(this, {
1603
- 'color': color,
1604
- 'font':font,
1605
- 'size': prop['chart.text.size'],
1606
- 'bold': bold,
1607
- 'x': xPos + (xInterval / 2) + offsetx,
1608
- 'y': yPos + offsety,
1609
- 'valign': valign,
1610
- 'halign':halign,
1611
- 'text':String(labels[i]),
1612
- 'angle':angle,
1613
- 'tag': 'labels'
1614
- });
1615
- }
1616
-
1617
- // Do this for the next time around
1618
- xPos += xInterval;
1619
- }
1620
-
1621
- /**
1622
- * Draw the final indicator line
1623
- */
1624
- if (typeof(labels[0]) == 'object') {
1625
- co.beginPath();
1626
- co.strokeStyle = '#bbb';
1627
- co.moveTo(this.gutterLeft + graphArea, ca.height - this.gutterBottom);
1628
- co.lineTo(this.gutterLeft + graphArea, ca.height - this.gutterBottom + 20);
1629
- co.stroke();
1630
- }
1631
- }
1632
- };
1633
-
1634
-
1635
-
1636
-
1637
-
1638
-
1639
-
1640
-
1641
- /**
1642
- * Draws the actual scatter graph marks
1643
- *
1644
- * @param i integer The dataset index
1645
- */
1646
- this.drawMarks =
1647
- this.DrawMarks = function (i)
1648
- {
1649
- /**
1650
- * Reset the coords array
1651
- */
1652
- this.coords[i] = [];
1653
-
1654
- /**
1655
- * Plot the values
1656
- */
1657
- var xmax = prop['chart.xmax'];
1658
- var default_color = prop['chart.defaultcolor'];
1659
-
1660
- for (var j=0,len=this.data[i].length; j<len; j+=1) {
1661
- /**
1662
- * This is here because tooltips are optional
1663
- */
1664
- var data_points = this.data[i];
1665
-
1666
- // Allow for null points
1667
- if (RG.isNull(data_points[j])) {
1668
- continue;
1669
- }
1670
-
1671
- var xCoord = data_points[j][0];
1672
- var yCoord = data_points[j][1];
1673
- var color = data_points[j][2] ? data_points[j][2] : default_color;
1674
- var tooltip = (data_points[j] && data_points[j][3]) ? data_points[j][3] : null;
1675
-
1676
-
1677
- this.DrawMark(
1678
- i,
1679
- xCoord,
1680
- yCoord,
1681
- xmax,
1682
- this.scale2.max,
1683
- color,
1684
- tooltip,
1685
- this.coords[i],
1686
- data_points,
1687
- j
1688
- );
1689
- }
1690
- };
1691
-
1692
-
1693
-
1694
-
1695
-
1696
-
1697
-
1698
-
1699
- /**
1700
- * Draws a single scatter mark
1701
- */
1702
- this.drawMark =
1703
- this.DrawMark = function (data_set_index, x, y, xMax, yMax, color, tooltip, coords, data, data_index)
1704
- {
1705
- var tickmarks = prop['chart.tickmarks'],
1706
- tickSize = prop['chart.ticksize'],
1707
- xMin = prop['chart.xmin'],
1708
- x = ((x - xMin) / (xMax - xMin)) * (ca.width - this.gutterLeft - this.gutterRight),
1709
- originalX = x,
1710
- originalY = y;
1711
-
1712
- /**
1713
- * This allows chart.tickmarks to be an array
1714
- */
1715
- if (tickmarks && typeof(tickmarks) == 'object') {
1716
- tickmarks = tickmarks[data_set_index];
1717
- }
1718
-
1719
-
1720
- /**
1721
- * This allows chart.ticksize to be an array
1722
- */
1723
- if (typeof(tickSize) == 'object') {
1724
- var tickSize = tickSize[data_set_index];
1725
- var halfTickSize = tickSize / 2;
1726
- } else {
1727
- var halfTickSize = tickSize / 2;
1728
- }
1729
-
1730
-
1731
- /**
1732
- * This bit is for boxplots only
1733
- */
1734
- if ( y
1735
- && typeof y === 'object'
1736
- && typeof y[0] === 'number'
1737
- && typeof y[1] === 'number'
1738
- && typeof y[2] === 'number'
1739
- && typeof y[3] === 'number'
1740
- && typeof y[4] === 'number'
1741
- ) {
1742
-
1743
- //var yMin = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
1744
- this.Set('chart.boxplot', true);
1745
- //this.graphheight = ca.height - this.gutterTop - this.gutterBottom;
1746
-
1747
- //if (prop['chart.xaxispos'] == 'center') {
1748
- // this.graphheight /= 2;
1749
- //}
1750
-
1751
-
1752
- var y0 = this.getYCoord(y[0]),
1753
- y1 = this.getYCoord(y[1]),
1754
- y2 = this.getYCoord(y[2]),
1755
- y3 = this.getYCoord(y[3]),
1756
- y4 = this.getYCoord(y[4]),
1757
- col1 = y[5],
1758
- col2 = y[6],
1759
- boxWidth = typeof y[7] == 'number' ? y[7] : prop['chart.boxplot.width'];
1760
-
1761
- //var y = this.graphheight - y2;
1762
-
1763
- } else {
1764
-
1765
- /**
1766
- * The new way of getting the Y coord. This function (should) handle everything
1767
- */
1768
- var yCoord = this.getYCoord(y);
1769
- }
1770
-
1771
- //if (prop['chart.xaxispos'] == 'center'] {
1772
- // y /= 2;
1773
- // y += this.halfGraphHeight;
1774
- //
1775
- // if (prop['chart.ylabels.invert']) {
1776
- // p(y)
1777
- // }
1778
- //}
1779
-
1780
- /**
1781
- * Account for the X axis being at the centre
1782
- */
1783
- // This is so that points are on the graph, and not the gutter - which helps
1784
- x += this.gutterLeft;
1785
- //y = ca.height - this.gutterBottom - y;
1786
-
1787
-
1788
-
1789
-
1790
- co.beginPath();
1791
-
1792
- // Color
1793
- co.strokeStyle = color;
1794
-
1795
-
1796
-
1797
- /**
1798
- * Boxplots
1799
- */
1800
- if (prop['chart.boxplot']) {
1801
-
1802
- // boxWidth is a scale value, so convert it to a pixel vlue
1803
- boxWidth = (boxWidth / prop['chart.xmax']) * (ca.width - this.gutterLeft - this.gutterRight);
1804
-
1805
- var halfBoxWidth = boxWidth / 2;
1806
-
1807
- if (prop['chart.line.visible']) {
1808
- co.beginPath();
1809
-
1810
- // Set the outline color of the box
1811
-
1812
- if (typeof y[8] === 'string') {
1813
- co.strokeStyle = y[8];
1814
- }
1815
- co.strokeRect(x - halfBoxWidth, y1, boxWidth, y3 - y1);
1816
-
1817
- // Draw the upper coloured box if a value is specified
1818
- if (col1) {
1819
- co.fillStyle = col1;
1820
- co.fillRect(x - halfBoxWidth, y1, boxWidth, y2 - y1);
1821
- }
1822
-
1823
- // Draw the lower coloured box if a value is specified
1824
- if (col2) {
1825
- co.fillStyle = col2;
1826
- co.fillRect(x - halfBoxWidth, y2, boxWidth, y3 - y2);
1827
- }
1828
- co.stroke();
1829
-
1830
- // Now draw the whiskers
1831
- co.beginPath();
1832
- if (prop['chart.boxplot.capped']) {
1833
- co.moveTo(x - halfBoxWidth, ma.round(y0));
1834
- co.lineTo(x + halfBoxWidth, ma.round(y0));
1835
- }
1836
-
1837
- co.moveTo(ma.round(x), y0);
1838
- co.lineTo(ma.round(x ), y1);
1839
-
1840
- if (prop['chart.boxplot.capped']) {
1841
- co.moveTo(x - halfBoxWidth, ma.round(y4));
1842
- co.lineTo(x + halfBoxWidth, ma.round(y4));
1843
- }
1844
-
1845
- co.moveTo(ma.round(x), y4);
1846
- co.lineTo(ma.round(x), y3);
1847
-
1848
- co.stroke();
1849
- }
1850
- }
1851
-
1852
-
1853
- /**
1854
- * Draw the tickmark, but not for boxplots
1855
- */
1856
- if (prop['chart.line.visible'] && typeof(y) == 'number' && !y0 && !y1 && !y2 && !y3 && !y4) {
1857
-
1858
- if (tickmarks == 'circle') {
1859
- co.arc(x, yCoord, halfTickSize, 0, 6.28, 0);
1860
- co.fillStyle = color;
1861
- co.fill();
1862
-
1863
- } else if (tickmarks == 'plus') {
1864
-
1865
- co.moveTo(x, yCoord - halfTickSize);
1866
- co.lineTo(x, yCoord + halfTickSize);
1867
- co.moveTo(x - halfTickSize, yCoord);
1868
- co.lineTo(x + halfTickSize, yCoord);
1869
- co.stroke();
1870
-
1871
- } else if (tickmarks == 'square') {
1872
- co.strokeStyle = color;
1873
- co.fillStyle = color;
1874
- co.fillRect(
1875
- x - halfTickSize,
1876
- yCoord - halfTickSize,
1877
- tickSize,
1878
- tickSize
1879
- );
1880
- //co.fill();
1881
-
1882
- } else if (tickmarks == 'cross') {
1883
-
1884
- co.moveTo(x - halfTickSize, yCoord - halfTickSize);
1885
- co.lineTo(x + halfTickSize, yCoord + halfTickSize);
1886
- co.moveTo(x + halfTickSize, yCoord - halfTickSize);
1887
- co.lineTo(x - halfTickSize, yCoord + halfTickSize);
1888
-
1889
- co.stroke();
1890
-
1891
- /**
1892
- * Diamond shape tickmarks
1893
- */
1894
- } else if (tickmarks == 'diamond') {
1895
- co.fillStyle = co.strokeStyle;
1896
-
1897
- co.moveTo(x, yCoord - halfTickSize);
1898
- co.lineTo(x + halfTickSize, yCoord);
1899
- co.lineTo(x, yCoord + halfTickSize);
1900
- co.lineTo(x - halfTickSize, yCoord);
1901
- co.lineTo(x, yCoord - halfTickSize);
1902
-
1903
- co.fill();
1904
- co.stroke();
1905
-
1906
- /**
1907
- * Custom tickmark style
1908
- */
1909
- } else if (typeof(tickmarks) == 'function') {
1910
-
1911
- var graphWidth = ca.width - this.gutterLeft - this.gutterRight,
1912
- graphheight = ca.height - this.gutterTop - this.gutterBottom,
1913
- xVal = ((x - this.gutterLeft) / graphWidth) * xMax,
1914
- yVal = ((graphheight - (yCoord - this.gutterTop)) / graphheight) * yMax;
1915
-
1916
- tickmarks(this, data, x, yCoord, xVal, yVal, xMax, yMax, color, data_set_index, data_index)
1917
-
1918
-
1919
-
1920
-
1921
-
1922
-
1923
-
1924
-
1925
-
1926
-
1927
-
1928
-
1929
-
1930
-
1931
-
1932
-
1933
-
1934
- /**
1935
- * Image based tickmark
1936
- */
1937
- // lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index
1938
- } else if (
1939
- typeof tickmarks === 'string' &&
1940
- (
1941
- tickmarks.substr(0, 6) === 'image:' ||
1942
- tickmarks.substr(0, 5) === 'data:' ||
1943
- tickmarks.substr(0, 1) === '/' ||
1944
- tickmarks.substr(0, 3) === '../' ||
1945
- tickmarks.substr(0, 7) === 'images/'
1946
- )
1947
- ) {
1948
-
1949
- var img = new Image();
1950
-
1951
- if (tickmarks.substr(0, 6) === 'image:') {
1952
- img.src = tickmarks.substr(6);
1953
- } else {
1954
- img.src = tickmarks;
1955
- }
1956
-
1957
-
1958
- img.onload = function ()
1959
- {
1960
- if (prop['chart.tickmarks.image.halign'] === 'center') x -= (this.width / 2);
1961
- if (prop['chart.tickmarks.image.halign'] === 'right') x -= this.width;
1962
-
1963
- if (prop['chart.tickmarks.image.valign'] === 'center') yCoord -= (this.height / 2);
1964
- if (prop['chart.tickmarks.image.valign'] === 'bottom') yCoord -= this.height;
1965
-
1966
- x += prop['chart.tickmarks.image.offsetx'];
1967
- yCoord += prop['chart.tickmarks.image.offsety'];
1968
-
1969
- co.drawImage(this, x, yCoord);
1970
- }
1971
-
1972
-
1973
-
1974
-
1975
-
1976
- /**
1977
- * No tickmarks
1978
- */
1979
- } else if (tickmarks === null) {
1980
-
1981
- /**
1982
- * Unknown tickmark type
1983
- */
1984
- } else {
1985
- alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks );
1986
- }
1987
- }
1988
-
1989
- /**
1990
- * Add the tickmark to the coords array
1991
- */
1992
-
1993
- if ( prop['chart.boxplot']
1994
- && typeof y0 === 'number'
1995
- && typeof y1 === 'number'
1996
- && typeof y2 === 'number'
1997
- && typeof y3 === 'number'
1998
- && typeof y4 === 'number') {
1999
-
2000
- x = [x - halfBoxWidth, x + halfBoxWidth];
2001
- yCoord = [y0, y1, y2, y3, y4];
2002
- }
2003
-
2004
- coords.push([x, yCoord, tooltip]);
2005
- };
2006
-
2007
-
2008
-
2009
-
2010
-
2011
-
2012
-
2013
-
2014
- /**
2015
- * Draws an optional line connecting the tick marks.
2016
- *
2017
- * @param i The index of the dataset to use
2018
- */
2019
- this.drawLine =
2020
- this.DrawLine = function (i)
2021
- {
2022
- if (typeof(prop['chart.line.visible']) == 'boolean' && prop['chart.line.visible'] == false) {
2023
- return;
2024
- }
2025
-
2026
- if (prop['chart.line'] && this.coords[i].length >= 2) {
2027
-
2028
- if (prop['chart.line.dash'] && typeof co.setLineDash === 'function') {
2029
- co.setLineDash(prop['chart.line.dash']);
2030
- }
2031
-
2032
- co.lineCap = 'round';
2033
- co.lineJoin = 'round';
2034
- co.lineWidth = this.getLineWidth(i);// i is the index of the set of coordinates
2035
- co.strokeStyle = prop['chart.line.colors'][i];
2036
-
2037
- co.beginPath();
2038
-
2039
- var prevY = null;
2040
- var currY = null;
2041
-
2042
- for (var j=0,len=this.coords[i].length; j<len; j+=1) {
2043
-
2044
-
2045
- var xPos = this.coords[i][j][0];
2046
- var yPos = this.coords[i][j][1];
2047
-
2048
- if (j > 0) prevY = this.coords[i][j - 1][1];
2049
- currY = yPos;
2050
-
2051
- if (j == 0 || RG.is_null(prevY) || RG.is_null(currY)) {
2052
- co.moveTo(xPos, yPos);
2053
- } else {
2054
-
2055
- // Stepped?
2056
- var stepped = prop['chart.line.stepped'];
2057
-
2058
- if ( (typeof stepped == 'boolean' && stepped)
2059
- || (typeof stepped == 'object' && stepped[i])
2060
- ) {
2061
- co.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]);
2062
- }
2063
-
2064
- co.lineTo(xPos, yPos);
2065
- }
2066
- }
2067
- co.stroke();
2068
-
2069
- /**
2070
- * Set the linedash back to the default
2071
- */
2072
- if (prop['chart.line.dash'] && typeof co.setLineDash === 'function') {
2073
- co.setLineDash([1,0]);
2074
- }
2075
- }
2076
-
2077
- /**
2078
- * Set the linewidth back to 1
2079
- */
2080
- co.lineWidth = 1;
2081
- };
2082
-
2083
-
2084
-
2085
-
2086
-
2087
-
2088
-
2089
-
2090
- /**
2091
- * Returns the linewidth
2092
- *
2093
- * @param number i The index of the "line" (/set of coordinates)
2094
- */
2095
- this.getLineWidth =
2096
- this.GetLineWidth = function (i)
2097
- {
2098
- var linewidth = prop['chart.line.linewidth'];
2099
-
2100
- if (typeof linewidth == 'number') {
2101
- return linewidth;
2102
-
2103
- } else if (typeof linewidth == 'object') {
2104
- if (linewidth[i]) {
2105
- return linewidth[i];
2106
- } else {
2107
- return linewidth[0];
2108
- }
2109
-
2110
- alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');
2111
- }
2112
- };
2113
-
2114
-
2115
-
2116
-
2117
-
2118
-
2119
-
2120
-
2121
- /**
2122
- * Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function
2123
- * is not common
2124
- */
2125
- this.drawVBars =
2126
- this.DrawVBars = function ()
2127
- {
2128
-
2129
- var vbars = prop['chart.background.vbars'];
2130
- var graphWidth = ca.width - this.gutterLeft - this.gutterRight;
2131
-
2132
- if (vbars) {
2133
-
2134
- var xmax = prop['chart.xmax'];
2135
- var xmin = prop['chart.xmin'];
2136
-
2137
- for (var i=0,len=vbars.length; i<len; i+=1) {
2138
-
2139
- var key = i;
2140
- var value = vbars[key];
2141
-
2142
- /**
2143
- * Accomodate date/time values
2144
- */
2145
- if (typeof value[0] == 'string') value[0] = RG.parseDate(value[0]);
2146
- if (typeof value[1] == 'string') value[1] = RG.parseDate(value[1]) - value[0];
2147
-
2148
- var x = (( (value[0] - xmin) / (xmax - xmin) ) * graphWidth) + this.gutterLeft;
2149
- var width = (value[1] / (xmax - xmin) ) * graphWidth;
2150
-
2151
- co.fillStyle = value[2];
2152
- co.fillRect(x, this.gutterTop, width, (ca.height - this.gutterTop - this.gutterBottom));
2153
- }
2154
- }
2155
- };
2156
-
2157
-
2158
-
2159
-
2160
-
2161
-
2162
-
2163
-
2164
- /**
2165
- * Draws in-graph labels.
2166
- *
2167
- * @param object obj The graph object
2168
- */
2169
- this.drawInGraphLabels =
2170
- this.DrawInGraphLabels = function (obj)
2171
- {
2172
- var labels = obj.Get('chart.labels.ingraph');
2173
- var labels_processed = [];
2174
-
2175
- if (!labels) {
2176
- return;
2177
- }
2178
-
2179
- // Defaults
2180
- var fgcolor = 'black';
2181
- var bgcolor = 'white';
2182
- var direction = 1;
2183
-
2184
- /**
2185
- * Preprocess the labels array. Numbers are expanded
2186
- */
2187
- for (var i=0,len=labels.length; i<len; i+=1) {
2188
- if (typeof(labels[i]) == 'number') {
2189
- for (var j=0; j<labels[i]; ++j) {
2190
- labels_processed.push(null);
2191
- }
2192
- } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
2193
- labels_processed.push(labels[i]);
2194
-
2195
- } else {
2196
- labels_processed.push('');
2197
- }
2198
- }
2199
-
2200
- /**
2201
- * Turn off any shadow
2202
- */
2203
- RG.NoShadow(obj);
2204
-
2205
- if (labels_processed && labels_processed.length > 0) {
2206
-
2207
- var i=0;
2208
-
2209
- for (var set=0; set<obj.coords.length; ++set) {
2210
- for (var point = 0; point<obj.coords[set].length; ++point) {
2211
- if (labels_processed[i]) {
2212
- var x = obj.coords[set][point][0];
2213
- var y = obj.coords[set][point][1];
2214
- var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
2215
-
2216
- var text_x = x;
2217
- var text_y = y - 5 - length;
2218
-
2219
- co.moveTo(x, y - 5);
2220
- co.lineTo(x, y - 5 - length);
2221
-
2222
- co.stroke();
2223
- co.beginPath();
2224
-
2225
- // This draws the arrow
2226
- co.moveTo(x, y - 5);
2227
- co.lineTo(x - 3, y - 10);
2228
- co.lineTo(x + 3, y - 10);
2229
- co.closePath();
2230
-
2231
-
2232
- co.beginPath();
2233
- // Fore ground color
2234
- co.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
2235
- RG.text2(this, {
2236
- 'font':obj.Get('chart.text.font'),
2237
- 'size':obj.Get('chart.text.size'),
2238
- 'x':text_x,
2239
- 'y':text_y,
2240
- 'text':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
2241
- 'valign':'bottom',
2242
- 'halign':'center',
2243
- 'bounding':true,
2244
- 'bounding.fill':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white',
2245
- 'tag': 'labels.ingraph'
2246
- });
2247
- co.fill();
2248
- }
2249
-
2250
- i++;
2251
- }
2252
- }
2253
- }
2254
- };
2255
-
2256
-
2257
-
2258
-
2259
-
2260
-
2261
-
2262
-
2263
- /**
2264
- * This function makes it much easier to get the (if any) point that is currently being hovered over.
2265
- *
2266
- * @param object e The event object
2267
- */
2268
- this.getShape =
2269
- this.getPoint = function (e)
2270
- {
2271
- var mouseXY = RG.getMouseXY(e);
2272
- var mouseX = mouseXY[0];
2273
- var mouseY = mouseXY[1];
2274
- var overHotspot = false;
2275
- var offset = prop['chart.tooltips.hotspot']; // This is how far the hotspot extends
2276
-
2277
- for (var set=0,len=this.coords.length; set<len; ++set) {
2278
- for (var i=0,len2=this.coords[set].length; i<len2; ++i) {
2279
-
2280
- var x = this.coords[set][i][0];
2281
- var y = this.coords[set][i][1];
2282
- var tooltip = this.data[set][i][3];
2283
-
2284
- if (typeof(y) == 'number') {
2285
- if (mouseX <= (x + offset) &&
2286
- mouseX >= (x - offset) &&
2287
- mouseY <= (y + offset) &&
2288
- mouseY >= (y - offset)) {
2289
-
2290
- var tooltip = RG.parseTooltipText(this.data[set][i][3], 0);
2291
- var index_adjusted = i;
2292
-
2293
- for (var ds=(set-1); ds >=0; --ds) {
2294
- index_adjusted += this.data[ds].length;
2295
- }
2296
-
2297
- return {
2298
- 0: this, 1: x, 2: y, 3: set, 4: i, 5: this.data[set][i][3],
2299
- 'object': this, 'x': x, 'y': y, 'dataset': set, 'index': i, 'tooltip': tooltip, 'index_adjusted': index_adjusted
2300
- };
2301
- }
2302
- } else if (RG.is_null(y)) {
2303
- // Nothing to see here
2304
-
2305
- } else {
2306
-
2307
- var mark = this.data[set][i];
2308
-
2309
- /**
2310
- * Determine the width
2311
- */
2312
- var width = prop['chart.boxplot.width'];
2313
-
2314
- if (typeof(mark[1][7]) == 'number') {
2315
- width = mark[1][7];
2316
- }
2317
-
2318
- if ( typeof(x) == 'object'
2319
- && mouseX > x[0]
2320
- && mouseX < x[1]
2321
- && mouseY < y[1]
2322
- && mouseY > y[3]
2323
- ) {
2324
-
2325
- var tooltip = RG.parseTooltipText(this.data[set][i][3], 0);
2326
-
2327
- return {
2328
- 0: this, 1: x[0], 2: x[1] - x[0], 3: y[1], 4: y[3] - y[1], 5: set, 6: i, 7: this.data[set][i][3],
2329
- 'object': this, 'x': x[0], 'y': y[1], 'width': x[1] - x[0], 'height': y[3] - y[1], 'dataset': set, 'index': i, 'tooltip': tooltip
2330
- };
2331
- }
2332
- }
2333
- }
2334
- }
2335
- };
2336
-
2337
-
2338
-
2339
-
2340
-
2341
-
2342
-
2343
-
2344
- /**
2345
- * Draws the above line labels
2346
- */
2347
- this.drawAboveLabels =
2348
- this.DrawAboveLabels = function ()
2349
- {
2350
- var size = prop['chart.labels.above.size'];
2351
- var font = prop['chart.text.font'];
2352
- var units_pre = prop['chart.units.pre'];
2353
- var units_post = prop['chart.units.post'];
2354
-
2355
-
2356
- for (var set=0,len=this.coords.length; set<len; ++set) {
2357
- for (var point=0,len2=this.coords[set].length; point<len2; ++point) {
2358
-
2359
- var x_val = this.data[set][point][0];
2360
- var y_val = this.data[set][point][1];
2361
-
2362
- if (!RG.is_null(y_val)) {
2363
-
2364
- // Use the top most value from a box plot
2365
- if (RG.is_array(y_val)) {
2366
- var max = 0;
2367
- for (var i=0; i<y_val; ++i) {
2368
- max = Math.max(max, y_val[i]);
2369
- }
2370
-
2371
- y_val = max;
2372
- }
2373
-
2374
- var x_pos = this.coords[set][point][0];
2375
- var y_pos = this.coords[set][point][1];
2376
-
2377
- RG.Text2(this, {
2378
- 'font':font,
2379
- 'size':size,
2380
- 'x':x_pos,
2381
- 'y':y_pos - 5 - size,
2382
- 'text':x_val.toFixed(prop['chart.labels.above.decimals']) + ', ' + y_val.toFixed(prop['chart.labels.above.decimals']),
2383
- 'valign':'center',
2384
- 'halign':'center',
2385
- 'bounding':true,
2386
- 'boundingFill':'rgba(255, 255, 255, 0.7)',
2387
- 'tag': 'labels.above'
2388
- });
2389
- }
2390
- }
2391
- }
2392
- };
2393
-
2394
-
2395
-
2396
-
2397
-
2398
-
2399
-
2400
-
2401
- /**
2402
- * When you click on the chart, this method can return the Y value at that point. It works for any point on the
2403
- * chart (that is inside the gutters) - not just points within the Bars.
2404
- *
2405
- * @param object e The event object
2406
- */
2407
- this.getYValue =
2408
- this.getValue = function (arg)
2409
- {
2410
- if (arg.length == 2) {
2411
- var mouseX = arg[0];
2412
- var mouseY = arg[1];
2413
- } else {
2414
- var mouseCoords = RG.getMouseXY(arg);
2415
- var mouseX = mouseCoords[0];
2416
- var mouseY = mouseCoords[1];
2417
- }
2418
-
2419
- var obj = this;
2420
-
2421
- if ( mouseY < this.gutterTop
2422
- || mouseY > (ca.height - this.gutterBottom)
2423
- || mouseX < this.gutterLeft
2424
- || mouseX > (ca.width - this.gutterRight)
2425
- ) {
2426
- return null;
2427
- }
2428
-
2429
- if (prop['chart.xaxispos'] == 'center') {
2430
- var value = (((this.grapharea / 2) - (mouseY - this.gutterTop)) / this.grapharea) * (this.max - this.min)
2431
- value *= 2;
2432
-
2433
-
2434
- // Account for each side of the X axis
2435
- if (value >= 0) {
2436
- value += this.min
2437
-
2438
- if (prop['chart.ylabels.invert']) {
2439
- value -= this.min;
2440
- value = this.max - value;
2441
- }
2442
-
2443
- } else {
2444
-
2445
- value -= this.min;
2446
- if (prop['chart.ylabels.invert']) {
2447
- value += this.min;
2448
- value = this.max + value;
2449
- value *= -1;
2450
- }
2451
- }
2452
-
2453
- } else {
2454
-
2455
- var value = ((this.grapharea - (mouseY - this.gutterTop)) / this.grapharea) * (this.max - this.min)
2456
- value += this.min;
2457
-
2458
- if (prop['chart.ylabels.invert']) {
2459
- value -= this.min;
2460
- value = this.max - value;
2461
- }
2462
- }
2463
-
2464
- return value;
2465
- };
2466
-
2467
-
2468
-
2469
-
2470
-
2471
-
2472
-
2473
-
2474
- /**
2475
- * When you click on the chart, this method can return the X value at that point.
2476
- *
2477
- * @param mixed arg This can either be an event object or the X coordinate
2478
- * @param number If specifying the X coord as the first arg then this should be the Y coord
2479
- */
2480
- this.getXValue = function (arg)
2481
- {
2482
- if (arg.length == 2) {
2483
- var mouseX = arg[0];
2484
- var mouseY = arg[1];
2485
- } else {
2486
- var mouseXY = RG.getMouseXY(arg);
2487
- var mouseX = mouseXY[0];
2488
- var mouseY = mouseXY[1];
2489
- }
2490
- var obj = this;
2491
-
2492
- if ( mouseY < this.gutterTop
2493
- || mouseY > (ca.height - this.gutterBottom)
2494
- || mouseX < this.gutterLeft
2495
- || mouseX > (ca.width - this.gutterRight)
2496
- ) {
2497
- return null;
2498
- }
2499
-
2500
- var width = (ca.width - this.gutterLeft - this.gutterRight);
2501
- var value = ((mouseX - this.gutterLeft) / width) * (prop['chart.xmax'] - prop['chart.xmin'])
2502
- value += prop['chart.xmin'];
2503
-
2504
- return value;
2505
- };
2506
-
2507
-
2508
-
2509
-
2510
-
2511
-
2512
-
2513
-
2514
- /**
2515
- * Each object type has its own Highlight() function which highlights the appropriate shape
2516
- *
2517
- * @param object shape The shape to highlight
2518
- */
2519
- this.highlight =
2520
- this.Highlight = function (shape)
2521
- {
2522
- if (typeof prop['chart.highlight.style'] === 'function') {
2523
- (prop['chart.highlight.style'])(shape);
2524
- } else {
2525
- // Boxplot highlight
2526
- if (shape['height']) {
2527
- RG.Highlight.Rect(this, shape);
2528
-
2529
- // Point highlight
2530
- } else {
2531
- RG.Highlight.Point(this, shape);
2532
- }
2533
- }
2534
- };
2535
-
2536
-
2537
-
2538
-
2539
-
2540
-
2541
-
2542
-
2543
- /**
2544
- * The getObjectByXY() worker method. Don't call this call:
2545
- *
2546
- * RG.ObjectRegistry.getObjectByXY(e)
2547
- *
2548
- * @param object e The event object
2549
- */
2550
- this.getObjectByXY = function (e)
2551
- {
2552
- var mouseXY = RG.getMouseXY(e);
2553
-
2554
- if (
2555
- mouseXY[0] > (this.gutterLeft - 3)
2556
- && mouseXY[0] < (ca.width - this.gutterRight + 3)
2557
- && mouseXY[1] > (this.gutterTop - 3)
2558
- && mouseXY[1] < ((ca.height - this.gutterBottom) + 3)
2559
- ) {
2560
-
2561
- return this;
2562
- }
2563
- };
2564
-
2565
-
2566
-
2567
-
2568
-
2569
-
2570
-
2571
-
2572
- /**
2573
- * This function can be used when the canvas is clicked on (or similar - depending on the event)
2574
- * to retrieve the relevant X coordinate for a particular value.
2575
- *
2576
- * @param int value The value to get the X coordinate for
2577
- */
2578
- this.getXCoord = function (value)
2579
- {
2580
- if (typeof value != 'number' && typeof value != 'string') {
2581
- return null;
2582
- }
2583
-
2584
- // Allow for date strings to be passed
2585
- if (typeof value === 'string') {
2586
- value = RG.parseDate(value);
2587
- }
2588
-
2589
- var xmin = prop['chart.xmin'];
2590
- var xmax = prop['chart.xmax'];
2591
- var x;
2592
-
2593
- if (value < xmin) return null;
2594
- if (value > xmax) return null;
2595
-
2596
- var gutterRight = this.gutterRight;
2597
- var gutterLeft = this.gutterLeft;
2598
-
2599
- if (prop['chart.yaxispos'] == 'right') {
2600
- x = ((value - xmin) / (xmax - xmin)) * (ca.width - gutterLeft - gutterRight);
2601
- x = (ca.width - gutterRight - x);
2602
- } else {
2603
- x = ((value - xmin) / (xmax - xmin)) * (ca.width - gutterLeft - gutterRight);
2604
- x = x + gutterLeft;
2605
- }
2606
-
2607
- return x;
2608
- };
2609
-
2610
-
2611
-
2612
-
2613
-
2614
-
2615
-
2616
-
2617
- /**
2618
- * This function positions a tooltip when it is displayed
2619
- *
2620
- * @param obj object The chart object
2621
- * @param int x The X coordinate specified for the tooltip
2622
- * @param int y The Y coordinate specified for the tooltip
2623
- * @param objec tooltip The tooltips DIV element
2624
- *
2625
- this.positionTooltip = function (obj, x, y, tooltip, idx)
2626
- {
2627
- var shape = RG.Registry.Get('chart.tooltip.shape');
2628
- var dataset = shape['dataset'];
2629
- var index = shape['index'];
2630
- var coordX = obj.coords[dataset][index][0]
2631
- var coordY = obj.coords[dataset][index][1]
2632
- var canvasXY = RG.getCanvasXY(obj.canvas);
2633
- var mouseXY = RG.getMouseXY(window.event);
2634
- var gutterLeft = obj.gutterLeft;
2635
- var gutterTop = obj.gutterTop;
2636
- var width = tooltip.offsetWidth;
2637
- var height = tooltip.offsetHeight;
2638
- tooltip.style.left = 0;
2639
- tooltip.style.top = 0;
2640
-
2641
- // Is the coord a boxplot
2642
- //var isBoxplot = typeof(coordY) == 'object' ? true : false;
2643
-
2644
- // Show any overflow (ie the arrow)
2645
- tooltip.style.overflow = '';
2646
-
2647
-
2648
- // Reposition the tooltip if at the edges:
2649
-
2650
- // LEFT edge //////////////////////////////////////////////////////////////////
2651
- if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
2652
- tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + 'px';
2653
- tooltip.style.top = window.event.pageY - height - 5 + 'px';
2654
-
2655
- // RIGHT edge //////////////////////////////////////////////////////////////////
2656
- } else if ((canvasXY[0] + (coordX[0] || coordX) + (width / 2)) > doc.body.offsetWidth) {
2657
- tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + 'px';
2658
- tooltip.style.top = window.event.pageY - height - 5 + 'px';
2659
-
2660
- // Default positioning - CENTERED //////////////////////////////////////////////////////////////////
2661
- } else {
2662
- tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + 'px';
2663
- tooltip.style.top = window.event.pageY - height - 5 + 'px';
2664
- }
2665
- };*/
2666
-
2667
-
2668
-
2669
-
2670
-
2671
-
2672
-
2673
-
2674
- /**
2675
- * Returns the applicable Y COORDINATE when given a Y value
2676
- *
2677
- * @param int value The value to use
2678
- * @return int The appropriate Y coordinate
2679
- */
2680
- this.getYCoord =
2681
- this.getYCoordFromValue = function (value)
2682
- {
2683
- if (typeof(value) != 'number') {
2684
- return null;
2685
- }
2686
-
2687
- var invert = prop['chart.ylabels.invert'];
2688
- var xaxispos = prop['chart.xaxispos'];
2689
- var graphHeight = ca.height - this.gutterTop - this.gutterBottom;
2690
- var halfGraphHeight = graphHeight / 2;
2691
- var ymax = this.max;
2692
- var ymin = prop['chart.ymin'];
2693
- var coord = 0;
2694
-
2695
- if (value > ymax || (prop['chart.xaxispos'] == 'bottom' && value < ymin) || (prop['chart.xaxispos'] == 'center' && ((value > 0 && value < ymin) || (value < 0 && value > (-1 * ymin))))) {
2696
- return null;
2697
- }
2698
-
2699
- /**
2700
- * This calculates scale values if the X axis is in the center
2701
- */
2702
- if (xaxispos == 'center') {
2703
-
2704
- coord = ((Math.abs(value) - ymin) / (ymax - ymin)) * halfGraphHeight;
2705
-
2706
- if (invert) {
2707
- coord = halfGraphHeight - coord;
2708
- }
2709
-
2710
- if (value < 0) {
2711
- coord += this.gutterTop;
2712
- coord += halfGraphHeight;
2713
- } else {
2714
- coord = halfGraphHeight - coord;
2715
- coord += this.gutterTop;
2716
- }
2717
-
2718
- /**
2719
- * And this calculates scale values when the X axis is at the bottom
2720
- */
2721
- } else {
2722
-
2723
- coord = ((value - ymin) / (ymax - ymin)) * graphHeight;
2724
-
2725
- if (invert) {
2726
- coord = graphHeight - coord;
2727
- }
2728
-
2729
- // Invert the coordinate because the Y scale starts at the top
2730
- coord = graphHeight - coord;
2731
-
2732
- // And add on the top gutter
2733
- coord = this.gutterTop + coord;
2734
- }
2735
-
2736
- return coord;
2737
- };
2738
-
2739
-
2740
-
2741
-
2742
-
2743
-
2744
-
2745
-
2746
- /**
2747
- * A helper class that helps facilitatesbubble charts
2748
- */
2749
- RG.Scatter.Bubble = function (scatter, min, max, width, data)
2750
- {
2751
- this.scatter = scatter;
2752
- this.min = min;
2753
- this.max = max;
2754
- this.width = width;
2755
- this.data = data;
2756
- this.coords = [];
2757
- this.type = 'scatter.bubble'
2758
-
2759
-
2760
-
2761
- /**
2762
- * A setter for the Bubble chart class - it just acts as a "passthru" to the Scatter object
2763
- */
2764
- this.set =
2765
- this.Set = function (name, value)
2766
- {
2767
- this.scatter.set(name, value);
2768
-
2769
- return this;
2770
- };
2771
-
2772
-
2773
-
2774
- /**
2775
- * A getter for the Bubble chart class - it just acts as a "passthru" to the Scatter object
2776
- */
2777
- this.get =
2778
- this.Get = function (name)
2779
- {
2780
- this.scatter.get(name);
2781
- };
2782
-
2783
-
2784
-
2785
-
2786
- /**
2787
- * Tha Bubble chart draw function
2788
- */
2789
- this.draw =
2790
- this.Draw = function ()
2791
- {
2792
- var bubble_min = this.min,
2793
- bubble_max = this.max,
2794
- bubble_data = this.data,
2795
- bubble_max_width = this.width;
2796
-
2797
- // Keep a record of the bubble chart object
2798
- var obj_bubble = this,
2799
- obj_scatter = this.scatter;
2800
-
2801
- // This custom ondraw event listener draws the bubbles
2802
- this.scatter.ondraw = function (obj)
2803
- {
2804
- // Loop through all the points (first dataset)
2805
- for (var i=0; i<obj.coords[0].length; ++i) {
2806
-
2807
- bubble_data[i] = ma.max(bubble_data[i], bubble_min);
2808
- bubble_data[i] = ma.min(bubble_data[i], bubble_max);
2809
-
2810
- var r = ((bubble_data[i] - bubble_min) / (bubble_max - bubble_min) ) * bubble_max_width,
2811
- color = obj_scatter.data[0][i][2] ? obj_scatter.data[0][i][2] : obj_scatter.properties['chart.defaultcolor'];
2812
-
2813
- co.beginPath();
2814
- co.fillStyle = RG.radialGradient(obj,
2815
- obj_scatter.coords[0][i][0] + (r / 2.5),
2816
- obj_scatter.coords[0][i][1] - (r / 2.5),
2817
- 0,
2818
- obj_scatter.coords[0][i][0] + (r / 2.5),
2819
- obj_scatter.coords[0][i][1] - (r / 2.5),
2820
- r,
2821
- prop['chart.colors.bubble.graduated'] ? 'white' : color,
2822
- color
2823
- );
2824
-
2825
- // Draw the bubble
2826
- co.arc(
2827
- obj_scatter.coords[0][i][0],
2828
- obj_scatter.coords[0][i][1],
2829
- r,
2830
- 0,
2831
- RG.TWOPI,
2832
- false
2833
- );
2834
-
2835
- co.fill();
2836
-
2837
- obj_bubble.coords[i] = [
2838
- obj_scatter.coords[0][i][0],
2839
- obj_scatter.coords[0][i][1],
2840
- r,
2841
- co.fillStyle
2842
- ];
2843
- }
2844
- }
2845
-
2846
- this.scatter.Draw();
2847
-
2848
- return this;
2849
- };
2850
- };
2851
-
2852
-
2853
-
2854
-
2855
-
2856
-
2857
-
2858
-
2859
- /**
2860
- * This allows for easy specification of gradients
2861
- */
2862
- this.parseColors = function ()
2863
- {
2864
- // Save the original colors so that they can be restored when the canvas is reset
2865
- if (this.original_colors.length === 0) {
2866
- this.original_colors['data'] = RG.array_clone(this.data);
2867
- this.original_colors['chart.background.vbars'] = RG.array_clone(prop['chart.background.vbars']);
2868
- this.original_colors['chart.background.hbars'] = RG.array_clone(prop['chart.background.hbars']);
2869
- this.original_colors['chart.line.colors'] = RG.array_clone(prop['chart.line.colors']);
2870
- this.original_colors['chart.defaultcolor'] = RG.array_clone(prop['chart.defaultcolor']);
2871
- this.original_colors['chart.crosshairs.color'] = RG.array_clone(prop['chart.crosshairs.color']);
2872
- this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
2873
- this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
2874
- this.original_colors['chart.background.barcolor1'] = RG.array_clone(prop['chart.background.barcolor1']);
2875
- this.original_colors['chart.background.barcolor2'] = RG.array_clone(prop['chart.background.barcolor2']);
2876
- this.original_colors['chart.background.grid.color'] = RG.array_clone(prop['chart.background.grid.color']);
2877
- this.original_colors['chart.background.color'] = RG.array_clone(prop['chart.background.color']);
2878
- this.original_colors['chart.axis.color'] = RG.array_clone(prop['chart.axis.color']);
2879
- }
2880
-
2881
-
2882
-
2883
-
2884
-
2885
- // Colors
2886
- var data = this.data;
2887
- if (data) {
2888
- for (var dataset=0; dataset<data.length; ++dataset) {
2889
- for (var i=0; i<this.data[dataset].length; ++i) {
2890
-
2891
- // Boxplots
2892
- if (this.data[dataset][i] && typeof(this.data[dataset][i][1]) == 'object' && this.data[dataset][i][1]) {
2893
-
2894
- if (typeof(this.data[dataset][i][1][5]) == 'string') this.data[dataset][i][1][5] = this.parseSingleColorForGradient(this.data[dataset][i][1][5]);
2895
- if (typeof(this.data[dataset][i][1][6]) == 'string') this.data[dataset][i][1][6] = this.parseSingleColorForGradient(this.data[dataset][i][1][6]);
2896
- }
2897
-
2898
- if (!RG.isNull(this.data[dataset][i])) {
2899
- this.data[dataset][i][2] = this.parseSingleColorForGradient(this.data[dataset][i][2]);
2900
- }
2901
- }
2902
- }
2903
- }
2904
-
2905
- // Parse HBars
2906
- var hbars = prop['chart.background.hbars'];
2907
- if (hbars) {
2908
- for (i=0; i<hbars.length; ++i) {
2909
- hbars[i][2] = this.parseSingleColorForGradient(hbars[i][2]);
2910
- }
2911
- }
2912
-
2913
- // Parse HBars
2914
- var vbars = prop['chart.background.vbars'];
2915
- if (vbars) {
2916
- for (i=0; i<vbars.length; ++i) {
2917
- vbars[i][2] = this.parseSingleColorForGradient(vbars[i][2]);
2918
- }
2919
- }
2920
-
2921
- // Parse line colors
2922
- var colors = prop['chart.line.colors'];
2923
- if (colors) {
2924
- for (i=0; i<colors.length; ++i) {
2925
- colors[i] = this.parseSingleColorForGradient(colors[i]);
2926
- }
2927
- }
2928
-
2929
- prop['chart.defaultcolor'] = this.parseSingleColorForGradient(prop['chart.defaultcolor']);
2930
- prop['chart.crosshairs.color'] = this.parseSingleColorForGradient(prop['chart.crosshairs.color']);
2931
- prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
2932
- prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
2933
- prop['chart.background.barcolor1'] = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
2934
- prop['chart.background.barcolor2'] = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
2935
- prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
2936
- prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
2937
- prop['chart.axis.color'] = this.parseSingleColorForGradient(prop['chart.axis.color']);
2938
- };
2939
-
2940
-
2941
-
2942
-
2943
-
2944
-
2945
-
2946
-
2947
- /**
2948
- * Use this function to reset the object to the post-constructor state. Eg reset colors if
2949
- * need be etc
2950
- */
2951
- this.reset = function ()
2952
- {
2953
- };
2954
-
2955
-
2956
-
2957
-
2958
-
2959
-
2960
-
2961
-
2962
- /**
2963
- * This parses a single color value for a gradient
2964
- */
2965
- this.parseSingleColorForGradient = function (color)
2966
- {
2967
- if (!color || typeof(color) != 'string') {
2968
- return color;
2969
- }
2970
-
2971
- if (color.match(/^gradient\((.*)\)$/i)) {
2972
-
2973
- var parts = RegExp.$1.split(':');
2974
-
2975
- // Create the gradient
2976
- var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'], 0, prop['chart.gutter.top']);
2977
-
2978
- var diff = 1 / (parts.length - 1);
2979
-
2980
- grad.addColorStop(0, RG.trim(parts[0]));
2981
-
2982
- for (var j=1; j<parts.length; ++j) {
2983
- grad.addColorStop(j * diff, RG.trim(parts[j]));
2984
- }
2985
- }
2986
-
2987
- return grad ? grad : color;
2988
- };
2989
-
2990
-
2991
-
2992
-
2993
-
2994
-
2995
-
2996
-
2997
- /**
2998
- * This function handles highlighting an entire data-series for the interactive
2999
- * key
3000
- *
3001
- * @param int index The index of the data series to be highlighted
3002
- */
3003
- this.interactiveKeyHighlight = function (index)
3004
- {
3005
- if (this.coords && this.coords[index] && this.coords[index].length) {
3006
- this.coords[index].forEach(function (value, idx, arr)
3007
- {
3008
- co.beginPath();
3009
- co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
3010
- co.arc(value[0], value[1], prop['chart.ticksize'] + 3, 0, RG.TWOPI, false);
3011
- co.fill();
3012
- });
3013
- }
3014
- };
3015
-
3016
-
3017
-
3018
-
3019
-
3020
-
3021
-
3022
-
3023
- /**
3024
- * Using a function to add events makes it easier to facilitate method chaining
3025
- *
3026
- * @param string type The type of even to add
3027
- * @param function func
3028
- */
3029
- this.on = function (type, func)
3030
- {
3031
- if (type.substr(0,2) !== 'on') {
3032
- type = 'on' + type;
3033
- }
3034
-
3035
- if (typeof this[type] !== 'function') {
3036
- this[type] = func;
3037
- } else {
3038
- RG.addCustomEventListener(this, type, func);
3039
- }
3040
-
3041
- return this;
3042
- };
3043
-
3044
-
3045
-
3046
-
3047
-
3048
-
3049
-
3050
-
3051
- /**
3052
- * This function runs once only
3053
- * (put at the end of the file (before any effects))
3054
- */
3055
- this.firstDrawFunc = function ()
3056
- {
3057
- };
3058
-
3059
-
3060
-
3061
-
3062
-
3063
-
3064
-
3065
-
3066
- /**
3067
- * Trace2
3068
- *
3069
- * This is a new version of the Trace effect which no longer requires jQuery and is more compatible
3070
- * with other effects (eg Expand). This new effect is considerably simpler and less code.
3071
- *
3072
- * @param object Options for the effect. Currently only "frames" is available.
3073
- * @param int A function that is called when the ffect is complete
3074
- */
3075
- this.trace =
3076
- this.trace2 = function ()
3077
- {
3078
- var obj = this,
3079
- callback = arguments[2],
3080
- opt = arguments[0] || {},
3081
- frames = opt.frames || 30,
3082
- frame = 0,
3083
- callback = arguments[1] || function () {}
3084
-
3085
- obj.Set('animationTrace', true);
3086
- obj.Set('animationTraceClip', 0);
3087
-
3088
- function iterator ()
3089
- {
3090
- RG.clear(obj.canvas);
3091
-
3092
- RG.redrawCanvas(obj.canvas);
3093
-
3094
- if (frame++ < frames) {
3095
- obj.set('animationTraceClip', frame / frames);
3096
- RG.Effects.updateCanvas(iterator);
3097
- } else {
3098
- callback(obj);
3099
- }
3100
- }
3101
-
3102
- iterator();
3103
-
3104
- return this;
3105
- };
3106
-
3107
-
3108
-
3109
-
3110
-
3111
-
3112
-
3113
-
3114
- /**
3115
- * This helps the Gantt reset colors when the reset function is called.
3116
- * It handles going through the data and resetting the colors.
3117
- */
3118
- this.resetColorsToOriginalValues = function ()
3119
- {
3120
- /**
3121
- * Copy the original colors over for single-event-per-line data
3122
- */
3123
- for (var i=0,len=this.original_colors['data'].length; i<len; ++i) {
3124
- for (var j=0,len2=this.original_colors['data'][i].length; j<len2;++j) {
3125
-
3126
- // The color for the point
3127
- this.data[i][j][2] = RG.array_clone(this.original_colors['data'][i][j][2]);
3128
-
3129
- // Handle boxplots
3130
- if (typeof this.data[i][j][1] === 'object') {
3131
- this.data[i][j][1][5] = RG.array_clone(this.original_colors['data'][i][j][1][5]);
3132
- this.data[i][j][1][6] = RG.array_clone(this.original_colors['data'][i][j][1][6]);
3133
- }
3134
- }
3135
- }
3136
- };
3137
-
3138
-
3139
-
3140
-
3141
-
3142
-
3143
-
3144
-
3145
- /**
3146
- * Register the object
3147
- */
3148
- RG.register(this);
3149
-
3150
-
3151
-
3152
-
3153
-
3154
-
3155
-
3156
-
3157
- /**
3158
- * This is the 'end' of the constructor so if the first argument
3159
- * contains configuration data - handle that.
3160
- */
3161
- if (parseConfObjectForOptions) {
3162
- RG.parseObjectStyleConfig(this, conf.options);
3163
- }
3164
- };
2
+ RGraph=window.RGraph||{isRGraph:true};RGraph.Scatter=function(conf)
3
+ {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var parseConfObjectForOptions=true;this.data=new Array(conf.data.length);this.data=RGraph.arrayClone(conf.data);if(typeof conf.data==='object'&&typeof conf.data[0]==='object'&&(typeof conf.data[0][0]==='number'||typeof conf.data[0][0]==='string')){var tmp=RGraph.arrayClone(conf.data);conf.data=new Array();conf.data[0]=RGraph.arrayClone(tmp);this.data=RGraph.arrayClone(conf.data);}}else{var conf={id:conf};conf.data=arguments[1];this.data=[];if(arguments[1][0]&&arguments[1][0][0]&&typeof arguments[1][0][0]=='object'){for(var i=0;i<arguments[1].length;++i){this.data[i]=RGraph.arrayClone(arguments[1][i]);}}else{for(var i=1;i<arguments.length;++i){this.data[i-1]=RGraph.arrayClone(arguments[i]);}}}
4
+ if(!RGraph.isArray(this.data[0][0])){this.data=[this.data];}
5
+ for(var i=0,len=this.data.length;i<len;++i){for(var j=0,len2=this.data[i].length;j<len2;++j){if(typeof this.data[i][j]==='object'&&!RGraph.isNull(this.data[i][j])&&typeof this.data[i][j][0]==='string'){if(this.data[i][j][0].match(/^[.0-9]+$/)){this.data[i][j][0]=parseFloat(this.data[i][j][0]);}else if(this.data[i][j][0]===''){this.data[i][j][0]=0;}}
6
+ if(typeof this.data[i][j]==='object'&&!RGraph.isNull(this.data[i][j])&&typeof this.data[i][j][1]==='string'){if(this.data[i][j][1].match(/[.0-9]+/)){this.data[i][j][1]=parseFloat(this.data[i][j][1]);}else if(this.data[i][j][1]===''){this.data[i][j][1]=0;}}}}
7
+ this.id=conf.id;this.canvas=document.getElementById(this.id);this.canvas.__object__=this;this.context=this.canvas.getContext?this.canvas.getContext('2d'):null;this.max=0;this.coords=[];this.type='scatter';this.isRGraph=true;this.uid=RGraph.CreateUID();this.canvas.uid=this.canvas.uid?this.canvas.uid:RGraph.CreateUID();this.colorsParsed=false;this.coordsText=[];this.original_colors=[];this.firstDraw=true;this.properties={'chart.background.barcolor1':'rgba(0,0,0,0)','chart.background.barcolor2':'rgba(0,0,0,0)','chart.background.grid':true,'chart.background.grid.width':1,'chart.background.grid.color':'#ddd','chart.background.grid.hsize':20,'chart.background.grid.vsize':20,'chart.background.hbars':null,'chart.background.vbars':null,'chart.background.grid.vlines':true,'chart.background.grid.hlines':true,'chart.background.grid.border':true,'chart.background.grid.autofit':true,'chart.background.grid.autofit.align':true,'chart.background.grid.autofit.numhlines':5,'chart.background.grid.autofit.numvlines':20,'chart.background.image':null,'chart.background.image.stretch':true,'chart.background.image.x':null,'chart.background.image.y':null,'chart.background.image.w':null,'chart.background.image.h':null,'chart.background.image.align':null,'chart.background.color':null,'chart.text.size':12,'chart.text.angle':0,'chart.text.color':'black','chart.text.font':'Segoe UI, Arial, Verdana, sans-serif','chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':true,'chart.tooltips':[],'chart.tooltips.effect':'fade','chart.tooltips.event':'onmousemove','chart.tooltips.hotspot':3,'chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.highlight':true,'chart.tooltips.coords.page':false,'chart.units.pre':'','chart.units.post':'','chart.numyticks':10,'chart.tickmarks':'cross','chart.tickmarks.image.halign':'center','chart.tickmarks.image.valign':'center','chart.tickmarks.image.offsetx':0,'chart.tickmarks.image.offsety':0,'chart.ticksize':5,'chart.numxticks':true,'chart.xaxis':true,'chart.gutter.left':25,'chart.gutter.right':25,'chart.gutter.top':25,'chart.gutter.bottom':30,'chart.colors.bubble.graduated':true,'chart.xmin':0,'chart.xmax':0,'chart.ymax':null,'chart.ymin':0,'chart.scale.decimals':0,'chart.scale.point':'.','chart.scale.thousand':',','chart.scale.zerostart':true,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.bold':true,'chart.title.font':null,'chart.title.xaxis':'','chart.title.xaxis.bold':true,'chart.title.xaxis.size':null,'chart.title.xaxis.font':null,'chart.title.xaxis.color':null,'chart.title.yaxis':'','chart.title.yaxis.bold':true,'chart.title.yaxis.size':null,'chart.title.yaxis.font':null,'chart.title.yaxis.color':null,'chart.title.xaxis.pos':null,'chart.title.yaxis.pos':null,'chart.title.yaxis.x':null,'chart.title.yaxis.y':null,'chart.title.xaxis.x':null,'chart.title.xaxis.y':null,'chart.title.x':null,'chart.title.y':null,'chart.title.halign':null,'chart.title.valign':null,'chart.labels':[],'chart.labels.bold':false,'chart.labels.color':null,'chart.labels.ingraph':null,'chart.labels.above':false,'chart.labels.above.size':8,'chart.labels.above.decimals':0,'chart.labels.offsetx':0,'chart.labels.offsety':0,'chart.ylabels.offsetx':0,'chart.ylabels.offsety':0,'chart.ylabels':true,'chart.ylabels.count':5,'chart.ylabels.invert':false,'chart.ylabels.specific':null,'chart.ylabels.inside':false,'chart.contextmenu':null,'chart.defaultcolor':'black','chart.xaxispos':'bottom','chart.yaxispos':'left','chart.crosshairs':false,'chart.crosshairs.color':'#333','chart.crosshairs.linewidth':1,'chart.crosshairs.coords':false,'chart.crosshairs.coords.fixed':true,'chart.crosshairs.coords.fadeout':false,'chart.crosshairs.coords.labels.x':'X','chart.crosshairs.coords.labels.y':'Y','chart.crosshairs.coords.formatter.x':null,'chart.crosshairs.coords.formatter.y':null,'chart.crosshairs.hline':true,'chart.crosshairs.vline':true,'chart.annotatable':false,'chart.annotate.color':'black','chart.line':false,'chart.line.linewidth':1,'chart.line.colors':['green','red'],'chart.line.shadow.color':'rgba(0,0,0,0)','chart.line.shadow.blur':2,'chart.line.shadow.offsetx':3,'chart.line.shadow.offsety':3,'chart.line.stepped':false,'chart.line.visible':true,'chart.noaxes':false,'chart.noyaxis':false,'chart.key':null,'chart.key.background':'white','chart.key.position':'graph','chart.key.halign':'right','chart.key.shadow':false,'chart.key.shadow.color':'#666','chart.key.shadow.blur':3,'chart.key.shadow.offsetx':2,'chart.key.shadow.offsety':2,'chart.key.position.gutter.boxed':false,'chart.key.position.x':null,'chart.key.position.y':null,'chart.key.interactive':false,'chart.key.interactive.highlight.chart.fill':'rgba(255,0,0,0.9)','chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)','chart.key.color.shape':'square','chart.key.rounded':true,'chart.key.linewidth':1,'chart.key.colors':null,'chart.key.text.color':'black','chart.axis.color':'black','chart.zoom.factor':1.5,'chart.zoom.fade.in':true,'chart.zoom.fade.out':true,'chart.zoom.hdir':'right','chart.zoom.vdir':'down','chart.zoom.frames':25,'chart.zoom.delay':16.666,'chart.zoom.shadow':true,'chart.zoom.background':true,'chart.zoom.action':'zoom','chart.boxplot.width':1,'chart.boxplot.capped':true,'chart.resizable':false,'chart.resize.handle.background':null,'chart.xmin':0,'chart.labels.specific.align':'left','chart.xscale':false,'chart.xscale.units.pre':'','chart.xscale.units.post':'','chart.xscale.numlabels':10,'chart.xscale.formatter':null,'chart.xscale.decimals':0,'chart.xscale.thousand':',','chart.xscale.point':'.','chart.noendxtick':false,'chart.noendytick':true,'chart.events.mousemove':null,'chart.events.click':null,'chart.highlight.stroke':'rgba(0,0,0,0)','chart.highlight.fill':'rgba(255,255,255,0.7)','chart.clearto':'rgba(0,0,0,0)','chart.animation.trace':false,'chart.animation.trace.clip':1}
8
+ for(var i=0;i<this.data.length;++i){for(var j=0;j<this.data[i].length;++j){if(RGraph.isNull(this.data[i][j])){this.data[i][j]=[];}
9
+ if(this.data[i][j]&&typeof(this.data[i][j][0])=='string'){this.data[i][j][0]=RGraph.parseDate(this.data[i][j][0]);}}}
10
+ this.data_arr=[];for(var i=0;i<this.data.length;++i){for(var j=0;j<this.data[i].length;++j){this.data_arr.push(this.data[i][j]);}}
11
+ for(var i=0;i<this.data_arr.length;++i){this['$'+i]={}}
12
+ if(!this.canvas){alert('[SCATTER] No canvas support');return;}
13
+ if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
14
+ var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
15
+ if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
16
+ this.set=this.Set=function(name)
17
+ {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
18
+ if(name.substr(0,6)!='chart.'){name='chart.'+name;}
19
+ while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
20
+ if(name==='chart.xticks'){name='chart.numxticks';}
21
+ if(name=='chart.line.colors'){prop['chart.colors']=value;}
22
+ if(name=='chart.tooltip.hotspot'){name='chart.tooltips.hotspot';}
23
+ if(name=='chart.yaxispos'&&value!='left'&&value!='right'){alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '"+value+"' Changing it to left");value='left';}
24
+ if(name=='chart.xaxispos'){if(value!='bottom'&&value!='center'){alert('[SCATTER] ('+this.id+') chart.xaxispos should be center or bottom. Tried to set it to: '+value+' Changing it to center');value='center';}}
25
+ if(name=='chart.noxaxis'){name='chart.xaxis';value=!value;}
26
+ prop[name.toLowerCase()]=value;return this;};this.get=this.Get=function(name)
27
+ {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
28
+ while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
29
+ return prop[name];};this.draw=this.Draw=function()
30
+ {if(typeof prop['chart.background.image']==='string'){RG.DrawBackgroundImage(this);}
31
+ RG.fireCustomEvent(this,'onbeforedraw');if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
32
+ this.coordsText=[];this.gutterLeft=prop['chart.gutter.left'];this.gutterRight=prop['chart.gutter.right'];this.gutterTop=prop['chart.gutter.top'];this.gutterBottom=prop['chart.gutter.bottom'];this.hasTooltips=false;var overHotspot=false;this.coords=[];if(typeof(prop['chart.xmin'])=='string')prop['chart.xmin']=RG.parseDate(prop['chart.xmin']);if(typeof(prop['chart.xmax'])=='string')prop['chart.xmax']=RG.parseDate(prop['chart.xmax']);if(!RGraph.ISOLD){this.Set('chart.tooltips',[]);for(var i=0,len=this.data.length;i<len;i+=1){for(var j=0,len2=this.data[i].length;j<len2;j+=1){if(this.data[i][j]&&this.data[i][j][3]){prop['chart.tooltips'].push(this.data[i][j][3]);this.hasTooltips=true;}else{prop['chart.tooltips'].push(null);}}}}
33
+ this.max=0;if(typeof prop['chart.ymax']==='number'){this.max=prop['chart.ymax'];this.min=prop['chart.ymin']?prop['chart.ymin']:0;this.scale2=RG.getScale2(this,{'max':this.max,'min':this.min,'strict':true,'scale.thousand':prop['chart.scale.thousand'],'scale.point':prop['chart.scale.point'],'scale.decimals':prop['chart.scale.decimals'],'ylabels.count':prop['chart.ylabels.count'],'scale.round':prop['chart.scale.round'],'units.pre':prop['chart.units.pre'],'units.post':prop['chart.units.post']});this.max=this.scale2.max;this.min=this.scale2.min;var decimals=prop['chart.scale.decimals'];}else{var i=0;var j=0;for(i=0,len=this.data.length;i<len;i+=1){for(j=0,len2=this.data[i].length;j<len2;j+=1){if(!RG.isNull(this.data[i][j])&&this.data[i][j][1]!=null){this.max=Math.max(this.max,typeof(this.data[i][j][1])=='object'?RG.array_max(this.data[i][j][1]):Math.abs(this.data[i][j][1]));}}}
34
+ this.min=prop['chart.ymin']?prop['chart.ymin']:0;this.scale2=RG.getScale2(this,{'max':this.max,'min':this.min,'scale.thousand':prop['chart.scale.thousand'],'scale.point':prop['chart.scale.point'],'scale.decimals':prop['chart.scale.decimals'],'ylabels.count':prop['chart.ylabels.count'],'scale.round':prop['chart.scale.round'],'units.pre':prop['chart.units.pre'],'units.post':prop['chart.units.post']});this.max=this.scale2.max;this.min=this.scale2.min;}
35
+ this.grapharea=ca.height-this.gutterTop-this.gutterBottom;RG.background.Draw(this);if(prop['chart.background.hbars']&&prop['chart.background.hbars'].length){RG.DrawBars(this);}
36
+ if(prop['chart.background.vbars']&&prop['chart.background.vbars'].length){this.DrawVBars();}
37
+ if(!prop['chart.noaxes']){this.DrawAxes();}
38
+ this.DrawLabels();if(prop['chart.animation.trace']){co.save();co.beginPath();co.rect(0,0,ca.width*prop['chart.animation.trace.clip'],ca.height);co.clip();}
39
+ for(i=0;i<this.data.length;++i){this.DrawMarks(i);co.shadowColor=prop['chart.line.shadow.color'];co.shadowOffsetX=prop['chart.line.shadow.offsetx'];co.shadowOffsetY=prop['chart.line.shadow.offsety'];co.shadowBlur=prop['chart.line.shadow.blur'];this.DrawLine(i);RG.NoShadow(this);}
40
+ if(prop['chart.line']){for(var i=0,len=this.data.length;i<len;i+=1){this.DrawMarks(i);}}
41
+ if(prop['chart.animation.trace']){co.restore();}
42
+ if(prop['chart.contextmenu']){RG.ShowContext(this);}
43
+ if(prop['chart.key']&&prop['chart.key'].length){RG.DrawKey(this,prop['chart.key'],prop['chart.line.colors']);}
44
+ if(prop['chart.labels.above']){this.DrawAboveLabels();}
45
+ this.DrawInGraphLabels(this);if(prop['chart.resizable']){RG.AllowResizing(this);}
46
+ RG.InstallEventListeners(this);if(this.firstDraw){this.firstDraw=false;RG.fireCustomEvent(this,'onfirstdraw');this.firstDrawFunc();}
47
+ RG.FireCustomEvent(this,'ondraw');return this;}
48
+ this.drawAxes=this.DrawAxes=function()
49
+ {var graphHeight=ca.height-this.gutterTop-this.gutterBottom;co.beginPath();co.strokeStyle=prop['chart.axis.color'];co.lineWidth=(prop['chart.axis.linewidth']||1)+0.001;if(prop['chart.noyaxis']==false){if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,this.gutterTop);co.lineTo(this.gutterLeft,ca.height-this.gutterBottom);}else{co.moveTo(ca.width-this.gutterRight,this.gutterTop);co.lineTo(ca.width-this.gutterRight,ca.height-this.gutterBottom);}}
50
+ if(prop['chart.xaxis']){if(prop['chart.xaxispos']=='center'){co.moveTo(this.gutterLeft,ma.round(this.gutterTop+((ca.height-this.gutterTop-this.gutterBottom)/2)));co.lineTo(ca.width-this.gutterRight,ma.round(this.gutterTop+((ca.height-this.gutterTop-this.gutterBottom)/2)));}else{var y=this.getYCoord(this.scale2.min>0?this.scale2.min:0);co.moveTo(this.gutterLeft,y);co.lineTo(ca.width-this.gutterRight,y);}}
51
+ if(prop['chart.noyaxis']===false){var numyticks=prop['chart.numyticks'];for(i=0;i<numyticks;++i){var y=((ca.height-this.gutterTop-this.gutterBottom)/numyticks)*i;y=y+this.gutterTop;if(prop['chart.xaxispos']=='center'&&i==(numyticks/2)){continue;}
52
+ if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,ma.round(y));co.lineTo(this.gutterLeft-3,ma.round(y));}else{co.moveTo(ca.width-this.gutterRight+3,Math.round(y));co.lineTo(ca.width-this.gutterRight,Math.round(y));}}
53
+ if(prop['chart.numyticks']>0){if(prop['chart.xaxispos']=='center'&&prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,ma.round(ca.height-this.gutterBottom));co.lineTo(this.gutterLeft-3,ma.round(ca.height-this.gutterBottom));}else if(prop['chart.xaxispos']=='center'){co.moveTo(ca.width-this.gutterRight+3,ma.round(ca.height-this.gutterBottom));co.lineTo(ca.width-this.gutterRight,ma.round(ca.height-this.gutterBottom));}}
54
+ if(prop['chart.xaxis']===false&&prop['chart.yaxispos']==='left'){co.moveTo(this.gutterLeft,ma.round(ca.height-this.gutterBottom));co.lineTo(this.gutterLeft-3,ma.round(ca.height-this.gutterBottom));}else if(prop['chart.xaxis']===false&&prop['chart.yaxispos']==='right'){co.moveTo(ca.width-this.gutterRight,ma.round(ca.height-this.gutterBottom));co.lineTo(ca.width-this.gutterRight+3,ma.round(ca.height-this.gutterBottom));}
55
+ if(prop['chart.xaxispos']==='bottom'&&prop['chart.numyticks']>0){if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,ma.round(this.getYCoord(prop['chart.ymin'])));co.lineTo(this.gutterLeft-3,ma.round(this.getYCoord(prop['chart.ymin'])));}else{co.moveTo(ca.width-this.gutterRight+3,ma.round(this.getYCoord(prop['chart.ymin'])));co.lineTo(ca.width-this.gutterRight,ma.round(this.getYCoord(prop['chart.ymin'])));}}}
56
+ if(prop['chart.numxticks']>0&&prop['chart.xaxis']){var x=0,y=this.getYCoord(prop['chart.ylabels.invert']?this.scale2.max:(this.scale2.max>0&&this.scale2.min>0?this.scale2.min:0))-3,size=3;if(prop['chart.ymin']===0&&prop['chart.xaxispos']==='bottom'){y+=3;}
57
+ if(this.scale2.max>0&&this.scale2.min>0){y+=3;}
58
+ this.xTickGap=(prop['chart.labels']&&prop['chart.labels'].length)?((ca.width-this.gutterLeft-this.gutterRight)/prop['chart.labels'].length):(ca.width-this.gutterLeft-this.gutterRight)/10;if(typeof(prop['chart.numxticks'])=='number'){this.xTickGap=(ca.width-this.gutterLeft-this.gutterRight)/prop['chart.numxticks'];}
59
+ for(x=(this.gutterLeft+(prop['chart.yaxispos']=='left'&&prop['chart.noyaxis']==false?this.xTickGap:0));x<=(ca.width-this.gutterRight-(prop['chart.yaxispos']=='left'||prop['chart.noyaxis']==true?-1:1));x+=this.xTickGap){if(prop['chart.yaxispos']=='left'&&prop['chart.noendxtick']==true&&x==(ca.width-this.gutterRight)){continue;}else if(prop['chart.yaxispos']=='right'&&prop['chart.noendxtick']==true&&x==this.gutterLeft){continue;}
60
+ co.moveTo(ma.round(x),y);co.lineTo(ma.round(x),y+(prop['chart.xaxispos']==='center'||prop['chart.ymin']<0?size*2:size));}}
61
+ co.stroke();co.lineWidth=1;};this.drawLabels=this.DrawLabels=function()
62
+ {co.fillStyle=prop['chart.text.color'];var font=prop['chart.text.font'],xMin=prop['chart.xmin'],xMax=prop['chart.xmax'],yMax=this.scale2.max,yMin=prop['chart.ymin']?prop['chart.ymin']:0,text_size=prop['chart.text.size'],units_pre=prop['chart.units.pre'],units_post=prop['chart.units.post'],numYLabels=prop['chart.ylabels.count'],invert=prop['chart.ylabels.invert'],inside=prop['chart.ylabels.inside'],context=co,canvas=ca,boxed=false,offsetx=prop['chart.ylabels.offsetx'],offsety=prop['chart.ylabels.offsety']
63
+ this.halfTextHeight=text_size/2;this.halfGraphHeight=(ca.height-this.gutterTop-this.gutterBottom)/2;if(prop['chart.ylabels']){var xPos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var align=prop['chart.yaxispos']=='right'?'left':'right';if(inside){if(prop['chart.yaxispos']=='left'){xPos=prop['chart.gutter.left']+5;align='left';boxed=true;}else{xPos=ca.width-prop['chart.gutter.right']-5;align='right';boxed=true;}}
64
+ if(prop['chart.xaxispos']=='center'){if(typeof(prop['chart.ylabels.specific'])=='object'&&prop['chart.ylabels.specific']!=null&&prop['chart.ylabels.specific'].length){var labels=prop['chart.ylabels.specific'];if(prop['chart.ymin']>0){labels=[];for(var i=0;i<(prop['chart.ylabels.specific'].length-1);++i){labels.push(prop['chart.ylabels.specific'][i]);}}
65
+ for(var i=0;i<labels.length;++i){var y=this.gutterTop+(i*(this.grapharea/(labels.length*2)));RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':y+offsety,'text':labels[i],'valign':'center','halign':align,'bounding':boxed,'tag':'labels.specific'});}
66
+ var reversed_labels=RG.array_reverse(labels);for(var i=0;i<reversed_labels.length;++i){var y=this.gutterTop+(this.grapharea/2)+((i+1)*(this.grapharea/(labels.length*2)));RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':y+offsety,'text':reversed_labels[i],'valign':'center','halign':align,'bounding':boxed,'tag':'labels.specific'});}
67
+ if(prop['chart.ymin']!=0){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':(this.grapharea/2)+this.gutterTop+offsety,'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length-1],'valign':'center','halign':align,'bounding':boxed,'tag':'labels.specific'});}}
68
+ if(!prop['chart.ylabels.specific']&&typeof numYLabels=='number'){for(var i=0,len=this.scale2.labels.length;i<len;i+=1){if(!invert){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+this.halfGraphHeight-(((i+1)/numYLabels)*this.halfGraphHeight)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':this.scale2.labels[i],'tag':'scale'});}else{RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+this.halfGraphHeight-((i/numYLabels)*this.halfGraphHeight)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':this.scale2.labels[this.scale2.labels.length-(i+1)],'tag':'scale'});}}
69
+ for(var i=0,len=this.scale2.labels.length;i<len;i+=1){if(!invert){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+this.halfGraphHeight+this.halfGraphHeight-((i/numYLabels)*this.halfGraphHeight)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':'-'+this.scale2.labels[len-(i+1)],'tag':'scale'});}else{if(i==(len-1)&&invert){continue;}
70
+ RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+this.halfGraphHeight+this.halfGraphHeight-(((i+1)/numYLabels)*this.halfGraphHeight)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':'-'+this.scale2.labels[i],'tag':'scale'});}}
71
+ if(!invert&&(yMin>0||prop['chart.scale.zerostart'])){RG.text2(this,{font:font,size:text_size,x:xPos+offsetx,y:this.gutterTop+this.halfGraphHeight+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat(this,yMin.toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),tag:'scale'});}
72
+ if(invert){RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.number_format(this,yMin.toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+(this.halfGraphHeight*2)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':'-'+RG.numberFormat(this,yMin.toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}}}else{var xPos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var align=prop['chart.yaxispos']=='right'?'left':'right';if(inside){if(prop['chart.yaxispos']=='left'){xPos=prop['chart.gutter.left']+5;align='left';boxed=true;}else{xPos=ca.width-this.gutterRight-5;align='right';boxed=true;}}
73
+ if(typeof prop['chart.ylabels.specific']=='object'&&prop['chart.ylabels.specific']){var labels=prop['chart.ylabels.specific'];if(prop['chart.ymin']>9999){labels=[];for(var i=0;i<(prop['chart.ylabels.specific'].length-1);++i){labels.push(prop['chart.ylabels.specific'][i]);}}
74
+ for(var i=0,len=labels.length;i<len;i+=1){var y=this.gutterTop+(i*(this.grapharea/(len-1)));RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':y+offsety,'text':labels[i],'halign':align,'valign':'center','bounding':boxed,'tag':'scale'});}}else{if(typeof(numYLabels)=='number'){if(invert){for(var i=0;i<numYLabels;++i){var interval=(ca.height-this.gutterTop-this.gutterBottom)/numYLabels;RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+((i+1)*interval)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':this.scale2.labels[i],'tag':'scale'});}
75
+ if(!prop['chart.xaxis']&&!prop['chart.ymin']){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.numberFormat(this,(this.min).toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}}else{for(var i=0,len=this.scale2.labels.length;i<len;i+=1){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+this.grapharea-(((i+1)/this.scale2.labels.length)*this.grapharea)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':this.scale2.labels[i],'tag':'scale'});}
76
+ if(!prop['chart.xaxis']&&prop['chart.ymin']==0){RG.text2(this,{font:font,size:text_size,x:xPos+offsetx,y:ca.height-this.gutterBottom+offsety,valign:'center',halign:align,boundin:boxed,boundingFill:'white',text:RG.numberFormat(this,(0).toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),tag:'scale'});}}}
77
+ if((prop['chart.ymin']||prop['chart.scale.zerostart'])&&!invert){RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':ca.height-this.gutterBottom+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.numberFormat(this,prop['chart.ymin'].toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}else if(invert){RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.numberFormat(this,prop['chart.ymin'].toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}}}}
78
+ if(prop['chart.xscale']){var numXLabels=prop['chart.xscale.numlabels'],y=ca.height-this.gutterBottom+5+(text_size/2),units_pre_x=prop['chart.xscale.units.pre'],units_post_x=prop['chart.xscale.units.post'],decimals=prop['chart.xscale.decimals'],point=prop['chart.xscale.point'],thousand=prop['chart.xscale.thousand'],color=prop['chart.labels.color'],bold=prop['chart.labels.bold'],offsetx=prop['chart.labels.offsetx'],offsety=prop['chart.labels.offsety'],angle=prop['chart.text.angle'];if(angle>0){angle*=-1;valign='center';halign='right';y+=5;}else{valign='center';halign='center';}
79
+ if(!prop['chart.xmax']){var xmax=0;var xmin=prop['chart.xmin'];for(var ds=0,len=this.data.length;ds<len;ds+=1){for(var point=0,len2=this.data[ds].length;point<len2;point+=1){xmax=Math.max(xmax,this.data[ds][point][0]);}}}else{xmax=prop['chart.xmax'];xmin=prop['chart.xmin']}
80
+ this.xscale2=RG.getScale2(this,{'max':xmax,'min':xmin,'scale.decimals':decimals,'scale.point':point,'scale.thousand':thousand,'units.pre':units_pre_x,'units.post':units_post_x,'ylabels.count':numXLabels,'strict':true});this.Set('chart.xmax',this.xscale2.max);var interval=(ca.width-this.gutterLeft-this.gutterRight)/this.xscale2.labels.length;for(var i=0,len=this.xscale2.labels.length;i<len;i+=1){var num=((prop['chart.xmax']-prop['chart.xmin'])*((i+1)/numXLabels))+(xmin||0),x=this.gutterLeft+((i+1)*interval),text=typeof prop['chart.xscale.formatter']==='function'?String(prop['chart.xscale.formatter'](this,num)):this.xscale2.labels[i];RG.text2(this,{color:color,font:font,size:text_size,bold:bold,x:x+offsetx,y:y+offsety,valign:valign,halign:halign,text:text,angle:angle,tag:'xscale'});}
81
+ var text=typeof prop['chart.xscale.formatter']==='function'?String(prop['chart.xscale.formatter'](this,prop['chart.xmin'])):String(prop['chart.xmin']);RG.text2(this,{color:color,font:font,size:text_size,bold:bold,x:this.gutterLeft+offsetx,y:y+offsety,valign:'center',halign:'center',text:text,tag:'xscale',angle:angle});}else{var graphArea=ca.width-this.gutterLeft-this.gutterRight;var xInterval=graphArea/prop['chart.labels'].length;var xPos=this.gutterLeft;var yPos=(ca.height-this.gutterBottom)+3;var labels=prop['chart.labels'];var color=prop['chart.labels.color'];var bold=prop['chart.labels.bold'];var offsetx=prop['chart.labels.offsetx'];var offsety=prop['chart.labels.offsety'];var angle=0;var valign='top';var halign='center';if(prop['chart.text.angle']>0){angle=-1*prop['chart.text.angle'];valign='center';halign='right';yPos+=10;}
82
+ for(i=0;i<labels.length;++i){if(typeof(labels[i])=='object'){if(prop['chart.labels.specific.align']=='center'){var rightEdge=0;if(labels[i+1]&&labels[i+1][1]){rightEdge=labels[i+1][1];}else{rightEdge=prop['chart.xmax'];}
83
+ var offset=(this.getXCoord(rightEdge)-this.getXCoord(labels[i][1]))/2;}else{var offset=5;}
84
+ RG.text2(this,{'color':color,'font':font,'size':prop['chart.text.size'],'bold':bold,'x':this.getXCoord(labels[i][1])+offset+offsetx,'y':yPos+offsety,'valign':valign,'halign':angle!=0?'right':(prop['chart.labels.specific.align']=='center'?'center':'left'),'text':String(labels[i][0]),'angle':angle,'marker':false,'tag':'labels.specific'});co.beginPath();co.strokeStyle='#bbb';co.moveTo(ma.round(this.gutterLeft+(graphArea*((labels[i][1]-xMin)/(prop['chart.xmax']-xMin)))),ca.height-this.gutterBottom);co.lineTo(ma.round(this.gutterLeft+(graphArea*((labels[i][1]-xMin)/(prop['chart.xmax']-xMin)))),ca.height-this.gutterBottom+20);co.stroke();}else{RG.text2(this,{'color':color,'font':font,'size':prop['chart.text.size'],'bold':bold,'x':xPos+(xInterval/2)+offsetx,'y':yPos+offsety,'valign':valign,'halign':halign,'text':String(labels[i]),'angle':angle,'tag':'labels'});}
85
+ xPos+=xInterval;}
86
+ if(typeof(labels[0])=='object'){co.beginPath();co.strokeStyle='#bbb';co.moveTo(this.gutterLeft+graphArea,ca.height-this.gutterBottom);co.lineTo(this.gutterLeft+graphArea,ca.height-this.gutterBottom+20);co.stroke();}}};this.drawMarks=this.DrawMarks=function(i)
87
+ {this.coords[i]=[];var xmax=prop['chart.xmax'];var default_color=prop['chart.defaultcolor'];for(var j=0,len=this.data[i].length;j<len;j+=1){var data_points=this.data[i];if(RG.isNull(data_points[j])){continue;}
88
+ var xCoord=data_points[j][0];var yCoord=data_points[j][1];var color=data_points[j][2]?data_points[j][2]:default_color;var tooltip=(data_points[j]&&data_points[j][3])?data_points[j][3]:null;this.DrawMark(i,xCoord,yCoord,xmax,this.scale2.max,color,tooltip,this.coords[i],data_points,j);}};this.drawMark=this.DrawMark=function(data_set_index,x,y,xMax,yMax,color,tooltip,coords,data,data_index)
89
+ {var tickmarks=prop['chart.tickmarks'],tickSize=prop['chart.ticksize'],xMin=prop['chart.xmin'],x=((x-xMin)/(xMax-xMin))*(ca.width-this.gutterLeft-this.gutterRight),originalX=x,originalY=y;if(tickmarks&&typeof(tickmarks)=='object'){tickmarks=tickmarks[data_set_index];}
90
+ if(typeof(tickSize)=='object'){var tickSize=tickSize[data_set_index];var halfTickSize=tickSize/2;}else{var halfTickSize=tickSize/2;}
91
+ if(y&&typeof y==='object'&&typeof y[0]==='number'&&typeof y[1]==='number'&&typeof y[2]==='number'&&typeof y[3]==='number'&&typeof y[4]==='number'){this.Set('chart.boxplot',true);var y0=this.getYCoord(y[0]),y1=this.getYCoord(y[1]),y2=this.getYCoord(y[2]),y3=this.getYCoord(y[3]),y4=this.getYCoord(y[4]),col1=y[5],col2=y[6],boxWidth=typeof y[7]=='number'?y[7]:prop['chart.boxplot.width'];}else{var yCoord=this.getYCoord(y);}
92
+ x+=this.gutterLeft;co.beginPath();co.strokeStyle=color;if(prop['chart.boxplot']){boxWidth=(boxWidth/prop['chart.xmax'])*(ca.width-this.gutterLeft-this.gutterRight);var halfBoxWidth=boxWidth/2;if(prop['chart.line.visible']){co.beginPath();if(typeof y[8]==='string'){co.strokeStyle=y[8];}
93
+ co.strokeRect(x-halfBoxWidth,y1,boxWidth,y3-y1);if(col1){co.fillStyle=col1;co.fillRect(x-halfBoxWidth,y1,boxWidth,y2-y1);}
94
+ if(col2){co.fillStyle=col2;co.fillRect(x-halfBoxWidth,y2,boxWidth,y3-y2);}
95
+ co.stroke();co.beginPath();if(prop['chart.boxplot.capped']){co.moveTo(x-halfBoxWidth,ma.round(y0));co.lineTo(x+halfBoxWidth,ma.round(y0));}
96
+ co.moveTo(ma.round(x),y0);co.lineTo(ma.round(x),y1);if(prop['chart.boxplot.capped']){co.moveTo(x-halfBoxWidth,ma.round(y4));co.lineTo(x+halfBoxWidth,ma.round(y4));}
97
+ co.moveTo(ma.round(x),y4);co.lineTo(ma.round(x),y3);co.stroke();}}
98
+ if(prop['chart.line.visible']&&typeof(y)=='number'&&!y0&&!y1&&!y2&&!y3&&!y4){if(tickmarks=='circle'){co.arc(x,yCoord,halfTickSize,0,6.28,0);co.fillStyle=color;co.fill();}else if(tickmarks=='plus'){co.moveTo(x,yCoord-halfTickSize);co.lineTo(x,yCoord+halfTickSize);co.moveTo(x-halfTickSize,yCoord);co.lineTo(x+halfTickSize,yCoord);co.stroke();}else if(tickmarks=='square'){co.strokeStyle=color;co.fillStyle=color;co.fillRect(x-halfTickSize,yCoord-halfTickSize,tickSize,tickSize);}else if(tickmarks=='cross'){co.moveTo(x-halfTickSize,yCoord-halfTickSize);co.lineTo(x+halfTickSize,yCoord+halfTickSize);co.moveTo(x+halfTickSize,yCoord-halfTickSize);co.lineTo(x-halfTickSize,yCoord+halfTickSize);co.stroke();}else if(tickmarks=='diamond'){co.fillStyle=co.strokeStyle;co.moveTo(x,yCoord-halfTickSize);co.lineTo(x+halfTickSize,yCoord);co.lineTo(x,yCoord+halfTickSize);co.lineTo(x-halfTickSize,yCoord);co.lineTo(x,yCoord-halfTickSize);co.fill();co.stroke();}else if(typeof(tickmarks)=='function'){var graphWidth=ca.width-this.gutterLeft-this.gutterRight,graphheight=ca.height-this.gutterTop-this.gutterBottom,xVal=((x-this.gutterLeft)/graphWidth)*xMax,yVal=((graphheight-(yCoord-this.gutterTop))/graphheight)*yMax;tickmarks(this,data,x,yCoord,xVal,yVal,xMax,yMax,color,data_set_index,data_index)}else if(typeof tickmarks==='string'&&(tickmarks.substr(0,6)==='image:'||tickmarks.substr(0,5)==='data:'||tickmarks.substr(0,1)==='/'||tickmarks.substr(0,3)==='../'||tickmarks.substr(0,7)==='images/')){var img=new Image();if(tickmarks.substr(0,6)==='image:'){img.src=tickmarks.substr(6);}else{img.src=tickmarks;}
99
+ img.onload=function()
100
+ {if(prop['chart.tickmarks.image.halign']==='center')x-=(this.width/2);if(prop['chart.tickmarks.image.halign']==='right')x-=this.width;if(prop['chart.tickmarks.image.valign']==='center')yCoord-=(this.height/2);if(prop['chart.tickmarks.image.valign']==='bottom')yCoord-=this.height;x+=prop['chart.tickmarks.image.offsetx'];yCoord+=prop['chart.tickmarks.image.offsety'];co.drawImage(this,x,yCoord);}}else if(tickmarks===null){}else{alert('[SCATTER] ('+this.id+') Unknown tickmark style: '+tickmarks);}}
101
+ if(prop['chart.boxplot']&&typeof y0==='number'&&typeof y1==='number'&&typeof y2==='number'&&typeof y3==='number'&&typeof y4==='number'){x=[x-halfBoxWidth,x+halfBoxWidth];yCoord=[y0,y1,y2,y3,y4];}
102
+ coords.push([x,yCoord,tooltip]);};this.drawLine=this.DrawLine=function(i)
103
+ {if(typeof(prop['chart.line.visible'])=='boolean'&&prop['chart.line.visible']==false){return;}
104
+ if(prop['chart.line']&&this.coords[i].length>=2){if(prop['chart.line.dash']&&typeof co.setLineDash==='function'){co.setLineDash(prop['chart.line.dash']);}
105
+ co.lineCap='round';co.lineJoin='round';co.lineWidth=this.getLineWidth(i);co.strokeStyle=prop['chart.line.colors'][i];co.beginPath();var prevY=null;var currY=null;for(var j=0,len=this.coords[i].length;j<len;j+=1){var xPos=this.coords[i][j][0];var yPos=this.coords[i][j][1];if(j>0)prevY=this.coords[i][j-1][1];currY=yPos;if(j==0||RG.is_null(prevY)||RG.is_null(currY)){co.moveTo(xPos,yPos);}else{var stepped=prop['chart.line.stepped'];if((typeof stepped=='boolean'&&stepped)||(typeof stepped=='object'&&stepped[i])){co.lineTo(this.coords[i][j][0],this.coords[i][j-1][1]);}
106
+ co.lineTo(xPos,yPos);}}
107
+ co.stroke();if(prop['chart.line.dash']&&typeof co.setLineDash==='function'){co.setLineDash([1,0]);}}
108
+ co.lineWidth=1;};this.getLineWidth=this.GetLineWidth=function(i)
109
+ {var linewidth=prop['chart.line.linewidth'];if(typeof linewidth=='number'){return linewidth;}else if(typeof linewidth=='object'){if(linewidth[i]){return linewidth[i];}else{return linewidth[0];}
110
+ alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');}};this.drawVBars=this.DrawVBars=function()
111
+ {var vbars=prop['chart.background.vbars'];var graphWidth=ca.width-this.gutterLeft-this.gutterRight;if(vbars){var xmax=prop['chart.xmax'];var xmin=prop['chart.xmin'];for(var i=0,len=vbars.length;i<len;i+=1){var key=i;var value=vbars[key];if(typeof value[0]=='string')value[0]=RG.parseDate(value[0]);if(typeof value[1]=='string')value[1]=RG.parseDate(value[1])-value[0];var x=(((value[0]-xmin)/(xmax-xmin))*graphWidth)+this.gutterLeft;var width=(value[1]/(xmax-xmin))*graphWidth;co.fillStyle=value[2];co.fillRect(x,this.gutterTop,width,(ca.height-this.gutterTop-this.gutterBottom));}}};this.drawInGraphLabels=this.DrawInGraphLabels=function(obj)
112
+ {var labels=obj.Get('chart.labels.ingraph');var labels_processed=[];if(!labels){return;}
113
+ var fgcolor='black';var bgcolor='white';var direction=1;for(var i=0,len=labels.length;i<len;i+=1){if(typeof(labels[i])=='number'){for(var j=0;j<labels[i];++j){labels_processed.push(null);}}else if(typeof(labels[i])=='string'||typeof(labels[i])=='object'){labels_processed.push(labels[i]);}else{labels_processed.push('');}}
114
+ RG.NoShadow(obj);if(labels_processed&&labels_processed.length>0){var i=0;for(var set=0;set<obj.coords.length;++set){for(var point=0;point<obj.coords[set].length;++point){if(labels_processed[i]){var x=obj.coords[set][point][0];var y=obj.coords[set][point][1];var length=typeof(labels_processed[i][4])=='number'?labels_processed[i][4]:25;var text_x=x;var text_y=y-5-length;co.moveTo(x,y-5);co.lineTo(x,y-5-length);co.stroke();co.beginPath();co.moveTo(x,y-5);co.lineTo(x-3,y-10);co.lineTo(x+3,y-10);co.closePath();co.beginPath();co.fillStyle=(typeof(labels_processed[i])=='object'&&typeof(labels_processed[i][1])=='string')?labels_processed[i][1]:'black';RG.text2(this,{'font':obj.Get('chart.text.font'),'size':obj.Get('chart.text.size'),'x':text_x,'y':text_y,'text':(typeof(labels_processed[i])=='object'&&typeof(labels_processed[i][0])=='string')?labels_processed[i][0]:labels_processed[i],'valign':'bottom','halign':'center','bounding':true,'bounding.fill':(typeof(labels_processed[i])=='object'&&typeof(labels_processed[i][2])=='string')?labels_processed[i][2]:'white','tag':'labels.ingraph'});co.fill();}
115
+ i++;}}}};this.getShape=this.getPoint=function(e)
116
+ {var mouseXY=RG.getMouseXY(e);var mouseX=mouseXY[0];var mouseY=mouseXY[1];var overHotspot=false;var offset=prop['chart.tooltips.hotspot'];for(var set=0,len=this.coords.length;set<len;++set){for(var i=0,len2=this.coords[set].length;i<len2;++i){var x=this.coords[set][i][0];var y=this.coords[set][i][1];var tooltip=this.data[set][i][3];if(typeof(y)=='number'){if(mouseX<=(x+offset)&&mouseX>=(x-offset)&&mouseY<=(y+offset)&&mouseY>=(y-offset)){var tooltip=RG.parseTooltipText(this.data[set][i][3],0);var index_adjusted=i;for(var ds=(set-1);ds>=0;--ds){index_adjusted+=this.data[ds].length;}
117
+ return{0:this,1:x,2:y,3:set,4:i,5:this.data[set][i][3],'object':this,'x':x,'y':y,'dataset':set,'index':i,'tooltip':tooltip,'index_adjusted':index_adjusted};}}else if(RG.is_null(y)){}else{var mark=this.data[set][i];var width=prop['chart.boxplot.width'];if(typeof(mark[1][7])=='number'){width=mark[1][7];}
118
+ if(typeof(x)=='object'&&mouseX>x[0]&&mouseX<x[1]&&mouseY<y[1]&&mouseY>y[3]){var tooltip=RG.parseTooltipText(this.data[set][i][3],0);return{0:this,1:x[0],2:x[1]-x[0],3:y[1],4:y[3]-y[1],5:set,6:i,7:this.data[set][i][3],'object':this,'x':x[0],'y':y[1],'width':x[1]-x[0],'height':y[3]-y[1],'dataset':set,'index':i,'tooltip':tooltip};}}}}};this.drawAboveLabels=this.DrawAboveLabels=function()
119
+ {var size=prop['chart.labels.above.size'];var font=prop['chart.text.font'];var units_pre=prop['chart.units.pre'];var units_post=prop['chart.units.post'];for(var set=0,len=this.coords.length;set<len;++set){for(var point=0,len2=this.coords[set].length;point<len2;++point){var x_val=this.data[set][point][0];var y_val=this.data[set][point][1];if(!RG.is_null(y_val)){if(RG.is_array(y_val)){var max=0;for(var i=0;i<y_val;++i){max=Math.max(max,y_val[i]);}
120
+ y_val=max;}
121
+ var x_pos=this.coords[set][point][0];var y_pos=this.coords[set][point][1];RG.Text2(this,{'font':font,'size':size,'x':x_pos,'y':y_pos-5-size,'text':x_val.toFixed(prop['chart.labels.above.decimals'])+', '+y_val.toFixed(prop['chart.labels.above.decimals']),'valign':'center','halign':'center','bounding':true,'boundingFill':'rgba(255, 255, 255, 0.7)','tag':'labels.above'});}}}};this.getYValue=this.getValue=function(arg)
122
+ {if(arg.length==2){var mouseX=arg[0];var mouseY=arg[1];}else{var mouseCoords=RG.getMouseXY(arg);var mouseX=mouseCoords[0];var mouseY=mouseCoords[1];}
123
+ var obj=this;if(mouseY<this.gutterTop||mouseY>(ca.height-this.gutterBottom)||mouseX<this.gutterLeft||mouseX>(ca.width-this.gutterRight)){return null;}
124
+ if(prop['chart.xaxispos']=='center'){var value=(((this.grapharea/2)-(mouseY-this.gutterTop))/this.grapharea)*(this.max-this.min)
125
+ value*=2;if(value>=0){value+=this.min
126
+ if(prop['chart.ylabels.invert']){value-=this.min;value=this.max-value;}}else{value-=this.min;if(prop['chart.ylabels.invert']){value+=this.min;value=this.max+value;value*=-1;}}}else{var value=((this.grapharea-(mouseY-this.gutterTop))/this.grapharea)*(this.max-this.min)
127
+ value+=this.min;if(prop['chart.ylabels.invert']){value-=this.min;value=this.max-value;}}
128
+ return value;};this.getXValue=function(arg)
129
+ {if(arg.length==2){var mouseX=arg[0];var mouseY=arg[1];}else{var mouseXY=RG.getMouseXY(arg);var mouseX=mouseXY[0];var mouseY=mouseXY[1];}
130
+ var obj=this;if(mouseY<this.gutterTop||mouseY>(ca.height-this.gutterBottom)||mouseX<this.gutterLeft||mouseX>(ca.width-this.gutterRight)){return null;}
131
+ var width=(ca.width-this.gutterLeft-this.gutterRight);var value=((mouseX-this.gutterLeft)/width)*(prop['chart.xmax']-prop['chart.xmin'])
132
+ value+=prop['chart.xmin'];return value;};this.highlight=this.Highlight=function(shape)
133
+ {if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else{if(shape['height']){RG.Highlight.Rect(this,shape);}else{RG.Highlight.Point(this,shape);}}};this.getObjectByXY=function(e)
134
+ {var mouseXY=RG.getMouseXY(e);if(mouseXY[0]>(this.gutterLeft-3)&&mouseXY[0]<(ca.width-this.gutterRight+3)&&mouseXY[1]>(this.gutterTop-3)&&mouseXY[1]<((ca.height-this.gutterBottom)+3)){return this;}};this.getXCoord=function(value)
135
+ {if(typeof value!='number'&&typeof value!='string'){return null;}
136
+ if(typeof value==='string'){value=RG.parseDate(value);}
137
+ var xmin=prop['chart.xmin'];var xmax=prop['chart.xmax'];var x;if(value<xmin)return null;if(value>xmax)return null;var gutterRight=this.gutterRight;var gutterLeft=this.gutterLeft;if(prop['chart.yaxispos']=='right'){x=((value-xmin)/(xmax-xmin))*(ca.width-gutterLeft-gutterRight);x=(ca.width-gutterRight-x);}else{x=((value-xmin)/(xmax-xmin))*(ca.width-gutterLeft-gutterRight);x=x+gutterLeft;}
138
+ return x;};this.getYCoord=this.getYCoordFromValue=function(value)
139
+ {if(typeof(value)!='number'){return null;}
140
+ var invert=prop['chart.ylabels.invert'];var xaxispos=prop['chart.xaxispos'];var graphHeight=ca.height-this.gutterTop-this.gutterBottom;var halfGraphHeight=graphHeight/2;var ymax=this.max;var ymin=prop['chart.ymin'];var coord=0;if(value>ymax||(prop['chart.xaxispos']=='bottom'&&value<ymin)||(prop['chart.xaxispos']=='center'&&((value>0&&value<ymin)||(value<0&&value>(-1*ymin))))){return null;}
141
+ if(xaxispos=='center'){coord=((Math.abs(value)-ymin)/(ymax-ymin))*halfGraphHeight;if(invert){coord=halfGraphHeight-coord;}
142
+ if(value<0){coord+=this.gutterTop;coord+=halfGraphHeight;}else{coord=halfGraphHeight-coord;coord+=this.gutterTop;}}else{coord=((value-ymin)/(ymax-ymin))*graphHeight;if(invert){coord=graphHeight-coord;}
143
+ coord=graphHeight-coord;coord=this.gutterTop+coord;}
144
+ return coord;};RG.Scatter.Bubble=function(scatter,min,max,width,data)
145
+ {this.scatter=scatter;this.min=min;this.max=max;this.width=width;this.data=data;this.coords=[];this.type='scatter.bubble'
146
+ this.set=this.Set=function(name,value)
147
+ {this.scatter.set(name,value);return this;};this.get=this.Get=function(name)
148
+ {this.scatter.get(name);};this.draw=this.Draw=function()
149
+ {var bubble_min=this.min,bubble_max=this.max,bubble_data=this.data,bubble_max_width=this.width;var obj_bubble=this,obj_scatter=this.scatter;this.scatter.ondraw=function(obj)
150
+ {for(var i=0;i<obj.coords[0].length;++i){bubble_data[i]=ma.max(bubble_data[i],bubble_min);bubble_data[i]=ma.min(bubble_data[i],bubble_max);var r=((bubble_data[i]-bubble_min)/(bubble_max-bubble_min))*bubble_max_width,color=obj_scatter.data[0][i][2]?obj_scatter.data[0][i][2]:obj_scatter.properties['chart.defaultcolor'];co.beginPath();co.fillStyle=RG.radialGradient(obj,obj_scatter.coords[0][i][0]+(r/2.5),obj_scatter.coords[0][i][1]-(r/2.5),0,obj_scatter.coords[0][i][0]+(r/2.5),obj_scatter.coords[0][i][1]-(r/2.5),r,prop['chart.colors.bubble.graduated']?'white':color,color);co.arc(obj_scatter.coords[0][i][0],obj_scatter.coords[0][i][1],r,0,RG.TWOPI,false);co.fill();obj_bubble.coords[i]=[obj_scatter.coords[0][i][0],obj_scatter.coords[0][i][1],r,co.fillStyle];}}
151
+ this.scatter.Draw();return this;};};this.parseColors=function()
152
+ {if(this.original_colors.length===0){this.original_colors['data']=RG.array_clone(this.data);this.original_colors['chart.background.vbars']=RG.array_clone(prop['chart.background.vbars']);this.original_colors['chart.background.hbars']=RG.array_clone(prop['chart.background.hbars']);this.original_colors['chart.line.colors']=RG.array_clone(prop['chart.line.colors']);this.original_colors['chart.defaultcolor']=RG.array_clone(prop['chart.defaultcolor']);this.original_colors['chart.crosshairs.color']=RG.array_clone(prop['chart.crosshairs.color']);this.original_colors['chart.highlight.stroke']=RG.array_clone(prop['chart.highlight.stroke']);this.original_colors['chart.highlight.fill']=RG.array_clone(prop['chart.highlight.fill']);this.original_colors['chart.background.barcolor1']=RG.array_clone(prop['chart.background.barcolor1']);this.original_colors['chart.background.barcolor2']=RG.array_clone(prop['chart.background.barcolor2']);this.original_colors['chart.background.grid.color']=RG.array_clone(prop['chart.background.grid.color']);this.original_colors['chart.background.color']=RG.array_clone(prop['chart.background.color']);this.original_colors['chart.axis.color']=RG.array_clone(prop['chart.axis.color']);}
153
+ var data=this.data;if(data){for(var dataset=0;dataset<data.length;++dataset){for(var i=0;i<this.data[dataset].length;++i){if(this.data[dataset][i]&&typeof(this.data[dataset][i][1])=='object'&&this.data[dataset][i][1]){if(typeof(this.data[dataset][i][1][5])=='string')this.data[dataset][i][1][5]=this.parseSingleColorForGradient(this.data[dataset][i][1][5]);if(typeof(this.data[dataset][i][1][6])=='string')this.data[dataset][i][1][6]=this.parseSingleColorForGradient(this.data[dataset][i][1][6]);}
154
+ if(!RG.isNull(this.data[dataset][i])){this.data[dataset][i][2]=this.parseSingleColorForGradient(this.data[dataset][i][2]);}}}}
155
+ var hbars=prop['chart.background.hbars'];if(hbars){for(i=0;i<hbars.length;++i){hbars[i][2]=this.parseSingleColorForGradient(hbars[i][2]);}}
156
+ var vbars=prop['chart.background.vbars'];if(vbars){for(i=0;i<vbars.length;++i){vbars[i][2]=this.parseSingleColorForGradient(vbars[i][2]);}}
157
+ var colors=prop['chart.line.colors'];if(colors){for(i=0;i<colors.length;++i){colors[i]=this.parseSingleColorForGradient(colors[i]);}}
158
+ prop['chart.defaultcolor']=this.parseSingleColorForGradient(prop['chart.defaultcolor']);prop['chart.crosshairs.color']=this.parseSingleColorForGradient(prop['chart.crosshairs.color']);prop['chart.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.highlight.stroke']);prop['chart.highlight.fill']=this.parseSingleColorForGradient(prop['chart.highlight.fill']);prop['chart.background.barcolor1']=this.parseSingleColorForGradient(prop['chart.background.barcolor1']);prop['chart.background.barcolor2']=this.parseSingleColorForGradient(prop['chart.background.barcolor2']);prop['chart.background.grid.color']=this.parseSingleColorForGradient(prop['chart.background.grid.color']);prop['chart.background.color']=this.parseSingleColorForGradient(prop['chart.background.color']);prop['chart.axis.color']=this.parseSingleColorForGradient(prop['chart.axis.color']);};this.reset=function()
159
+ {};this.parseSingleColorForGradient=function(color)
160
+ {if(!color||typeof(color)!='string'){return color;}
161
+ if(color.match(/^gradient\((.*)\)$/i)){var parts=RegExp.$1.split(':');var grad=co.createLinearGradient(0,ca.height-prop['chart.gutter.bottom'],0,prop['chart.gutter.top']);var diff=1/(parts.length-1);grad.addColorStop(0,RG.trim(parts[0]));for(var j=1;j<parts.length;++j){grad.addColorStop(j*diff,RG.trim(parts[j]));}}
162
+ return grad?grad:color;};this.interactiveKeyHighlight=function(index)
163
+ {if(this.coords&&this.coords[index]&&this.coords[index].length){this.coords[index].forEach(function(value,idx,arr)
164
+ {co.beginPath();co.fillStyle=prop['chart.key.interactive.highlight.chart.fill'];co.arc(value[0],value[1],prop['chart.ticksize']+3,0,RG.TWOPI,false);co.fill();});}};this.on=function(type,func)
165
+ {if(type.substr(0,2)!=='on'){type='on'+type;}
166
+ if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
167
+ return this;};this.firstDrawFunc=function()
168
+ {};this.trace=this.trace2=function()
169
+ {var obj=this,callback=arguments[2],opt=arguments[0]||{},frames=opt.frames||30,frame=0,callback=arguments[1]||function(){}
170
+ obj.Set('animationTrace',true);obj.Set('animationTraceClip',0);function iterator()
171
+ {RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(frame++<frames){obj.set('animationTraceClip',frame/frames);RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
172
+ iterator();return this;};this.resetColorsToOriginalValues=function()
173
+ {for(var i=0,len=this.original_colors['data'].length;i<len;++i){for(var j=0,len2=this.original_colors['data'][i].length;j<len2;++j){this.data[i][j][2]=RG.array_clone(this.original_colors['data'][i][j][2]);if(typeof this.data[i][j][1]==='object'){this.data[i][j][1][5]=RG.array_clone(this.original_colors['data'][i][j][1][5]);this.data[i][j][1][6]=RG.array_clone(this.original_colors['data'][i][j][1][6]);}}}};RG.register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};