rgraph-rails 4.62 → 4.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -4
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/vendor/assets/javascripts/RGraph.bar.js +240 -3742
  5. data/vendor/assets/javascripts/RGraph.bipolar.js +165 -2005
  6. data/vendor/assets/javascripts/RGraph.common.annotate.js +35 -395
  7. data/vendor/assets/javascripts/RGraph.common.context.js +30 -595
  8. data/vendor/assets/javascripts/RGraph.common.core.js +418 -5359
  9. data/vendor/assets/javascripts/RGraph.common.csv.js +20 -276
  10. data/vendor/assets/javascripts/RGraph.common.deprecated.js +35 -450
  11. data/vendor/assets/javascripts/RGraph.common.dynamic.js +88 -1395
  12. data/vendor/assets/javascripts/RGraph.common.effects.js +90 -1545
  13. data/vendor/assets/javascripts/RGraph.common.key.js +52 -753
  14. data/vendor/assets/javascripts/RGraph.common.resizing.js +37 -563
  15. data/vendor/assets/javascripts/RGraph.common.sheets.js +29 -352
  16. data/vendor/assets/javascripts/RGraph.common.tooltips.js +32 -450
  17. data/vendor/assets/javascripts/RGraph.common.zoom.js +14 -219
  18. data/vendor/assets/javascripts/RGraph.cornergauge.js +71 -0
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +34 -570
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +33 -544
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +51 -755
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +37 -645
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +36 -633
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +35 -514
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +37 -559
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +33 -548
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +36 -664
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +50 -812
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +51 -856
  30. data/vendor/assets/javascripts/RGraph.fuel.js +58 -964
  31. data/vendor/assets/javascripts/RGraph.funnel.js +55 -984
  32. data/vendor/assets/javascripts/RGraph.gantt.js +77 -1354
  33. data/vendor/assets/javascripts/RGraph.gauge.js +85 -1421
  34. data/vendor/assets/javascripts/RGraph.hbar.js +162 -2788
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +80 -1401
  36. data/vendor/assets/javascripts/RGraph.line.js +249 -4248
  37. data/vendor/assets/javascripts/RGraph.meter.js +74 -1280
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +19 -301
  39. data/vendor/assets/javascripts/RGraph.odo.js +71 -1264
  40. data/vendor/assets/javascripts/RGraph.pie.js +137 -2288
  41. data/vendor/assets/javascripts/RGraph.radar.js +110 -1847
  42. data/vendor/assets/javascripts/RGraph.rose.js +108 -1977
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +80 -1432
  44. data/vendor/assets/javascripts/RGraph.scatter.js +172 -3163
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +60 -1120
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +66 -1735
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +21 -246
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +255 -3937
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +20 -276
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +68 -1303
  51. data/vendor/assets/javascripts/RGraph.svg.common.key.js +19 -205
  52. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +29 -352
  53. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +22 -273
  54. data/vendor/assets/javascripts/RGraph.svg.funnel.js +32 -0
  55. data/vendor/assets/javascripts/RGraph.svg.hbar.js +59 -1400
  56. data/vendor/assets/javascripts/RGraph.svg.line.js +70 -1580
  57. data/vendor/assets/javascripts/RGraph.svg.pie.js +55 -1131
  58. data/vendor/assets/javascripts/RGraph.svg.radar.js +57 -1502
  59. data/vendor/assets/javascripts/RGraph.svg.rose.js +66 -1817
  60. data/vendor/assets/javascripts/RGraph.svg.scatter.js +58 -1261
  61. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +28 -865
  62. data/vendor/assets/javascripts/RGraph.svg.waterfall.js +45 -1252
  63. data/vendor/assets/javascripts/RGraph.thermometer.js +63 -1136
  64. data/vendor/assets/javascripts/RGraph.vprogress.js +83 -1470
  65. data/vendor/assets/javascripts/RGraph.waterfall.js +83 -1347
  66. metadata +5 -4
  67. data/vendor/assets/javascripts/financial-data.js +0 -1067
@@ -1,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);}};