rgraph-rails 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +0 -1
  3. data/README.md +3 -3
  4. data/lib/rgraph-rails/version.rb +1 -1
  5. data/vendor/assets/javascripts/RGraph.bar.js +239 -3764
  6. data/vendor/assets/javascripts/RGraph.bipolar.js +115 -1986
  7. data/vendor/assets/javascripts/RGraph.common.annotate.js +35 -399
  8. data/vendor/assets/javascripts/RGraph.common.context.js +30 -600
  9. data/vendor/assets/javascripts/RGraph.common.core.js +403 -5187
  10. data/vendor/assets/javascripts/RGraph.common.csv.js +19 -275
  11. data/vendor/assets/javascripts/RGraph.common.deprecated.js +35 -454
  12. data/vendor/assets/javascripts/RGraph.common.dynamic.js +84 -1189
  13. data/vendor/assets/javascripts/RGraph.common.effects.js +90 -1548
  14. data/vendor/assets/javascripts/RGraph.common.key.js +54 -755
  15. data/vendor/assets/javascripts/RGraph.common.resizing.js +37 -567
  16. data/vendor/assets/javascripts/RGraph.common.sheets.js +29 -356
  17. data/vendor/assets/javascripts/RGraph.common.tooltips.js +32 -614
  18. data/vendor/assets/javascripts/RGraph.common.zoom.js +14 -223
  19. data/vendor/assets/javascripts/RGraph.cornergauge.js +71 -0
  20. data/vendor/assets/javascripts/RGraph.drawing.background.js +35 -620
  21. data/vendor/assets/javascripts/RGraph.drawing.circle.js +35 -576
  22. data/vendor/assets/javascripts/RGraph.drawing.image.js +52 -807
  23. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +41 -717
  24. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +37 -668
  25. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +36 -563
  26. data/vendor/assets/javascripts/RGraph.drawing.poly.js +40 -608
  27. data/vendor/assets/javascripts/RGraph.drawing.rect.js +35 -597
  28. data/vendor/assets/javascripts/RGraph.drawing.text.js +34 -642
  29. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +50 -809
  30. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +51 -856
  31. data/vendor/assets/javascripts/RGraph.fuel.js +58 -964
  32. data/vendor/assets/javascripts/RGraph.funnel.js +55 -984
  33. data/vendor/assets/javascripts/RGraph.gantt.js +75 -1241
  34. data/vendor/assets/javascripts/RGraph.gauge.js +87 -1397
  35. data/vendor/assets/javascripts/RGraph.hbar.js +143 -2376
  36. data/vendor/assets/javascripts/RGraph.hprogress.js +80 -1397
  37. data/vendor/assets/javascripts/RGraph.line.js +241 -4162
  38. data/vendor/assets/javascripts/RGraph.meter.js +74 -1278
  39. metadata +3 -30
  40. data/vendor/assets/images/bg.png +0 -0
  41. data/vendor/assets/images/bullet.png +0 -0
  42. data/vendor/assets/images/facebook-large.png +0 -0
  43. data/vendor/assets/images/google-plus-large.png +0 -0
  44. data/vendor/assets/images/logo.png +0 -0
  45. data/vendor/assets/images/meter-image-sd-needle.png +0 -0
  46. data/vendor/assets/images/meter-image-sd.png +0 -0
  47. data/vendor/assets/images/meter-sketch-needle.png +0 -0
  48. data/vendor/assets/images/meter-sketch.png +0 -0
  49. data/vendor/assets/images/odometer-background.png +0 -0
  50. data/vendor/assets/images/rgraph.jpg +0 -0
  51. data/vendor/assets/images/title.png +0 -0
  52. data/vendor/assets/images/twitter-large.png +0 -0
  53. data/vendor/assets/javascripts/RGraph.modaldialog.js +0 -301
  54. data/vendor/assets/javascripts/RGraph.odo.js +0 -1265
  55. data/vendor/assets/javascripts/RGraph.pie.js +0 -2272
  56. data/vendor/assets/javascripts/RGraph.radar.js +0 -1847
  57. data/vendor/assets/javascripts/RGraph.rose.js +0 -1877
  58. data/vendor/assets/javascripts/RGraph.rscatter.js +0 -1425
  59. data/vendor/assets/javascripts/RGraph.scatter.js +0 -2970
  60. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +0 -1015
  61. data/vendor/assets/javascripts/RGraph.thermometer.js +0 -1129
  62. data/vendor/assets/javascripts/RGraph.vprogress.js +0 -1452
  63. data/vendor/assets/javascripts/RGraph.waterfall.js +0 -1252
  64. data/vendor/assets/javascripts/financial-data.js +0 -1067
  65. data/vendor/assets/stylesheets/ModalDialog.css +0 -90
  66. data/vendor/assets/stylesheets/animations.css +0 -3347
  67. data/vendor/assets/stylesheets/website.css +0 -446
@@ -1,4163 +1,242 @@
1
- // version: 2016-06-04
2
- /**
3
- * o--------------------------------------------------------------------------------o
4
- * | This file is part of the RGraph package - you can learn more at: |
5
- * | |
6
- * | http://www.rgraph.net |
7
- * | |
8
- * | RGraph is dual licensed under the Open Source GPL (General Public License) |
9
- * | v2.0 license and a commercial license which means that you're not bound by |
10
- * | the terms of the GPL. The commercial license is just 99 GBP and you can |
11
- * | read about it here: |
12
- * | http://www.rgraph.net/license |
13
- * o--------------------------------------------------------------------------------o
14
- */
15
1
 
16
- RGraph = window.RGraph || {isRGraph: true};
17
-
18
- /**
19
- * The line chart constructor
20
- *
21
- * @param object canvas The cxanvas object
22
- * @param array ... The lines to plot
23
- */
24
- RGraph.Line = 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 id = conf.id;
34
- var canvas = document.getElementById(id);
35
- var data = conf.data;
36
- var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
37
-
38
- } else {
39
-
40
- var id = conf;
41
- var canvas = document.getElementById(id);
42
- var data = arguments[1];
43
- }
44
-
45
-
46
-
47
-
48
- this.id = id;
49
- this.canvas = canvas;
50
- this.context = this.canvas.getContext('2d');
51
- this.canvas.__object__ = this;
52
- this.type = 'line';
53
- this.max = 0;
54
- this.coords = [];
55
- this.coords2 = [];
56
- this.coords.key = [];
57
- this.coordsText = [];
58
- this.coordsSpline = [];
59
- this.coordsAxes = {xaxis: [], yaxis: []};
60
- this.hasnegativevalues = false;
61
- this.isRGraph = true;
62
- this.uid = RGraph.CreateUID();
63
- this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
64
- this.colorsParsed = false;
65
- this.original_colors = [];
66
- this.firstDraw = true; // After the first draw this will be false
67
-
68
-
69
- /**
70
- * Compatibility with older browsers
71
- */
72
- //RGraph.OldBrowserCompat(this.context);
73
-
74
-
75
- // Various config type stuff
76
- this.properties =
77
- {
78
- 'chart.background.barcolor1': 'rgba(0,0,0,0)',
79
- 'chart.background.barcolor2': 'rgba(0,0,0,0)',
80
- 'chart.background.grid': 1,
81
- 'chart.background.grid.width': 1,
82
- 'chart.background.grid.hsize': 25,
83
- 'chart.background.grid.vsize': 25,
84
- 'chart.background.grid.color': '#ddd',
85
- 'chart.background.grid.vlines': true,
86
- 'chart.background.grid.hlines': true,
87
- 'chart.background.grid.border': true,
88
- 'chart.background.grid.autofit': true,
89
- 'chart.background.grid.autofit.align': true,
90
- 'chart.background.grid.autofit.numhlines': 5,
91
- 'chart.background.grid.autofit.numvlines': null,
92
- 'chart.background.grid.dashed': false,
93
- 'chart.background.grid.dotted': false,
94
- 'chart.background.hbars': null,
95
- 'chart.background.image': null,
96
- 'chart.background.image.stretch': true,
97
- 'chart.background.image.x': null,
98
- 'chart.background.image.y': null,
99
- 'chart.background.image.w': null,
100
- 'chart.background.image.h': null,
101
- 'chart.background.image.align': null,
102
- 'chart.background.color': null,
103
- 'chart.labels': null,
104
- 'chart.labels.bold': false,
105
- 'chart.labels.color': null,
106
- 'chart.labels.ingraph': null,
107
- 'chart.labels.above': false, // Working
108
- 'chart.labels.above.size': 8, // Working
109
- 'chart.labels.above.decimals': null, // Working
110
- 'chart.labels.above.color': null,
111
- 'chart.labels.above.background': 'white',
112
- 'chart.labels.above.font': null,
113
- 'chart.labels.above.border': true,
114
- 'chart.labels.above.offsety': 5,
115
- 'chart.labels.above.units.pre': '',
116
- 'chart.labels.above.units.post': '',
117
- 'chart.labels.above.specific': null,
118
- 'chart.labels.offsetx': 0,
119
- 'chart.labels.offsety': 0,
120
- 'chart.xtickgap': 20,
121
- 'chart.smallxticks': 3,
122
- 'chart.largexticks': 5,
123
- 'chart.ytickgap': 20,
124
- 'chart.smallyticks': 3,
125
- 'chart.largeyticks': 5,
126
- 'chart.numyticks': 10,
127
- 'chart.linewidth': 2.01,
128
- 'chart.colors': ['red', '#0f0', '#00f', '#f0f', '#ff0', '#0ff','green','pink','blue','black'],
129
- 'chart.hmargin': 0,
130
- 'chart.tickmarks.dot.stroke': 'white',
131
- 'chart.tickmarks.dot.fill': null,
132
- 'chart.tickmarks.dot.linewidth': 3,
133
- 'chart.tickmarks': 'endcircle',
134
- 'chart.tickmarks.linewidth': null,
135
- 'chart.tickmarks.image': null,
136
- 'chart.tickmarks.image.halign': 'center',
137
- 'chart.tickmarks.image.valign': 'center',
138
- 'chart.tickmarks.image.offsetx':0,
139
- 'chart.tickmarks.image.offsety':0,
140
- 'chart.ticksize': 3,
141
- 'chart.gutter.left': 25,
142
- 'chart.gutter.right': 25,
143
- 'chart.gutter.top': 25,
144
- 'chart.gutter.bottom': 30,
145
- 'chart.tickdirection': -1,
146
- 'chart.yaxispoints': 5,
147
- 'chart.fillstyle': null,
148
- 'chart.xaxispos': 'bottom',
149
- 'chart.xaxispos.value': 0,
150
- 'chart.yaxispos': 'left',
151
- 'chart.xticks': null,
152
- 'chart.text.size': 12,
153
- 'chart.text.angle': 0,
154
- 'chart.text.color': 'black',
155
- 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
156
- 'chart.text.accessible': true,
157
- 'chart.text.accessible.overflow': 'visible',
158
- 'chart.text.accessible.pointerevents': false,
159
- 'chart.ymin': 0,
160
- 'chart.ymax': null,
161
- 'chart.title': '',
162
- 'chart.title.background': null,
163
- 'chart.title.hpos': null,
164
- 'chart.title.vpos': null,
165
- 'chart.title.bold': true,
166
- 'chart.title.font': null,
167
- 'chart.title.xaxis': '',
168
- 'chart.title.xaxis.bold': true,
169
- 'chart.title.xaxis.size': null,
170
- 'chart.title.xaxis.font': null,
171
- 'chart.title.yaxis': '',
172
- 'chart.title.yaxis.bold': true,
173
- 'chart.title.yaxis.size': null,
174
- 'chart.title.yaxis.font': null,
175
- 'chart.title.yaxis.color': null,
176
- 'chart.title.xaxis.pos': null,
177
- 'chart.title.yaxis.pos': null,
178
- 'chart.title.yaxis.x': null,
179
- 'chart.title.yaxis.y': null,
180
- 'chart.title.xaxis.x': null,
181
- 'chart.title.xaxis.y': null,
182
- 'chart.title.x': null,
183
- 'chart.title.y': null,
184
- 'chart.title.halign': null,
185
- 'chart.title.valign': null,
186
- 'chart.shadow': true,
187
- 'chart.shadow.offsetx': 2,
188
- 'chart.shadow.offsety': 2,
189
- 'chart.shadow.blur': 3,
190
- 'chart.shadow.color': 'rgba(128,128,128,0.5)',
191
- 'chart.tooltips': null,
192
- 'chart.tooltips.hotspot.xonly': false,
193
- 'chart.tooltips.hotspot.size': 5,
194
- 'chart.tooltips.effect': 'fade',
195
- 'chart.tooltips.css.class': 'RGraph_tooltip',
196
- 'chart.tooltips.event': 'onmousemove',
197
- 'chart.tooltips.highlight': true,
198
- 'chart.tooltips.coords.page': false,
199
- 'chart.highlight.style': null,
200
- 'chart.highlight.stroke': 'gray',
201
- 'chart.highlight.fill': 'white',
202
- 'chart.stepped': false,
203
- 'chart.key': null,
204
- 'chart.key.background': 'white',
205
- 'chart.key.position': 'graph',
206
- 'chart.key.halign': null,
207
- 'chart.key.shadow': false,
208
- 'chart.key.shadow.color': '#666',
209
- 'chart.key.shadow.blur': 3,
210
- 'chart.key.shadow.offsetx': 2,
211
- 'chart.key.shadow.offsety': 2,
212
- 'chart.key.position.gutter.boxed': false,
213
- 'chart.key.position.x': null,
214
- 'chart.key.position.y': null,
215
- 'chart.key.color.shape': 'square',
216
- 'chart.key.rounded': true,
217
- 'chart.key.linewidth': 1,
218
- 'chart.key.colors': null,
219
- 'chart.key.interactive': false,
220
- 'chart.key.interactive.highlight.chart.stroke': 'rgba(255,0,0,0.3)',
221
- 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
222
- 'chart.key.text.color': 'black',
223
- 'chart.contextmenu': null,
224
- 'chart.ylabels': true,
225
- 'chart.ylabels.count': 5,
226
- 'chart.ylabels.inside': false,
227
- 'chart.ylabels.offsetx': 0,
228
- 'chart.ylabels.offsety': 0,
229
- 'chart.scale.invert': false,
230
- 'chart.xlabels.inside': false,
231
- 'chart.xlabels.inside.color': 'rgba(255,255,255,0.5)',
232
- 'chart.noaxes': false,
233
- 'chart.noyaxis': false,
234
- 'chart.noxaxis': false,
235
- 'chart.noendxtick': false,
236
- 'chart.noendytick': false,
237
- 'chart.units.post': '',
238
- 'chart.units.pre': '',
239
- 'chart.scale.zerostart': true,
240
- 'chart.scale.decimals': null,
241
- 'chart.scale.point': '.',
242
- 'chart.scale.thousand': ',',
243
- 'chart.crosshairs': false,
244
- 'chart.crosshairs.color': '#333',
245
- 'chart.crosshairs.hline': true,
246
- 'chart.crosshairs.vline': true,
247
- 'chart.annotatable': false,
248
- 'chart.annotate.color': 'black',
249
- 'chart.axesontop': false,
250
- 'chart.filled': false,
251
- 'chart.filled.range': false,
252
- 'chart.filled.range.threshold': null,
253
- 'chart.filled.range.threshold.colors': ['red', 'green'],
254
- 'chart.filled.accumulative': true,
255
- 'chart.variant': null,
256
- 'chart.axis.color': 'black',
257
- 'chart.axis.linewidth': 1,
258
- 'chart.numxticks': (data && typeof(data[0]) == 'number' ? data.length - 1: 20),
259
- 'chart.numyticks': 10,
260
- 'chart.zoom.factor': 1.5,
261
- 'chart.zoom.fade.in': true,
262
- 'chart.zoom.fade.out': true,
263
- 'chart.zoom.hdir': 'right',
264
- 'chart.zoom.vdir': 'down',
265
- 'chart.zoom.frames': 25,
266
- 'chart.zoom.delay': 16.666,
267
- 'chart.zoom.shadow': true,
268
- 'chart.zoom.background': true,
269
- 'chart.zoom.action': 'zoom',
270
- 'chart.backdrop': false,
271
- 'chart.backdrop.size': 30,
272
- 'chart.backdrop.alpha': 0.2,
273
- 'chart.resizable': false,
274
- 'chart.resize.handle.adjust': [0,0],
275
- 'chart.resize.handle.background': null,
276
- 'chart.adjustable': false,
277
- 'chart.noredraw': false,
278
- 'chart.outofbounds': false,
279
- 'chart.outofbounds.clip': false,
280
- 'chart.chromefix': true,
281
- 'chart.animation.factor': 1,
282
- 'chart.animation.unfold.x': false,
283
- 'chart.animation.unfold.y': true,
284
- 'chart.animation.unfold.initial': 2,
285
- 'chart.animation.trace.clip': 1,
286
- 'chart.curvy': false,
287
- 'chart.line.visible': [],
288
- 'chart.events.click': null,
289
- 'chart.events.mousemove': null,
290
- 'chart.errorbars': false,
291
- 'chart.errorbars.color': 'black',
292
- 'chart.errorbars.capped': true,
293
- 'chart.errorbars.capped.width': 12,
294
- 'chart.errorbars.linewidth': 1,
295
- 'chart.combinedchart.effect': null,
296
- 'chart.combinedchart.effect.options': null,
297
- 'chart.combinedchart.effect.callback': null,
298
- 'chart.clearto': 'rgba(0,0,0,0)'
299
- }
300
-
301
- /**
302
- * Change null arguments to empty arrays
303
- */
304
- for (var i=1; i<arguments.length; ++i) {
305
- if (typeof(arguments[i]) == 'null' || !arguments[i]) {
306
- arguments[i] = [];
307
- }
308
- }
309
-
310
-
311
- /**
312
- * Store the original data. This also allows for giving arguments as one big array.
313
- */
314
- this.original_data = [];
315
-
316
- // This allows for the new object based configuration style
317
- if (typeof conf === 'object' && conf.data) {
318
- if (typeof conf.data[0] === 'number' || RGraph.isNull(conf.data[0])) {
319
-
320
- this.original_data[0] = RGraph.arrayClone(conf.data);
321
-
322
- //} else if (typeof conf.data[0] === 'object' && !RGraph.isNull(conf.data[0])) {
323
- } else {
324
-
325
- for (var i=0; i<conf.data.length; ++i) {
326
- this.original_data[i] = RGraph.arrayClone(conf.data[i]);
327
- }
328
- }
329
-
330
- // Allow for the older configuration style
331
- } else {
332
- for (var i=1; i<arguments.length; ++i) {
333
-
334
- if ( arguments[1]
335
- && typeof(arguments[1]) == 'object'
336
- && arguments[1][0]
337
- && typeof(arguments[1][0]) == 'object'
338
- && arguments[1][0].length) {
339
-
340
- var tmp = [];
341
-
342
- for (var i=0; i<arguments[1].length; ++i) {
343
- tmp[i] = RGraph.array_clone(arguments[1][i]);
344
- }
345
-
346
- for (var j=0; j<tmp.length; ++j) {
347
- this.original_data[j] = RGraph.array_clone(tmp[j]);
348
- }
349
-
350
- } else {
351
- this.original_data[i - 1] = RGraph.array_clone(arguments[i]);
352
- }
353
- }
354
- }
355
-
356
-
357
- // Check for support
358
- if (!this.canvas) {
359
- alert('[LINE] Fatal error: no canvas support');
360
- return;
361
- }
362
-
363
- // Convert strings to numbers
364
- for (var i=0; i<this.original_data.length; ++i) {
365
- for (var j=0; j<this.original_data[i].length; ++j) {
366
- if (typeof this.original_data[i][j] === 'string') {
367
- this.original_data[i][j] = parseFloat(this.original_data[i][j]);
368
- }
369
- }
370
- }
371
-
372
-
373
- /**
374
- * Store the data here as one big array
375
- */
376
- this.data_arr = RGraph.arrayLinearize(this.original_data);
377
-
378
- for (var i=0; i<this.data_arr.length; ++i) {
379
- this['$' + i] = {};
380
- }
381
-
382
-
383
- /**
384
- * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
385
- * done already
386
- */
387
- if (!this.canvas.__rgraph_aa_translated__) {
388
- this.context.translate(0.5,0.5);
389
-
390
- this.canvas.__rgraph_aa_translated__ = true;
391
- }
392
-
393
-
394
-
395
-
396
- // Short variable names
397
- var RG = RGraph,
398
- ca = this.canvas,
399
- co = ca.getContext('2d'),
400
- prop = this.properties,
401
- pa2 = RG.path2,
402
- win = window,
403
- doc = document,
404
- ma = Math
405
-
406
-
407
-
408
- /**
409
- * "Decorate" the object with the generic effects if the effects library has been included
410
- */
411
- if (RG.Effects && typeof RG.Effects.decorate === 'function') {
412
- RG.Effects.decorate(this);
413
- }
414
-
415
-
416
-
417
-
418
-
419
- /**
420
- * An all encompassing accessor
421
- *
422
- * @param string name The name of the property
423
- * @param mixed value The value of the property
424
- */
425
- this.set =
426
- this.Set = function (name)
427
- {
428
- var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
429
-
430
- /**
431
- * the number of arguments is only one and it's an
432
- * object - parse it for configuration data and return.
433
- */
434
- if (arguments.length === 1 && typeof name === 'object') {
435
- RG.parseObjectStyleConfig(this, name);
436
- return this;
437
- }
438
-
439
-
440
-
441
-
442
-
443
- /**
444
- * This should be done first - prepend the propertyy name with "chart." if necessary
445
- */
446
- if (name.substr(0,6) != 'chart.') {
447
- name = 'chart.' + name;
448
- }
449
-
450
-
451
-
452
-
453
- // Convert uppercase letters to dot+lower case letter
454
- while(name.match(/([A-Z])/)) {
455
- name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
456
- }
457
-
458
-
459
-
460
- // Consolidate the tooltips
461
- if (name == 'chart.tooltips' && typeof value == 'object' && value) {
462
-
463
- var tooltips = [];
464
-
465
- for (var i=1; i<arguments.length; i++) {
466
- if (typeof(arguments[i]) == 'object' && arguments[i][0]) {
467
- for (var j=0; j<arguments[i].length; j++) {
468
- tooltips.push(arguments[i][j]);
469
- }
470
-
471
- } else if (typeof(arguments[i]) == 'function') {
472
- tooltips = arguments[i];
473
-
474
- } else {
475
- tooltips.push(arguments[i]);
476
- }
477
- }
478
-
479
- // Because "value" is used further down at the end of this function, set it to the expanded array os tooltips
480
- value = tooltips;
481
- }
482
-
483
-
484
- /**
485
- * If (buggy) Chrome and the linewidth is 1, change it to 1.01
486
- */
487
- if (name == 'chart.linewidth' && navigator.userAgent.match(/Chrome/)) {
488
- if (value == 1) {
489
- value = 1.01;
490
-
491
- } else if (RGraph.is_array(value)) {
492
- for (var i=0; i<value.length; ++i) {
493
- if (typeof(value[i]) == 'number' && value[i] == 1) {
494
- value[i] = 1.01;
495
- }
496
- }
497
- }
498
- }
499
-
500
-
501
- /**
502
- * Check for xaxispos
503
- */
504
- if (name == 'chart.xaxispos' ) {
505
- if (value != 'bottom' && value != 'center' && value != 'top') {
506
- alert('[LINE] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
507
- value = 'center';
508
- }
509
- }
510
-
511
-
512
- /**
513
- * chart.xticks is now called chart.numxticks
514
- */
515
- if (name == 'chart.xticks') {
516
- name = 'chart.numxticks';
517
- }
518
-
519
-
520
- /**
521
- * Change the new chart.spline option to chart.curvy
522
- */
523
- if (name == 'chart.spline') {
524
- name = 'chart.curvy';
525
- }
526
-
527
-
528
- /**
529
- * Chnge chart.ylabels.invert to chart.scale.invert
530
- */
531
- if (name == 'chart.ylabels.invert') {
532
- name = 'chart.scale.invert';
533
- }
534
-
535
-
536
-
537
-
538
-
539
-
540
-
541
- this.properties[name] = value;
542
-
543
- return this;
544
- };
545
-
546
-
547
-
548
-
549
- /**
550
- * An all encompassing accessor
551
- *
552
- * @param string name The name of the property
553
- */
554
- this.get =
555
- this.Get = function (name)
556
- {
557
- /**
558
- * This should be done first - prepend the property name with "chart." if necessary
559
- */
560
- if (name.substr(0,6) != 'chart.') {
561
- name = 'chart.' + name;
562
- }
563
-
564
- // Convert uppercase letters to dot+lower case letter
565
- name = name.replace(/([A-Z])/g, function (str)
566
- {
567
- return '.' + String(RegExp.$1).toLowerCase()
568
- });
569
-
570
- /**
571
- * If requested property is chart.spline - change it to chart.curvy
572
- */
573
- if (name == 'chart.spline') {
574
- name = 'chart.curvy';
575
- }
576
-
577
- return prop[name];
578
- };
579
-
580
-
581
-
582
-
583
- /**
584
- * The function you call to draw the line chart
585
- *
586
- * @param bool An optional bool used internally to ditinguish whether the
587
- * line chart is being called by the bar chart
588
- *
589
- * Draw()
590
- * |
591
- * +--Draw()
592
- * | |
593
- * | +-DrawLine()
594
- * |
595
- * +-RedrawLine()
596
- * |
597
- * +-DrawCurvyLine()
598
- * |
599
- * +-DrawSpline()
600
- */
601
- this.draw =
602
- this.Draw = function ()
603
- {
604
- // MUST be the first thing done!
605
- if (typeof(prop['chart.background.image']) == 'string') {
606
- RG.DrawBackgroundImage(this);
607
- }
608
-
609
-
610
- /**
611
- * Fire the onbeforedraw event
612
- */
613
- RG.FireCustomEvent(this, 'onbeforedraw');
614
-
615
-
616
-
617
-
618
-
619
-
620
- /**
621
- * Parse the colors. This allows for simple gradient syntax
622
- */
623
- if (!this.colorsParsed) {
624
-
625
- this.parseColors();
626
-
627
- // Don't want to do this again
628
- this.colorsParsed = true;
629
- }
630
-
631
-
632
-
633
- /**
634
- * This is new in May 2011 and facilitates indiviual gutter settings,
635
- * eg chart.gutter.left
636
- */
637
- this.gutterLeft = prop['chart.gutter.left'];
638
- this.gutterRight = prop['chart.gutter.right'];
639
- this.gutterTop = prop['chart.gutter.top'];
640
- this.gutterBottom = prop['chart.gutter.bottom'];
641
-
642
-
643
- /**
644
- * Check for Chrome 6 and shadow
645
- *
646
- * TODO Remove once it's been fixed (for a while)
647
- * 07/03/2014 - Removed
648
- * 29/10/2011 - Looks like it's been fixed as long the linewidth is at least 1.01
649
- * SEARCH TAGS: CHROME FIX SHADOW BUG
650
- */
651
- //if ( prop['chart.shadow']
652
- // && RG.ISCHROME
653
- // && prop['chart.linewidth'] <= 1
654
- // && prop['chart.chromefix']
655
- // && prop['chart.shadow.blur'] > 0) {
656
- // alert('[RGRAPH WARNING] Chrome has a shadow bug, meaning you should increase the linewidth to at least 1.01');
657
- //}
658
-
659
-
660
- // Reset the data back to that which was initially supplied
661
- this.data = RG.array_clone(this.original_data);
662
-
663
-
664
- // Reset the max value
665
- this.max = 0;
666
-
667
- /**
668
- * Reverse the datasets so that the data and the labels tally
669
- * COMMENTED OUT 15TH AUGUST 2011
670
- */
671
- //this.data = RG.array_reverse(this.data);
672
-
673
- if (prop['chart.filled'] && !prop['chart.filled.range'] && this.data.length > 1 && prop['chart.filled.accumulative']) {
674
-
675
- var accumulation = [];
676
-
677
- for (var set=0; set<this.data.length; ++set) {
678
- for (var point=0; point<this.data[set].length; ++point) {
679
- this.data[set][point] = Number(accumulation[point] ? accumulation[point] : 0) + this.data[set][point];
680
- accumulation[point] = this.data[set][point];
681
- }
682
- }
683
- }
684
-
685
- /**
686
- * Get the maximum Y scale value
687
- */
688
- if (prop['chart.ymax']) {
689
-
690
- this.max = prop['chart.ymax'];
691
- this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
692
-
693
- this.scale2 = RG.getScale2(this, {
694
- 'max':this.max,
695
- 'min':prop['chart.ymin'],
696
- 'strict':true,
697
- 'scale.thousand':prop['chart.scale.thousand'],
698
- 'scale.point':prop['chart.scale.point'],
699
- 'scale.decimals':prop['chart.scale.decimals'],
700
- 'ylabels.count':prop['chart.ylabels.count'],
701
- 'scale.round':prop['chart.scale.round'],
702
- 'units.pre': prop['chart.units.pre'],
703
- 'units.post': prop['chart.units.post']
704
- });
705
-
706
- this.max = this.scale2.max ? this.scale2.max : 0;
707
-
708
- // Check for negative values
709
- if (!prop['chart.outofbounds']) {
710
- for (dataset=0; dataset<this.data.length; ++dataset) {
711
- if (RGraph.isArray(this.data[dataset])) {
712
- for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
713
- // Check for negative values
714
- this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
715
- }
716
- }
717
- }
718
- }
719
-
720
- } else {
721
-
722
- this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
723
-
724
- // Work out the max Y value
725
- for (dataset=0; dataset<this.data.length; ++dataset) {
726
- for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
727
-
728
- this.max = Math.max(this.max, this.data[dataset][datapoint] ? Math.abs(parseFloat(this.data[dataset][datapoint])) : 0);
729
-
730
- // Check for negative values
731
- if (!prop['chart.outofbounds']) {
732
- this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
733
- }
734
- }
735
- }
736
-
737
- this.scale2 = RG.getScale2(this, {
738
- 'max':this.max,
739
- 'min':prop['chart.ymin'],
740
- 'scale.thousand':prop['chart.scale.thousand'],
741
- 'scale.point':prop['chart.scale.point'],
742
- 'scale.decimals':prop['chart.scale.decimals'],
743
- 'ylabels.count':prop['chart.ylabels.count'],
744
- 'scale.round':prop['chart.scale.round'],
745
- 'units.pre': prop['chart.units.pre'],
746
- 'units.post': prop['chart.units.post']
747
- });
748
-
749
- this.max = this.scale2.max ? this.scale2.max : 0;
750
- }
751
-
752
- /**
753
- * Setup the context menu if required
754
- */
755
- if (prop['chart.contextmenu']) {
756
- RG.ShowContext(this);
757
- }
758
-
759
- /**
760
- * Reset the coords arrays otherwise it will keep growing
761
- */
762
- this.coords = [];
763
- this.coordsText = [];
764
-
765
- /**
766
- * Work out a few things. They need to be here because they depend on things you can change before you
767
- * call Draw() but after you instantiate the object
768
- */
769
- this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
770
- this.halfgrapharea = this.grapharea / 2;
771
- this.halfTextHeight = prop['chart.text.size'] / 2;
772
-
773
- // Check the combination of the X axis position and if there any negative values
774
- //
775
- // 25th Feb 2016 - Removed entirely as this is another way to do
776
- // offset axes
777
- //if (prop['chart.xaxispos'] == 'bottom' && this.hasnegativevalues && !RG.ISOPERA) {
778
- // alert('[LINE] You have negative values and the X axis is at the bottom. This is not good...');
779
- //}
780
-
781
- if (prop['chart.variant'] == '3d') {
782
- RG.Draw3DAxes(this);
783
- }
784
-
785
- // Progressively Draw the chart
786
- RG.background.Draw(this);
787
-
788
-
789
- /**
790
- * Draw any horizontal bars that have been defined
791
- */
792
- if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length > 0) {
793
- RG.DrawBars(this);
794
- }
795
-
796
- if (prop['chart.axesontop'] == false) {
797
- this.DrawAxes();
798
- }
799
-
800
- //if (typeof(shadowColor) == 'object') {
801
- // shadowColor = RG.array_reverse(RG.array_clone(prop['chart.shadow.color']]);
802
- //}
803
-
804
- /**
805
- * This facilitates the new Trace2 effect
806
- */
807
-
808
- co.save()
809
- co.beginPath();
810
- co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
811
- co.clip();
812
-
813
- for (var i=0, j=0, len=this.data.length; i<len; i++, j++) {
814
-
815
- co.beginPath();
816
-
817
- /**
818
- * Turn on the shadow if required
819
- */
820
- if (!prop['chart.filled']) {
821
- this.SetShadow(i);
822
- }
823
-
824
- /**
825
- * Draw the line
826
- */
827
-
828
- if (prop['chart.fillstyle']) {
829
- if (typeof(prop['chart.fillstyle']) == 'object' && prop['chart.fillstyle'][j]) {
830
- var fill = prop['chart.fillstyle'][j];
831
-
832
- } else if (typeof(prop['chart.fillstyle']) == 'object' && prop['chart.fillstyle'].toString().indexOf('Gradient') > 0) {
833
- var fill = prop['chart.fillstyle'];
834
-
835
- } else if (typeof(prop['chart.fillstyle']) == 'string') {
836
- var fill = prop['chart.fillstyle'];
837
-
838
- }
839
- } else if (prop['chart.filled']) {
840
- var fill = prop['chart.colors'][j];
841
-
842
- } else {
843
- var fill = null;
844
- }
845
-
846
- /**
847
- * Figure out the tickmark to use
848
- */
849
- if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'object') {
850
- var tickmarks = prop['chart.tickmarks'][i];
851
- } else if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'string') {
852
- var tickmarks = prop['chart.tickmarks'];
853
- } else if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'function') {
854
- var tickmarks = prop['chart.tickmarks'];
855
- } else {
856
- var tickmarks = null;
857
- }
858
-
859
- //
860
- // Draw the line, accounting for the outofboundsClip option
861
- //
862
- if (prop['chart.outofbounds.clip']) {
863
- pa2(
864
- co,
865
- 'sa b r % % % % cl b',
866
- 0,
867
- this.gutterTop,
868
- ca.width,
869
- ca.height - this.gutterTop - this.gutterBottom
870
- );
871
- }
872
- this.drawLine(
873
- this.data[i],
874
- prop['chart.colors'][j],
875
- fill,
876
- this.getLineWidth(j),
877
- tickmarks,
878
- i
879
- );
880
- if (prop['chart.outofbounds.clip']) {
881
- co.restore();
882
- }
883
-
884
- co.stroke();
885
-
886
- /**
887
- * Draw errorbars
888
- *
889
- * ** This is now done in the redrawLine function **
890
- */
891
- }
892
-
893
- /**
894
- * If the line is filled re-stroke the lines
895
- */
896
- if (prop['chart.filled'] && prop['chart.filled.accumulative'] && !prop['chart.curvy']) {
897
-
898
- for (var i=0; i<this.coords2.length; ++i) {
899
-
900
- co.beginPath();
901
- co.lineWidth = this.GetLineWidth(i);
902
- co.strokeStyle = !this.hidden(i) ? prop['chart.colors'][i] : 'rgba(0,0,0,0)';
903
-
904
- for (var j=0,len=this.coords2[i].length; j<len; ++j) {
905
-
906
- if (j == 0 || this.coords2[i][j][1] == null || (this.coords2[i][j - 1] && this.coords2[i][j - 1][1] == null)) {
907
- co.moveTo(this.coords2[i][j][0], this.coords2[i][j][1]);
908
- } else {
909
- if (prop['chart.stepped']) {
910
- co.lineTo(this.coords2[i][j][0], this.coords2[i][j - 1][1]);
911
- }
912
- co.lineTo(this.coords2[i][j][0], this.coords2[i][j][1]);
913
- }
914
- }
915
-
916
- co.stroke();
917
- // No fill!
918
- }
919
-
920
- //Redraw the tickmarks
921
- if (prop['chart.tickmarks']) {
922
-
923
- co.beginPath();
924
-
925
- co.fillStyle = 'white';
926
-
927
- for (var i=0,len=this.coords2.length; i<len; ++i) {
928
-
929
- co.beginPath();
930
- co.strokeStyle = prop['chart.colors'][i];
931
-
932
- for (var j=0; j<this.coords2[i].length; ++j) {
933
- if (typeof(this.coords2[i][j]) == 'object' && typeof(this.coords2[i][j][0]) == 'number' && typeof(this.coords2[i][j][1]) == 'number') {
934
-
935
- var tickmarks = typeof(prop['chart.tickmarks']) == 'object' ? prop['chart.tickmarks'][i] : prop['chart.tickmarks'];
936
-
937
- this.DrawTick(
938
- this.coords2[i],
939
- this.coords2[i][j][0],
940
- this.coords2[i][j][1],
941
- co.strokeStyle,
942
- false,
943
- j == 0 ? 0 : this.coords2[i][j - 1][0],
944
- j == 0 ? 0 : this.coords2[i][j - 1][1],
945
- tickmarks,
946
- j,
947
- i
948
- );
949
- }
950
- }
951
- }
952
-
953
- co.stroke();
954
- co.fill();
955
- }
956
-
957
- } else if (prop['chart.filled'] && prop['chart.filled.accumulative'] && prop['chart.curvy']) {
958
-
959
- // Restroke the curvy filled accumulative lines
960
-
961
- for (var i=0; i<this.coordsSpline.length; i+=1) {
962
- co.beginPath();
963
- co.strokeStyle = prop['chart.colors'][i];
964
- co.lineWidth = this.GetLineWidth(i);
965
-
966
- for (var j=0,len=this.coordsSpline[i].length; j<len; j+=1) {
967
-
968
- var point = this.coordsSpline[i][j];
969
-
970
- j == 0 ? co.moveTo(point[0], point[1]) : co.lineTo(point[0], point[1]);
971
- }
972
-
973
- co.stroke();
974
- }
975
-
976
-
977
-
978
-
979
-
980
-
981
-
982
- for (var i=0,len=this.coords2.length; i<len; i+=1) {
983
- for (var j=0,len2=this.coords2[i].length; j<len2; ++j) {
984
- if (typeof(this.coords2[i][j]) == 'object' && typeof(this.coords2[i][j][0]) == 'number' && typeof(this.coords2[i][j][1]) == 'number') {
985
-
986
- var tickmarks = typeof prop['chart.tickmarks'] == 'object' && !RGraph.is_null(prop['chart.tickmarks']) ? prop['chart.tickmarks'][i] : prop['chart.tickmarks'];
987
- co.strokeStyle = prop['chart.colors'][i];
988
- this.DrawTick(
989
- this.coords2[i],
990
- this.coords2[i][j][0],
991
- this.coords2[i][j][1],
992
- prop['chart.colors'][i],
993
- false,
994
- j == 0 ? 0 : this.coords2[i][j - 1][0],
995
- j == 0 ? 0 : this.coords2[i][j - 1][1],
996
- tickmarks,
997
- j,
998
- i
999
- );
1000
- }
1001
- }
1002
- }
1003
-
1004
-
1005
-
1006
- }
1007
- co.restore();
1008
-
1009
- // ???
1010
- co.beginPath();
1011
-
1012
-
1013
-
1014
-
1015
- /**
1016
- * If the axes have been requested to be on top, do that
1017
- */
1018
- if (prop['chart.axesontop']) {
1019
- this.DrawAxes();
1020
- }
1021
-
1022
- /**
1023
- * Draw the labels
1024
- */
1025
- this.DrawLabels();
1026
-
1027
- /**
1028
- * Draw the range if necessary
1029
- */
1030
- this.DrawRange();
1031
-
1032
- // Draw a key if necessary
1033
- if (prop['chart.key'] && prop['chart.key'].length && RG.DrawKey) {
1034
- RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
1035
- }
1036
-
1037
- /**
1038
- * Draw " above" labels if enabled
1039
- */
1040
- if (prop['chart.labels.above']) {
1041
- this.drawAboveLabels();
1042
- }
1043
-
1044
- /**
1045
- * Draw the "in graph" labels
1046
- */
1047
- RG.DrawInGraphLabels(this);
1048
-
1049
- /**
1050
- * Redraw the lines if a filled range is on the cards
1051
- */
1052
- if (prop['chart.filled'] && prop['chart.filled.range'] && this.data.length == 2) {
1053
-
1054
- co.beginPath();
1055
- var len = this.coords.length / 2;
1056
- co.lineWidth = prop['chart.linewidth'];
1057
- co.strokeStyle = this.hidden(0) ? 'rgba(0,0,0,0)' : prop['chart.colors'][0];
1058
-
1059
- for (var i=0; i<len; ++i) {
1060
-
1061
- if (!RG.isNull(this.coords[i][1])) {
1062
- if (i == 0) {
1063
- co.moveTo(this.coords[i][0], this.coords[i][1]);
1064
- } else {
1065
- co.lineTo(this.coords[i][0], this.coords[i][1]);
1066
- }
1067
- }
1068
- }
1069
-
1070
- co.stroke();
1071
-
1072
-
1073
- co.beginPath();
1074
-
1075
- if (prop['chart.colors'][1]) {
1076
- co.strokeStyle = this.hidden(1) ? 'rgba(0,0,0,0)' : prop['chart.colors'][1];
1077
- }
1078
-
1079
- for (var i=this.coords.length - 1; i>=len; --i) {
1080
- if (!RG.is_null(this.coords[i][1])) {
1081
- if (i == (this.coords.length - 1)) {
1082
- co.moveTo(this.coords[i][0], this.coords[i][1]);
1083
- } else {
1084
- co.lineTo(this.coords[i][0], this.coords[i][1]);
1085
- }
1086
- }
1087
- }
1088
-
1089
- co.stroke();
1090
-
1091
-
1092
- } else if (prop['chart.filled'] && prop['chart.filled.range']) {
1093
- alert('[LINE] You must have only two sets of data for a filled range chart');
1094
- }
1095
-
1096
- /**
1097
- * This function enables resizing
1098
- */
1099
- if (prop['chart.resizable']) {
1100
- RG.AllowResizing(this);
1101
- }
1102
-
1103
-
1104
- /**
1105
- * This installs the event listeners
1106
- */
1107
- RG.InstallEventListeners(this);
1108
-
1109
-
1110
-
1111
-
1112
-
1113
-
1114
- /**
1115
- * Fire the onfirstdraw event
1116
- */
1117
- if (this.firstDraw) {
1118
- RG.fireCustomEvent(this, 'onfirstdraw');
1119
- this.firstDraw = false;
1120
- this.firstDrawFunc();
1121
- }
1122
-
1123
-
1124
-
1125
-
1126
- /**
1127
- * Fire the RGraph ondraw event
1128
- */
1129
- RG.FireCustomEvent(this, 'ondraw');
1130
-
1131
- return this;
1132
- };
1133
-
1134
-
1135
-
1136
- /**
1137
- * Used in chaining. Runs a function there and then - not waiting for
1138
- * the events to fire (eg the onbeforedraw event)
1139
- *
1140
- * @param function func The function to execute
1141
- */
1142
- this.exec = function (func)
1143
- {
1144
- func(this);
1145
-
1146
- return this;
1147
- };
1148
-
1149
-
1150
-
1151
-
1152
- /**
1153
- * Draws the axes
1154
- */
1155
- this.drawAxes =
1156
- this.DrawAxes = function ()
1157
- {
1158
- // Don't draw the axes?
1159
- if (prop['chart.noaxes']) {
1160
- return;
1161
- }
1162
-
1163
- // Turn any shadow off
1164
- RG.noShadow(this);
1165
-
1166
- co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
1167
- co.lineCap = 'square';
1168
- co.lineJoin = 'miter';
1169
- co.strokeStyle = prop['chart.axis.color'];
1170
- coords = {
1171
- xaxis: {},
1172
- yaxis: {}
1173
- };
1174
-
1175
- co.beginPath();
1176
-
1177
- // Draw the X axis
1178
- if (prop['chart.noxaxis'] == false) {
1179
- if (prop['chart.xaxispos'] == 'center') {
1180
- coords.xaxis = [
1181
- this.gutterLeft,
1182
- ma.round((this.grapharea / 2) + this.gutterTop),
1183
- ca.width - this.gutterRight,
1184
- ma.round((this.grapharea / 2) + this.gutterTop)
1185
- ];
1186
- } else if (prop['chart.xaxispos'] === 'top') {
1187
- coords.xaxis = [
1188
- this.gutterLeft,
1189
- this.gutterTop,
1190
- ca.width - this.gutterRight,
1191
- this.gutterTop
1192
- ];
1193
- } else {
1194
-
1195
- var y = ma.round(this.getYCoord(prop['chart.ymin'] > 0 ? prop['chart.ymin'] : 0));
1196
-
1197
- coords.xaxis = [
1198
- this.gutterLeft,
1199
- y,
1200
- ca.width - this.gutterRight,
1201
- y
1202
- ];
1203
- }
1204
-
1205
- co.moveTo(coords.xaxis[0], coords.xaxis[1]);
1206
- co.lineTo(coords.xaxis[2], coords.xaxis[3]);
1207
-
1208
- // Save the coords so that they can
1209
- // be referenced at a later time
1210
- this.coordsAxes = coords;
1211
- }
1212
-
1213
- // Draw the Y axis
1214
- if (prop['chart.noyaxis'] == false) {
1215
- if (prop['chart.yaxispos'] == 'left') {
1216
- co.moveTo(this.gutterLeft, this.gutterTop);
1217
- co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
1218
- } else {
1219
- co.moveTo(ca.width - this.gutterRight, this.gutterTop);
1220
- co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
1221
- }
1222
- }
1223
-
1224
- /**
1225
- * Draw the X tickmarks
1226
- */
1227
- if (prop['chart.noxaxis'] == false && prop['chart.numxticks'] > 0) {
1228
-
1229
- var xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
1230
-
1231
-
1232
- if (!xTickInterval || xTickInterval <= 0) {
1233
- xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / (prop['chart.labels'] && prop['chart.labels'].length ? prop['chart.labels'].length - 1 : 10);
1234
- }
1235
-
1236
- for (x=this.gutterLeft + (prop['chart.yaxispos'] == 'left' ? xTickInterval : 0); x<=(ca.width - this.gutterRight + 1 ); x+=xTickInterval) {
1237
-
1238
- if (prop['chart.yaxispos'] == 'right' && x >= (ca.width - this.gutterRight - 1) ) {
1239
- break;
1240
- }
1241
-
1242
- // If the last tick is not desired...
1243
- if (prop['chart.noendxtick']) {
1244
- if (prop['chart.yaxispos'] == 'left' && x >= (ca.width - this.gutterRight - 1)) {
1245
- break;
1246
- } else if (prop['chart.yaxispos'] == 'right' && x == this.gutterLeft) {
1247
- continue;
1248
- }
1249
- }
1250
-
1251
- var yStart = prop['chart.xaxispos'] === 'center' ? (this.gutterTop + (this.grapharea / 2)) - 3 : ca.height - this.gutterBottom;
1252
- var yEnd = prop['chart.xaxispos'] === 'center' ? yStart + 6 : ca.height - this.gutterBottom - (x % 60 == 0 ? prop['chart.largexticks'] * prop['chart.tickdirection'] : prop['chart.smallxticks'] * prop['chart.tickdirection']);
1253
-
1254
-
1255
- // Draw the tick
1256
- if (prop['chart.ymin'] >= 0 && prop['chart.xaxispos'] === 'bottom') {
1257
- var yStart = this.getYCoord(prop['chart.ymin']) - (prop['chart.ymin'] >= 0 ? 0 : 3),
1258
- yEnd = this.getYCoord(prop['chart.ymin']) + 3;
1259
-
1260
- } else if (prop['chart.xaxispos'] == 'center') {
1261
- var yStart = Math.round((this.gutterTop + (this.grapharea / 2))) - 3,
1262
- yEnd = yStart + 6;
1263
-
1264
- } else if (prop['chart.xaxispos'] == 'bottom') {
1265
-
1266
- var yStart = this.getYCoord(0) - (prop['chart.ymin'] !== 0 ? 3 : 0),
1267
- yEnd = this.getYCoord(0) - (x % 60 == 0 ? prop['chart.largexticks'] * prop['chart.tickdirection'] : prop['chart.smallxticks'] * prop['chart.tickdirection']);
1268
- yEnd += 0;
1269
-
1270
-
1271
- } else if (prop['chart.xaxispos'] == 'top') {
1272
-
1273
- yStart = this.gutterTop - 3;
1274
- yEnd = this.gutterTop;
1275
- }
1276
-
1277
-
1278
- co.moveTo(ma.round(x), yStart);
1279
- co.lineTo(ma.round(x), yEnd);
1280
- }
1281
-
1282
- // Draw an extra tickmark if there is no X axis, but there IS a Y axis
1283
- // OR if there is an offset X axis
1284
- } else if (prop['chart.noyaxis'] == false && prop['chart.numyticks'] > 0) {
1285
-
1286
- if (!prop['chart.noendytick']) {
1287
- if (prop['chart.yaxispos'] == 'left') {
1288
- co.moveTo(this.gutterLeft, Math.round(ca.height - this.gutterBottom));
1289
- co.lineTo(this.gutterLeft - prop['chart.smallyticks'], Math.round(ca.height - this.gutterBottom));
1290
- } else {
1291
- co.moveTo(ca.width - this.gutterRight, Math.round(ca.height - this.gutterBottom));
1292
- co.lineTo(ca.width - this.gutterRight + prop['chart.smallyticks'], Math.round(ca.height - this.gutterBottom));
1293
- }
1294
- }
1295
- }
1296
-
1297
- /**
1298
- * Draw the Y tickmarks
1299
- */
1300
- var numyticks = prop['chart.numyticks'];
1301
-
1302
- if (prop['chart.noyaxis'] == false && numyticks > 0) {
1303
-
1304
- var counter = 0,
1305
- adjustment = 0;
1306
-
1307
- if (prop['chart.yaxispos'] == 'right') {
1308
- adjustment = (ca.width - this.gutterLeft - this.gutterRight);
1309
- }
1310
-
1311
- // X axis at the center
1312
- if (prop['chart.xaxispos'] == 'center') {
1313
- var interval = (this.grapharea / numyticks);
1314
- var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight + prop['chart.smallyticks']);
1315
-
1316
- // Draw the upper halves Y tick marks
1317
- for (y=this.gutterTop; y<(this.grapharea / 2) + this.gutterTop; y+=interval) {
1318
- if (y < (this.grapharea / 2) + this.gutterTop) {
1319
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
1320
- co.lineTo(lineto, Math.round(y));
1321
- }
1322
- }
1323
-
1324
- // Draw the lower halves Y tick marks
1325
- for (y=this.gutterTop + (this.halfgrapharea) + interval; y <= this.grapharea + this.gutterTop; y+=interval) {
1326
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
1327
- co.lineTo(lineto, Math.round(y));
1328
- }
1329
-
1330
- // X axis at the top
1331
- } else if (prop['chart.xaxispos'] == 'top') {
1332
- var interval = (this.grapharea / numyticks);
1333
- var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight + prop['chart.smallyticks']);
1334
-
1335
- // Draw the Y tick marks
1336
- for (y=this.gutterTop + interval; y <= this.grapharea + this.gutterBottom; y+=interval) {
1337
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
1338
- co.lineTo(lineto, Math.round(y));
1339
- }
1340
-
1341
-
1342
- // If there's no X axis draw an extra tick
1343
- if (prop['chart.noxaxis'] && prop['chart.noendytick'] == false) {
1344
- co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), this.gutterTop);
1345
- co.lineTo(lineto, this.gutterTop);
1346
- }
1347
-
1348
- // X axis at the bottom
1349
- } else {
1350
-
1351
- var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight + prop['chart.smallyticks']);
1352
-
1353
- for (y=this.gutterTop;
1354
- y<(ca.height - this.gutterBottom) && counter < numyticks;
1355
- y+=( (ca.height - this.gutterTop - this.gutterBottom) / numyticks)
1356
- ) {
1357
-
1358
- // This check is so that there's no tickmark at
1359
- // the same position as the X axis
1360
- if (ma.round(y) !== ma.round(this.coordsAxes.xaxis[1])) {
1361
- co.moveTo(this.gutterLeft + adjustment, ma.round(y));
1362
- co.lineTo(lineto, ma.round(y));
1363
- }
1364
-
1365
- var counter = counter + 1;
1366
- }
1367
-
1368
- // Draw an extra Y tick if there's an offsetX axis
1369
- if (prop['chart.ymin'] < 0) {
1370
-
1371
- co.moveTo(
1372
- (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight),
1373
- ma.round(y)
1374
- );
1375
-
1376
- co.lineTo(
1377
- lineto,
1378
- ma.round(y)
1379
- );
1380
- }
1381
- }
1382
-
1383
- // Draw an extra X tickmark
1384
- } else if (prop['chart.noxaxis'] == false && prop['chart.numxticks'] > 0) {
1385
-
1386
- if (prop['chart.yaxispos'] == 'left') {
1387
- co.moveTo(this.gutterLeft, prop['chart.xaxispos'] == 'top' ? this.gutterTop : ca.height - this.gutterBottom);
1388
- co.lineTo(this.gutterLeft, prop['chart.xaxispos'] == 'top' ? this.gutterTop - prop['chart.smallxticks'] : ca.height - this.gutterBottom + prop['chart.smallxticks']);
1389
- } else {
1390
- co.moveTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
1391
- co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom + prop['chart.smallxticks']);
1392
- }
1393
- }
1394
-
1395
- co.stroke();
1396
-
1397
- /**
1398
- * This is here so that setting the color after this function doesn't
1399
- * change the color of the axes
1400
- */
1401
- co.beginPath();
1402
- };
1403
-
1404
-
1405
-
1406
-
1407
- /**
1408
- * Draw the text labels for the axes
1409
- */
1410
- this.drawLabels =
1411
- this.DrawLabels = function ()
1412
- {
1413
- co.strokeStyle = 'black';
1414
- co.fillStyle = prop['chart.text.color'];
1415
- co.lineWidth = 1;
1416
-
1417
- // Turn off any shadow
1418
- RG.NoShadow(this);
1419
-
1420
- // This needs to be here
1421
- var font = prop['chart.text.font'];
1422
- var text_size = prop['chart.text.size'];
1423
- var decimals = prop['chart.scale.decimals'];
1424
- var context = co;
1425
- var canvas = ca;
1426
- var ymin = prop['chart.ymin'];
1427
-
1428
- // Draw the Y axis labels
1429
- if (prop['chart.ylabels'] && prop['chart.ylabels.specific'] == null) {
1430
-
1431
- var units_pre = prop['chart.units.pre'];
1432
- var units_post = prop['chart.units.post'];
1433
- var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1434
- var align = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1435
- var numYLabels = this.scale2.labels.length;
1436
- var bounding = false;
1437
- var bgcolor = prop['chart.ylabels.inside'] ? prop['chart.ylabels.inside.color'] : null;
1438
- var offsetx = prop['chart.ylabels.offsetx'];
1439
- var offsety = prop['chart.ylabels.offsety'];
1440
-
1441
-
1442
- /**
1443
- * If the Y labels are inside the Y axis, invert the alignment
1444
- */
1445
- if (prop['chart.ylabels.inside'] == true && align == 'left') {
1446
- xpos -= 10;
1447
- align = 'right';
1448
- bounding = true;
1449
-
1450
-
1451
- } else if (prop['chart.ylabels.inside'] == true && align == 'right') {
1452
- xpos += 10;
1453
- align = 'left';
1454
- bounding = true;
1455
- }
1456
-
1457
-
1458
-
1459
-
1460
- /**
1461
- * X axis in the center
1462
- */
1463
- if (prop['chart.xaxispos'] == 'center') {
1464
-
1465
- var half = this.grapharea / 2;
1466
-
1467
- /**
1468
- * Draw the top half
1469
- */
1470
- for (var i=0; i<this.scale2.labels.length; ++i) {
1471
- RG.text2(this, {
1472
- 'font': font,
1473
- 'size': text_size,
1474
- 'x': xpos + offsetx,
1475
- 'y': this.gutterTop + half - (((i+1)/numYLabels) * half) + offsety,
1476
- 'valign': 'center',
1477
- 'halign':align,
1478
- 'bounding': bounding,
1479
- 'boundingFill': bgcolor,
1480
- 'text': this.scale2.labels[i],
1481
- 'tag': 'scale'
1482
- });
1483
- }
1484
-
1485
- /**
1486
- * Draw the bottom half
1487
- */
1488
- for (var i=0; i<this.scale2.labels.length; ++i) {
1489
- RG.text2(this, {
1490
- 'font': font,
1491
- 'size': text_size,
1492
- 'x': xpos + offsetx,
1493
- 'y': this.gutterTop + half + (((i+1)/numYLabels) * half) + offsety,
1494
- 'valign': 'center',
1495
- 'halign':align,
1496
- 'bounding': bounding,
1497
- 'boundingFill': bgcolor,
1498
- 'text': '-' + this.scale2.labels[i],
1499
- 'tag': 'scale'
1500
- });
1501
- }
1502
-
1503
- // No X axis - so draw 0
1504
- if (prop['chart.noxaxis'] == true || ymin != 0 || prop['chart.scale.zerostart']) {
1505
- RG.text2(this,{
1506
- 'font':font,
1507
- 'size':text_size,
1508
- 'x':xpos + offsetx,
1509
- 'y':this.gutterTop + half + offsety,
1510
- 'text':prop['chart.units.pre'] + ymin.toFixed(decimals) + prop['chart.units.post'],
1511
- 'bounding':bounding,
1512
- 'boundingFill':bgcolor,
1513
- 'valign':'center',
1514
- 'halign':align,
1515
- 'tag': 'scale'
1516
- });
1517
- }
1518
-
1519
-
1520
-
1521
- /**
1522
- * X axis at the top
1523
- */
1524
- } else if (prop['chart.xaxispos'] == 'top') {
1525
-
1526
- var half = this.grapharea / 2;
1527
-
1528
- if (prop['chart.scale.invert']) {
1529
-
1530
- for (var i=0; i<this.scale2.labels.length; ++i) {
1531
-
1532
- RG.text2(this, {
1533
- 'font': font,
1534
- 'size': text_size,
1535
- 'x': xpos + offsetx,
1536
- 'y': this.gutterTop + ((i/this.scale2.labels.length) * this.grapharea) + offsety,
1537
- 'valign': 'center',
1538
- 'halign':align,
1539
- 'bounding': bounding,
1540
- 'boundingFill': bgcolor,
1541
- 'text': '-' + this.scale2.labels[this.scale2.labels.length - (i+1)],
1542
- 'tag': 'scale'
1543
- });
1544
- }
1545
- } else {
1546
- for (var i=0; i<this.scale2.labels.length; ++i) {
1547
- RG.text2(this, {
1548
- 'font': font,
1549
- 'size': text_size,
1550
- 'x': xpos + offsetx,
1551
- 'y': this.gutterTop + (((i+1)/numYLabels) * this.grapharea) + offsety,
1552
- 'valign': 'center',
1553
- 'halign':align,
1554
- 'bounding': bounding,
1555
- 'boundingFill': bgcolor,
1556
- 'text': '-' + this.scale2.labels[i],
1557
- 'tag': 'scale'
1558
- });
1559
- }
1560
- }
1561
-
1562
- // Draw the lower limit if chart.ymin is specified
1563
- if ((prop['chart.ymin'] != 0 || prop['chart.noxaxis']) || prop['chart.scale.invert'] || prop['chart.scale.zerostart']) {
1564
- RG.text2(this, {
1565
- 'font':font,
1566
- 'size':text_size,
1567
- 'x':xpos + offsetx,
1568
- 'y': prop['chart.scale.invert'] ? ca.height - this.gutterBottom + offsety : this.gutterTop + offsety,
1569
- 'text': (prop['chart.ymin'] != 0 ? '-' : '') + RG.number_format(this, prop['chart.ymin'].toFixed(decimals), units_pre, units_post),
1570
- 'valign':'center',
1571
- 'halign': align,
1572
- 'bounding':bounding,
1573
- 'boundingFill':bgcolor,
1574
- 'tag': 'scale'
1575
- });
1576
- }
1577
-
1578
-
1579
-
1580
-
1581
-
1582
-
1583
- /**
1584
- * X axis labels at the bottom
1585
- */
1586
- } else {
1587
-
1588
- if (prop['chart.scale.invert']) {
1589
-
1590
- // Draw the minimum value
1591
- RG.text2(this, {
1592
- 'font': font,
1593
- 'size': text_size,
1594
- 'x': xpos + offsetx,
1595
- 'y': this.gutterTop + offsety,
1596
- 'valign': 'center',
1597
- 'halign':align,
1598
- 'bounding': bounding,
1599
- 'boundingFill': bgcolor,
1600
- 'text': RG.numberFormat(this, this.min.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1601
- 'tag': 'scale'
1602
- });
1603
-
1604
- for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
1605
- RG.Text2(this, {
1606
- 'font': font,
1607
- 'size': text_size,
1608
- 'x': xpos + offsetx,
1609
- 'y': this.gutterTop + (((i+1)/this.scale2.labels.length) * this.grapharea) + offsety,
1610
- 'valign': 'center',
1611
- 'halign':align,
1612
- 'bounding': bounding,
1613
- 'boundingFill': bgcolor,
1614
- 'text': this.scale2.labels[i],
1615
- 'tag': 'scale'
1616
- });
1617
- }
1618
-
1619
- } else {
1620
- for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
1621
- RG.text2(this, {
1622
- 'font': font,
1623
- 'size': text_size,
1624
- 'x': xpos + offsetx,
1625
- 'y': this.gutterTop + ((i/this.scale2.labels.length) * this.grapharea) + offsety,
1626
- 'valign': 'center',
1627
- 'halign':align,
1628
- 'bounding': bounding,
1629
- 'boundingFill': bgcolor,
1630
- 'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
1631
- 'tag': 'scale'
1632
- });
1633
- }
1634
- }
1635
-
1636
- // Draw the lower limit if chart.ymin is specified
1637
- if ( (prop['chart.ymin']!= 0 && !prop['chart.scale.invert'] || prop['chart.scale.zerostart'])
1638
- || prop['chart.noxaxis']
1639
- ) {
1640
-
1641
- RG.text2(this, {
1642
- 'font':font,
1643
- 'size':text_size,
1644
- 'x':xpos + offsetx,
1645
- 'y':prop['chart.scale.invert'] ? this.gutterTop + offsety : ca.height - this.gutterBottom + offsety,
1646
- 'text':RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1647
- 'valign':'center',
1648
- 'halign':align,
1649
- 'bounding':bounding,
1650
- 'boundingFill':bgcolor,
1651
- 'tag': 'scale'
1652
- });
1653
-
1654
- }
1655
- }
1656
-
1657
-
1658
-
1659
-
1660
-
1661
-
1662
-
1663
- // No X axis - so draw 0 - but not if the X axis is in the center
1664
- if ( prop['chart.noxaxis'] == true
1665
- && prop['chart.ymin'] == null
1666
- && prop['chart.xaxispos'] != 'center'
1667
- && prop['chart.noendytick'] == false
1668
- ) {
1669
-
1670
- RG.text2(this, {
1671
- 'font':font,
1672
- 'size':text_size,
1673
- 'x':xpos + offsetx,
1674
- 'y':prop['chart.xaxispos'] == 'top' ? this.gutterTop + offsety : (ca.height - this.gutterBottom),'text': prop['chart.units.pre'] + Number(0).toFixed(prop['chart.scale.decimals']) + prop['chart.units.post'] + offsety,
1675
- 'valign':'center',
1676
- 'halign':align,
1677
- 'bounding':bounding,
1678
- 'boundingFill':bgcolor,
1679
- 'tag':'scale'
1680
- });
1681
- }
1682
-
1683
- } else if (prop['chart.ylabels'] && typeof(prop['chart.ylabels.specific']) == 'object') {
1684
-
1685
- // A few things
1686
- var gap = this.grapharea / prop['chart.ylabels.specific'].length;
1687
- var halign = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1688
- var bounding = false;
1689
- var bgcolor = null;
1690
- var ymin = prop['chart.ymin'] != null && prop['chart.ymin'];
1691
-
1692
- // Figure out the X coord based on the position of the axis
1693
- if (prop['chart.yaxispos'] == 'left') {
1694
- var x = this.gutterLeft - 5;
1695
-
1696
- if (prop['chart.ylabels.inside']) {
1697
- x += 10;
1698
- halign = 'left';
1699
- bounding = true;
1700
- bgcolor = 'rgba(255,255,255,0.5)';
1701
- }
1702
-
1703
- } else if (prop['chart.yaxispos'] == 'right') {
1704
- var x = ca.width - this.gutterRight + 5;
1705
-
1706
- if (prop['chart.ylabels.inside']) {
1707
- x -= 10;
1708
- halign = 'right';
1709
- bounding = true;
1710
- bgcolor = 'rgba(255,255,255,0.5)';
1711
- }
1712
- }
1713
-
1714
- var offsetx = prop['chart.ylabels.offsetx'];
1715
- var offsety = prop['chart.ylabels.offsety'];
1716
-
1717
- // Draw the labels
1718
- if (prop['chart.xaxispos'] == 'center') {
1719
-
1720
-
1721
-
1722
- // Draw the top halfs labels
1723
- for (var i=0; i<prop['chart.ylabels.specific'].length; ++i) {
1724
-
1725
- var y = this.gutterTop + (this.grapharea / (((prop['chart.ylabels.specific'].length - 1)) * 2) * i);
1726
-
1727
- if (ymin && ymin > 0) {
1728
- var y = ((this.grapharea / 2) / (prop['chart.ylabels.specific'].length - (ymin ? 1 : 0)) ) * i;
1729
- y += this.gutterTop;
1730
- }
1731
-
1732
- RG.text2(this, {
1733
- 'font':font,
1734
- 'size':text_size,
1735
- 'x':x + offsetx,
1736
- 'y':y + offsety,
1737
- 'text':String(prop['chart.ylabels.specific'][i]),
1738
- 'valign': 'center',
1739
- 'halign':halign,
1740
- 'bounding':bounding,
1741
- 'boundingFill':bgcolor,
1742
- 'tag': 'ylabels.specific'
1743
- });
1744
- }
1745
-
1746
- // Now reverse the labels and draw the bottom half
1747
- var reversed_labels = RG.array_reverse(prop['chart.ylabels.specific']);
1748
-
1749
- // Draw the bottom halfs labels
1750
- for (var i=0; i<reversed_labels.length; ++i) {
1751
-
1752
- var y = (this.grapharea / 2) + this.gutterTop + ((this.grapharea / ((reversed_labels.length - 1) * 2) ) * i);
1753
-
1754
- RG.text2(this, {
1755
- 'font':font,
1756
- 'size':text_size,
1757
- 'x':x + offsetx,
1758
- 'y':y + offsety,
1759
- 'text':i == 0 ? '' : String(reversed_labels[i]),
1760
- 'valign': 'center',
1761
- 'halign':halign,
1762
- 'bounding':bounding,
1763
- 'boundingFill':bgcolor,
1764
- 'tag': 'ylabels.specific'
1765
- });
1766
- }
1767
-
1768
- } else if (prop['chart.xaxispos'] == 'top') {
1769
-
1770
- // Reverse the labels and draw
1771
- var reversed_labels = RG.array_reverse(prop['chart.ylabels.specific']);
1772
-
1773
- // Draw the bottom halfs labels
1774
- for (var i=0; i<reversed_labels.length; ++i) {
1775
-
1776
- var y = (this.grapharea / (reversed_labels.length - 1)) * i;
1777
- y = y + this.gutterTop;
1778
-
1779
- RG.Text2(this, {
1780
- 'font':font,
1781
- 'size':text_size,
1782
- 'x':x + offsetx,
1783
- 'y':y + offsety,
1784
- 'text':String(reversed_labels[i]),
1785
- 'valign': 'center',
1786
- 'halign':halign,
1787
- 'bounding':bounding,
1788
- 'boundingFill':bgcolor,
1789
- 'tag': 'ylabels.specific'
1790
- });
1791
- }
1792
-
1793
- } else {
1794
- for (var i=0; i<prop['chart.ylabels.specific'].length; ++i) {
1795
- var y = this.gutterTop + ((this.grapharea / (prop['chart.ylabels.specific'].length - 1)) * i);
1796
- RG.text2(this, {
1797
- 'font':font,
1798
- 'size':text_size,
1799
- 'x':x + offsetx,
1800
- 'y':y + offsety,
1801
- 'text':String(prop['chart.ylabels.specific'][i]),
1802
- 'valign':'center',
1803
- 'halign':halign,
1804
- 'bounding':bounding,
1805
- 'boundingFill':bgcolor,
1806
- 'tag': 'ylabels.specific'
1807
- });
1808
- }
1809
- }
1810
- }
1811
-
1812
- // Draw the X axis labels
1813
- if (prop['chart.labels'] && prop['chart.labels'].length > 0) {
1814
-
1815
- var yOffset = 5,
1816
- bordered = false,
1817
- bgcolor = null
1818
-
1819
- co.fillStyle = prop['chart.labels.color'] || prop['chart.text.color'];
1820
-
1821
- /**
1822
- * Text angle
1823
- */
1824
- var angle = 0,
1825
- valign = 'top',
1826
- halign = 'center',
1827
- bold = prop['chart.labels.bold']
1828
-
1829
- if (prop['chart.xlabels.inside']) {
1830
- yOffset = -5;
1831
- bordered = true;
1832
- bgcolor = prop['chart.xlabels.inside.color'];
1833
- valign = 'bottom';
1834
- }
1835
-
1836
- if (prop['chart.xaxispos'] == 'top') {
1837
- valign = 'bottom';
1838
- yOffset += 2;
1839
- }
1840
-
1841
- if (typeof(prop['chart.text.angle']) == 'number' && prop['chart.text.angle'] > 0) {
1842
- angle = -1 * prop['chart.text.angle'];
1843
- valign = 'center';
1844
- halign = 'right';
1845
- yOffset = 10;
1846
-
1847
- if (prop['chart.xaxispos'] == 'top') {
1848
- yOffset = 10;
1849
- }
1850
- }
1851
-
1852
- var numLabels = prop['chart.labels'].length,
1853
- offsetx = prop['chart.labels.offsetx'],
1854
- offsety = prop['chart.labels.offsety'];
1855
-
1856
- for (i=0; i<numLabels; ++i) {
1857
-
1858
- // Changed 8th Nov 2010 to be not reliant on the coords
1859
- //if (this.properties['chart.labels'][i] && this.coords && this.coords[i] && this.coords[i][0]) {
1860
- if (prop['chart.labels'][i]) {
1861
-
1862
- var labelX = ((ca.width - this.gutterLeft - this.gutterRight - (2 * prop['chart.hmargin'])) / (numLabels - 1) ) * i;
1863
- labelX += this.gutterLeft + prop['chart.hmargin'];
1864
-
1865
- /**
1866
- * Account for an unrelated number of labels
1867
- */
1868
-
1869
- if (this.data.length === 0 || !this.data[0] || prop['chart.labels'].length != this.data[0].length) {
1870
- labelX = this.gutterLeft + prop['chart.hmargin'] + ((ca.width - this.gutterLeft - this.gutterRight - (2 * prop['chart.hmargin'])) * (i / (prop['chart.labels'].length - 1)));
1871
- }
1872
-
1873
- // This accounts for there only being one point on the chart
1874
- if (!labelX) {
1875
- labelX = this.gutterLeft + prop['chart.hmargin'];
1876
- }
1877
-
1878
- if (prop['chart.xaxispos'] == 'top' && prop['chart.text.angle'] > 0) {
1879
- halign = 'left';
1880
- }
1881
-
1882
- if (prop['chart.text.angle'] != 0) {
1883
- halign = 'right';
1884
- }
1885
-
1886
- RG.Text2(this, {
1887
- 'font':font,
1888
- 'size':text_size,
1889
- 'bold': bold,
1890
- 'x':labelX + offsetx,
1891
- 'y':(prop['chart.xaxispos'] == 'top') ? this.gutterTop - yOffset - (prop['chart.xlabels.inside'] ? -22 : 0) + offsety : (ca.height - this.gutterBottom) + yOffset + offsety,
1892
- 'text':String(prop['chart.labels'][i]),
1893
- 'valign':valign,
1894
- 'halign':halign,
1895
- 'bounding':bordered,
1896
- 'boundingFill':bgcolor,
1897
- 'angle':angle,
1898
- 'tag': 'labels'
1899
- });
1900
- }
1901
- }
1902
-
1903
- }
1904
-
1905
- co.stroke();
1906
- co.fill();
1907
- }
1908
-
1909
-
1910
-
1911
- /**
1912
- * Draws the line
1913
- */
1914
- this.drawLine =
1915
- this.DrawLine = function (lineData, color, fill, linewidth, tickmarks, index)
1916
- {
1917
- // This facilitates the Rise animation (the Y value only)
1918
- if (prop['chart.animation.unfold.y'] && prop['chart.animation.factor'] != 1) {
1919
- for (var i=0; i<lineData.length; ++i) {
1920
- lineData[i] *= prop['chart.animation.factor'];
1921
- }
1922
- }
1923
-
1924
- var penUp = false;
1925
- var yPos = null;
1926
- var xPos = 0;
1927
- co.lineWidth = 1;
1928
- var lineCoords = [];
1929
-
1930
- /**
1931
- * Get the previous line data
1932
- */
1933
- if (index > 0) {
1934
- var prevLineCoords = this.coords2[index - 1];
1935
- }
1936
-
1937
-
1938
- // Work out the X interval
1939
- var xInterval = (ca.width - (2 * prop['chart.hmargin']) - this.gutterLeft - this.gutterRight) / (lineData.length - 1);
1940
-
1941
- // Loop thru each value given, plotting the line
1942
- // (FORMERLY FIRST)
1943
- for (i=0,len=lineData.length; i<len; i+=1) {
1944
-
1945
- var data_point = lineData[i];
1946
-
1947
- /**
1948
- * Get the yPos for the given data point
1949
- */
1950
- var yPos = this.getYCoord(data_point);
1951
-
1952
-
1953
- // Null data points, and a special case for this bug:http://dev.rgraph.net/tests/ymin.html
1954
- if ( lineData[i] == null
1955
- || (prop['chart.xaxispos'] == 'bottom' && lineData[i] < this.min && !prop['chart.outofbounds'])
1956
- || (prop['chart.xaxispos'] == 'center' && lineData[i] < (-1 * this.max) && !prop['chart.outofbounds'])
1957
- || (((lineData[i] < this.min && prop['chart.xaxispos'] !== 'center') || lineData[i] > this.max) && !prop['chart.outofbounds'])) {
1958
-
1959
- yPos = null;
1960
- }
1961
-
1962
- // Not always very noticeable, but it does have an effect
1963
- // with thick lines
1964
- co.lineCap = 'round';
1965
- co.lineJoin = 'round';
1966
-
1967
- // Plot the line if we're at least on the second iteration
1968
- if (i > 0) {
1969
- xPos = xPos + xInterval;
1970
- } else {
1971
- xPos = prop['chart.hmargin'] + this.gutterLeft;
1972
- }
1973
-
1974
- if (prop['chart.animation.unfold.x']) {
1975
- xPos *= prop['chart.animation.factor'];
1976
-
1977
- if (xPos < prop['chart.gutter.left']) {
1978
- xPos = prop['chart.gutter.left'];
1979
- }
1980
- }
1981
-
1982
- /**
1983
- * Add the coords to an array
1984
- */
1985
- this.coords.push([xPos, yPos]);
1986
- lineCoords.push([xPos, yPos]);
1987
- }
1988
-
1989
- co.stroke();
1990
-
1991
- // Store the coords in another format, indexed by line number
1992
- this.coords2[index] = lineCoords;
1993
-
1994
- /**
1995
- * For IE only: Draw the shadow ourselves as ExCanvas doesn't produce shadows
1996
- */
1997
- if (RG.ISOLD && prop['chart.shadow']) {
1998
- this.DrawIEShadow(lineCoords, co.shadowColor);
1999
- }
2000
-
2001
-
2002
-
2003
- /**
2004
- * Now draw the actual line [FORMERLY SECOND]
2005
- */
2006
- co.beginPath();
2007
- // Transparent now as of 11/19/2011
2008
- co.strokeStyle = 'rgba(0,0,0,0)';
2009
- //co.strokeStyle = fill;
2010
- if (fill) {
2011
- co.fillStyle = fill;
2012
- }
2013
-
2014
- var isStepped = prop['chart.stepped'];
2015
- var isFilled = prop['chart.filled'];
2016
-
2017
- if (prop['chart.xaxispos'] == 'top') {
2018
- var xAxisPos = this.gutterTop;
2019
- } else if (prop['chart.xaxispos'] == 'center') {
2020
- var xAxisPos = this.gutterTop + (this.grapharea / 2);
2021
- } else if (prop['chart.xaxispos'] == 'bottom') {
2022
- var xAxisPos = ca.height - this.gutterBottom;
2023
- }
2024
-
2025
-
2026
-
2027
-
2028
- for (var i=0,len=lineCoords.length; i<len; i+=1) {
2029
-
2030
- xPos = lineCoords[i][0];
2031
- yPos = lineCoords[i][1];
2032
- var set = index;
2033
-
2034
- var prevY = (lineCoords[i - 1] ? lineCoords[i - 1][1] : null);
2035
- var isLast = (i + 1) == lineCoords.length;
2036
-
2037
- /**
2038
- * This nullifys values which are out-of-range
2039
- */
2040
- if (!prop['chart.outofbounds'] && (prevY < this.gutterTop || prevY > (ca.height - this.gutterBottom) ) ) {
2041
- penUp = true;
2042
- }
2043
-
2044
- if (i == 0 || penUp || !yPos || !prevY || prevY < this.gutterTop) {
2045
-
2046
- if (prop['chart.filled'] && !prop['chart.filled.range']) {
2047
-
2048
- if (!prop['chart.outofbounds'] || prevY === null || yPos === null) {
2049
- co.moveTo(xPos + 1, xAxisPos);
2050
- }
2051
-
2052
- // This facilitates the X axis being at the top
2053
- // NOTE: Also done below
2054
- if (prop['chart.xaxispos'] == 'top') {
2055
- co.moveTo(xPos + 1, xAxisPos);
2056
- }
2057
-
2058
- if (isStepped && i > 0) {
2059
- co.lineTo(xPos, lineCoords[i - 1][1]);
2060
- }
2061
-
2062
- co.lineTo(xPos, yPos);
2063
-
2064
- } else {
2065
-
2066
- if (RG.ISOLD && yPos == null) {
2067
- // Nada
2068
- } else {
2069
- co.moveTo(xPos + 1, yPos);
2070
- }
2071
- }
2072
-
2073
- if (yPos == null) {
2074
- penUp = true;
2075
-
2076
- } else {
2077
- penUp = false;
2078
- }
2079
-
2080
- } else {
2081
-
2082
- // Draw the stepped part of stepped lines
2083
- if (isStepped) {
2084
- co.lineTo(xPos, lineCoords[i - 1][1]);
2085
- }
2086
-
2087
- if ((yPos >= this.gutterTop && yPos <= (ca.height - this.gutterBottom)) || prop['chart.outofbounds'] ) {
2088
-
2089
- if (isLast && prop['chart.filled'] && !prop['chart.filled.range'] && prop['chart.yaxispos'] == 'right') {
2090
- xPos -= 1;
2091
- }
2092
-
2093
-
2094
- // Added 8th September 2009
2095
- if (!isStepped || !isLast) {
2096
- co.lineTo(xPos, yPos);
2097
-
2098
- if (isFilled && lineCoords[i+1] && lineCoords[i+1][1] == null) {
2099
- co.lineTo(xPos, xAxisPos);
2100
- }
2101
-
2102
- // Added August 2010
2103
- } else if (isStepped && isLast) {
2104
- co.lineTo(xPos,yPos);
2105
- }
2106
-
2107
-
2108
- penUp = false;
2109
- } else {
2110
- penUp = true;
2111
- }
2112
- }
2113
- }
2114
-
2115
- /**
2116
- * Draw a line to the X axis if the chart is filled
2117
- */
2118
- if (prop['chart.filled'] && !prop['chart.filled.range'] && !prop['chart.curvy']) {
2119
-
2120
- // Is this needed ??
2121
- var fillStyle = prop['chart.fillstyle'];
2122
-
2123
- /**
2124
- * Draw the bottom edge of the filled bit using either the X axis or the prevlinedata,
2125
- * depending on the index of the line. The first line uses the X axis, and subsequent
2126
- * lines use the prevLineCoords array
2127
- */
2128
- if (index > 0 && prop['chart.filled.accumulative']) {
2129
-
2130
- co.lineTo(xPos, prevLineCoords ? prevLineCoords[i - 1][1] : (ca.height - this.gutterBottom - 1 + (prop['chart.xaxispos'] == 'center' ? (ca.height - this.gutterTop - this.gutterBottom) / 2 : 0)));
2131
-
2132
- for (var k=(i - 1); k>=0; --k) {
2133
- co.lineTo(k == 0 ? prevLineCoords[k][0] + 1: prevLineCoords[k][0], prevLineCoords[k][1]);
2134
- }
2135
- } else {
2136
-
2137
- // Draw a line down to the X axis
2138
- if (prop['chart.xaxispos'] == 'top') {
2139
- co.lineTo(xPos, prop['chart.gutter.top'] + 1);
2140
- co.lineTo(lineCoords[0][0],prop['chart.gutter.top'] + 1);
2141
- } else if (typeof(lineCoords[i - 1][1]) == 'number') {
2142
-
2143
- var yPosition = prop['chart.xaxispos'] == 'center' ? ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop : this.getYCoord(0);//ca.height - this.gutterBottom;
2144
-
2145
- co.lineTo(xPos,yPosition);
2146
- co.lineTo(lineCoords[0][0],yPosition);
2147
- }
2148
- }
2149
-
2150
- co.fillStyle = !this.hidden(index) ? fill : 'rgba(0,0,0,0)';
2151
-
2152
- co.fill();
2153
- co.beginPath();
2154
-
2155
- }
2156
-
2157
- /**
2158
- * FIXME this may need removing when Chrome is fixed
2159
- * SEARCH TAGS: CHROME SHADOW BUG
2160
- */
2161
- //if (false && RGraph.ISCHROME && prop['chart.shadow'] && prop['chart.chromefix'] && prop['chart.shadow.blur'] > 0) {
2162
- //
2163
- // for (var i=lineCoords.length - 1; i>=0; --i) {
2164
- // if (
2165
- // typeof(lineCoords[i][1]) != 'number'
2166
- // || (typeof(lineCoords[i+1]) == 'object' && typeof(lineCoords[i+1][1]) != 'number')
2167
- // ) {
2168
- // co.moveTo(lineCoords[i][0],lineCoords[i][1]);
2169
- // } else {
2170
- // co.lineTo(lineCoords[i][0],lineCoords[i][1]);
2171
- // }
2172
- // }
2173
- //}
2174
-
2175
- co.stroke();
2176
-
2177
-
2178
- if (prop['chart.backdrop']) {
2179
- this.DrawBackdrop(lineCoords, color);
2180
- }
2181
-
2182
-
2183
-
2184
-
2185
- /**
2186
- * TODO CLIP TRACE
2187
- * By using the clip() method the Trace animation can be updated.
2188
- * NOTE: Needs to be done for the filled part as well
2189
- */
2190
- co.save();
2191
- co.beginPath();
2192
- co.rect(0,0,ca.width * prop['chart.animation.trace.clip'],ca.height);
2193
- co.clip();
2194
-
2195
-
2196
-
2197
-
2198
-
2199
- //
2200
- // Draw errorbars
2201
- //
2202
- if (typeof prop['chart.errorbars'] !== 'null') {
2203
- this.drawErrorbars();
2204
- }
2205
-
2206
-
2207
-
2208
-
2209
- // Now redraw the lines with the correct line width
2210
- this.SetShadow(index);
2211
- this.RedrawLine(lineCoords, color, linewidth, index);
2212
- co.stroke();
2213
- RG.NoShadow(this);
2214
-
2215
-
2216
-
2217
-
2218
-
2219
-
2220
-
2221
- // Draw the tickmarks
2222
- for (var i=0; i<lineCoords.length; ++i) {
2223
-
2224
- i = Number(i);
2225
-
2226
- /**
2227
- * Set the color
2228
- */
2229
- co.strokeStyle = color;
2230
-
2231
-
2232
- if (isStepped && i == (lineCoords.length - 1)) {
2233
- co.beginPath();
2234
- //continue;
2235
- }
2236
-
2237
- if (
2238
- (
2239
- tickmarks != 'endcircle'
2240
- && tickmarks != 'endsquare'
2241
- && tickmarks != 'filledendsquare'
2242
- && tickmarks != 'endtick'
2243
- && tickmarks != 'endtriangle'
2244
- && tickmarks != 'arrow'
2245
- && tickmarks != 'filledarrow'
2246
- )
2247
- || (i == 0 && tickmarks != 'arrow' && tickmarks != 'filledarrow')
2248
- || i == (lineCoords.length - 1)
2249
- ) {
2250
-
2251
- var prevX = (i <= 0 ? null : lineCoords[i - 1][0]);
2252
- var prevY = (i <= 0 ? null : lineCoords[i - 1][1]);
2253
-
2254
- this.DrawTick(
2255
- lineData,
2256
- lineCoords[i][0],
2257
- lineCoords[i][1],
2258
- color,
2259
- false,
2260
- prevX,
2261
- prevY,
2262
- tickmarks,
2263
- i,
2264
- index
2265
- );
2266
-
2267
- // Draws tickmarks on the stepped bits of stepped charts. Takend out 14th July 2010
2268
- //
2269
- //if (this.properties['chart.stepped'] && lineCoords[i + 1] && this.properties['chart.tickmarks'] != 'endsquare' && this.properties['chart.tickmarks'] != 'endcircle' && this.properties['chart.tickmarks'] != 'endtick') {
2270
- // this.DrawTick(lineCoords[i + 1][0], lineCoords[i][1], color);
2271
- //}
2272
- }
2273
- }
2274
-
2275
- co.restore();
2276
-
2277
- // Draw something off canvas to skirt an annoying bug
2278
- co.beginPath();
2279
- co.arc(ca.width + 50000, ca.height + 50000, 2, 0, 6.38, 1);
2280
- };
2281
-
2282
-
2283
-
2284
-
2285
- /**
2286
- * This functions draws a tick mark on the line
2287
- */
2288
- this.drawTick =
2289
- this.DrawTick = function (lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index, dataset)
2290
- {
2291
- // Various conditions mean no tick
2292
- if (this.hidden(dataset)) {
2293
- return;
2294
- } else if (RG.is_null(yPos)) {
2295
- return false;
2296
- } else if ((yPos > (ca.height - this.gutterBottom)) && !prop['chart.outofbounds']) {
2297
- return;
2298
- } else if ((yPos < this.gutterTop) && !prop['chart.outofbounds']) {
2299
- return;
2300
- }
2301
-
2302
- co.beginPath();
2303
-
2304
- var offset = 0;
2305
-
2306
- // Reset the stroke and lineWidth back to the same as what they were when the line was drawm
2307
- // UPDATE 28th July 2011 - the line width is now set to 1
2308
- co.lineWidth = prop['chart.tickmarks.linewidth'] ? prop['chart.tickmarks.linewidth'] : prop['chart.linewidth'];
2309
- co.strokeStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
2310
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
2311
-
2312
- // Cicular tick marks
2313
- if ( tickmarks == 'circle'
2314
- || tickmarks == 'filledcircle'
2315
- || tickmarks == 'endcircle') {
2316
-
2317
- if (tickmarks == 'circle'|| tickmarks == 'filledcircle' || (tickmarks == 'endcircle' && (index == 0 || index == (lineData.length - 1)))) {
2318
- co.beginPath();
2319
- co.arc(xPos + offset, yPos + offset, prop['chart.ticksize'], 0, 360 / (180 / RG.PI), false);
2320
-
2321
- if (tickmarks == 'filledcircle') {
2322
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
2323
- } else {
2324
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : 'white';
2325
- }
2326
-
2327
- co.stroke();
2328
- co.fill();
2329
- }
2330
-
2331
- // Halfheight "Line" style tick marks
2332
- } else if (tickmarks == 'halftick') {
2333
- co.beginPath();
2334
- co.moveTo(Math.round(xPos), yPos);
2335
- co.lineTo(Math.round(xPos), yPos + prop['chart.ticksize']);
2336
-
2337
- co.stroke();
2338
-
2339
- // Tick style tickmarks
2340
- } else if (tickmarks == 'tick') {
2341
- co.beginPath();
2342
- co.moveTo(Math.round(xPos), yPos - prop['chart.ticksize']);
2343
- co.lineTo(Math.round(xPos), yPos + prop['chart.ticksize']);
2344
-
2345
- co.stroke();
2346
-
2347
- // Endtick style tickmarks
2348
- } else if (tickmarks == 'endtick' && (index == 0 || index == (lineData.length - 1))) {
2349
- co.beginPath();
2350
- co.moveTo(Math.round(xPos), yPos - prop['chart.ticksize']);
2351
- co.lineTo(Math.round(xPos), yPos + prop['chart.ticksize']);
2352
-
2353
- co.stroke();
2354
-
2355
- // "Cross" style tick marks
2356
- } else if (tickmarks == 'cross') {
2357
- co.beginPath();
2358
-
2359
- var ticksize = prop['chart.ticksize'];
2360
-
2361
- co.moveTo(xPos - ticksize, yPos - ticksize);
2362
- co.lineTo(xPos + ticksize, yPos + ticksize);
2363
- co.moveTo(xPos + ticksize, yPos - ticksize);
2364
- co.lineTo(xPos - ticksize, yPos + ticksize);
2365
- co.stroke();
2366
-
2367
-
2368
- // Triangle style tick marks
2369
- } else if (tickmarks == 'triangle' || tickmarks == 'filledtriangle' || (tickmarks == 'endtriangle' && (index == 0 || index == (lineData.length - 1)))) {
2370
- co.beginPath();
2371
-
2372
- if (tickmarks == 'filledtriangle') {
2373
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
2374
- } else {
2375
- co.fillStyle = 'white';
2376
- }
2377
-
2378
- co.moveTo(ma.round(xPos - prop['chart.ticksize']), yPos + prop['chart.ticksize']);
2379
- co.lineTo(ma.round(xPos), yPos - prop['chart.ticksize']);
2380
- co.lineTo(ma.round(xPos + prop['chart.ticksize']), yPos + prop['chart.ticksize']);
2381
- co.closePath();
2382
-
2383
- co.stroke();
2384
- co.fill();
2385
-
2386
-
2387
- //
2388
- // A white bordered circle
2389
- //
2390
- } else if (tickmarks == 'borderedcircle' || tickmarks == 'dot') {
2391
-
2392
- co.lineWidth = prop['chart.tickmarks.dot.linewidth'] || 0.00000001;
2393
-
2394
- pa2(co, [
2395
- 'b',
2396
- 'a',xPos, yPos, prop['chart.ticksize'], 0, 360 / (180 / RG.PI), false,
2397
- 'c',
2398
- 'f',prop['chart.tickmarks.dot.fill'] || color,
2399
- 's',prop['chart.tickmarks.dot.stroke'] || color
2400
- ]);
2401
-
2402
- } else if ( tickmarks == 'square'
2403
- || tickmarks == 'filledsquare'
2404
- || (tickmarks == 'endsquare' && (index == 0 || index == (lineData.length - 1)))
2405
- || (tickmarks == 'filledendsquare' && (index == 0 || index == (lineData.length - 1))) ) {
2406
-
2407
- co.fillStyle = 'white';
2408
- co.strokeStyle = co.strokeStyle;
2409
-
2410
- co.beginPath();
2411
- co.rect(Math.round(xPos - prop['chart.ticksize']), Math.round(yPos - prop['chart.ticksize']), prop['chart.ticksize'] * 2, prop['chart.ticksize'] * 2);
2412
-
2413
- // Fillrect
2414
- if (tickmarks == 'filledsquare' || tickmarks == 'filledendsquare') {
2415
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : co.strokeStyle;
2416
- co.rect(Math.round(xPos - prop['chart.ticksize']), Math.round(yPos - prop['chart.ticksize']), prop['chart.ticksize'] * 2, prop['chart.ticksize'] * 2);
2417
-
2418
- } else if (tickmarks == 'square' || tickmarks == 'endsquare') {
2419
- co.fillStyle = isShadow ? prop['chart.shadow.color'] : 'white';
2420
- co.rect(Math.round((xPos - prop['chart.ticksize']) + 1), Math.round((yPos - prop['chart.ticksize']) + 1), (prop['chart.ticksize'] * 2) - 2, (prop['chart.ticksize'] * 2) - 2);
2421
- }
2422
-
2423
- co.stroke();
2424
- co.fill();
2425
-
2426
- /**
2427
- * FILLED arrowhead
2428
- */
2429
- } else if (tickmarks == 'filledarrow') {
2430
-
2431
- var x = Math.abs(xPos - prevX);
2432
- var y = Math.abs(yPos - prevY);
2433
-
2434
- if (yPos < prevY) {
2435
- var a = Math.atan(x / y) + 1.57;
2436
- } else {
2437
- var a = Math.atan(y / x) + 3.14;
2438
- }
2439
-
2440
- co.beginPath();
2441
- co.moveTo(Math.round(xPos), Math.round(yPos));
2442
- co.arc(Math.round(xPos), Math.round(yPos), 7, a - 0.5, a + 0.5, false);
2443
- co.closePath();
2444
-
2445
- co.stroke();
2446
- co.fill();
2447
-
2448
- /**
2449
- * Arrow head, NOT filled
2450
- */
2451
- } else if (tickmarks == 'arrow') {
2452
-
2453
- var orig_linewidth = co.lineWidth;
2454
-
2455
- var x = Math.abs(xPos - prevX);
2456
- var y = Math.abs(yPos - prevY);
2457
-
2458
- co.lineWidth;
2459
-
2460
- if (yPos < prevY) {
2461
- var a = Math.atan(x / y) + 1.57;
2462
- } else {
2463
- var a = Math.atan(y / x) + 3.14;
2464
- }
2465
-
2466
- co.beginPath();
2467
- co.moveTo(Math.round(xPos), Math.round(yPos));
2468
- co.arc(Math.round(xPos), Math.round(yPos), 7, a - 0.5 - (doc.all ? 0.1 : 0.01), a - 0.4, false);
2469
-
2470
- co.moveTo(Math.round(xPos), Math.round(yPos));
2471
- co.arc(Math.round(xPos), Math.round(yPos), 7, a + 0.5 + (doc.all ? 0.1 : 0.01), a + 0.5, true);
2472
- co.stroke();
2473
- co.fill();
2474
-
2475
- // Revert to original lineWidth
2476
- co.lineWidth = orig_linewidth;
2477
-
2478
-
2479
-
2480
-
2481
-
2482
-
2483
-
2484
-
2485
-
2486
-
2487
-
2488
-
2489
-
2490
-
2491
-
2492
- /**
2493
- * Image based tickmark
2494
- */
2495
- // lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index
2496
- } else if (
2497
- typeof tickmarks === 'string' &&
2498
- (
2499
- tickmarks.substr(0, 6) === 'image:' ||
2500
- tickmarks.substr(0, 5) === 'data:' ||
2501
- tickmarks.substr(0, 1) === '/' ||
2502
- tickmarks.substr(0, 3) === '../' ||
2503
- tickmarks.substr(0, 7) === 'images/'
2504
- )
2505
- ) {
2506
-
2507
- var img = new Image();
2508
-
2509
- if (tickmarks.substr(0, 6) === 'image:') {
2510
- img.src = tickmarks.substr(6);
2511
- } else {
2512
- img.src = tickmarks;
2513
- }
2514
-
2515
-
2516
- img.onload = function ()
2517
- {
2518
- if (prop['chart.tickmarks.image.halign'] === 'center') xPos -= (this.width / 2);
2519
- if (prop['chart.tickmarks.image.halign'] === 'right') xPos -= this.width;
2520
-
2521
- if (prop['chart.tickmarks.image.valign'] === 'center') yPos -= (this.height / 2);
2522
- if (prop['chart.tickmarks.image.valign'] === 'bottom') yPos -= this.height;
2523
-
2524
- xPos += prop['chart.tickmarks.image.offsetx'];
2525
- yPos += prop['chart.tickmarks.image.offsety'];
2526
-
2527
- co.drawImage(this, xPos, yPos);
2528
- };
2529
-
2530
-
2531
-
2532
-
2533
-
2534
-
2535
-
2536
-
2537
-
2538
-
2539
-
2540
-
2541
-
2542
- /**
2543
- * Custom tick drawing function
2544
- */
2545
- } else if (typeof(tickmarks) == 'function') {
2546
- tickmarks(this, lineData, lineData[index], index, xPos, yPos, color, prevX, prevY);
2547
- }
2548
- };
2549
-
2550
-
2551
-
2552
-
2553
- /**
2554
- * Draws a filled range if necessary
2555
- */
2556
- this.drawRange =
2557
- this.DrawRange = function ()
2558
- {
2559
- /**
2560
- * Fill the range if necessary
2561
- */
2562
- if (prop['chart.filled.range'] && prop['chart.filled']) {
2563
-
2564
- if (RG.isNull(prop['chart.filled.range.threshold'])) {
2565
- prop['chart.filled.range.threshold'] = this.ymin
2566
- prop['chart.filled.range.threshold.colors'] = [prop['chart.fillstyle'], prop['chart.fillstyle']]
2567
- }
2568
-
2569
- for (var idx=0; idx<2; ++idx) {
2570
-
2571
- var threshold_colors = prop['chart.filled.range.threshold.colors'];
2572
- var y = this.getYCoord(prop['chart.filled.range.threshold'])
2573
-
2574
- co.save();
2575
- if (idx == 0) {
2576
- co.beginPath();
2577
- co.rect(0,0,ca.width,y);
2578
- co.clip();
2579
-
2580
- } else {
2581
-
2582
- co.beginPath();
2583
- co.rect(0,y,ca.width, ca.height);
2584
- co.clip();
2585
- }
2586
-
2587
- co.beginPath();
2588
- co.fillStyle = (idx == 1 ? prop['chart.filled.range.threshold.colors'][1] : prop['chart.filled.range.threshold.colors'][0]);
2589
-
2590
- //co.strokeStyle = prop['chart.fillstyle']; // Strokestyle not used now (10th October 2012)
2591
-
2592
- co.lineWidth = !this.hidden(idx) ? 1 : 0;
2593
- var len = (this.coords.length / 2);
2594
-
2595
-
2596
-
2597
- for (var i=0; i<len; ++i) {
2598
- if (!RG.is_null(this.coords[i][1])) {
2599
- if (i == 0) {
2600
- co.moveTo(this.coords[i][0], this.coords[i][1])
2601
- } else {
2602
- co.lineTo(this.coords[i][0], this.coords[i][1])
2603
- }
2604
- }
2605
- }
2606
-
2607
-
2608
- for (var i=this.coords.length - 1; i>=len; --i) {
2609
- if (RG.is_null(this.coords[i][1])) {
2610
- co.moveTo(this.coords[i][0], this.coords[i][1])
2611
- } else {
2612
- co.lineTo(this.coords[i][0], this.coords[i][1])
2613
- }
2614
- //co.lineTo(this.coords[i][0], this.coords[i][1])
2615
- }
2616
-
2617
-
2618
-
2619
- // Taken out - 10th Oct 2012
2620
- //co.stroke();
2621
-
2622
- co.fill();
2623
- co.restore();
2624
- }
2625
- }
2626
- };
2627
-
2628
-
2629
-
2630
-
2631
- /**
2632
- * Redraws the line with the correct line width etc
2633
- *
2634
- * @param array coords The coordinates of the line
2635
- */
2636
- this.redrawLine =
2637
- this.RedrawLine = function (coords, color, linewidth, index)
2638
- {
2639
-
2640
- if (prop['chart.noredraw'] || prop['chart.filled.range']) {
2641
- return;
2642
- }
2643
-
2644
- co.strokeStyle = (typeof(color) == 'object' && color && color.toString().indexOf('CanvasGradient') == -1 ? color[0] : color);
2645
- co.lineWidth = linewidth;
2646
-
2647
- if (this.hidden(index)) {
2648
- co.strokeStyle = 'rgba(0,0,0,0)';
2649
- }
2650
-
2651
-
2652
-
2653
-
2654
-
2655
-
2656
-
2657
-
2658
- if (!RG.ISOLD && (prop['chart.curvy'] || prop['chart.spline'])) {
2659
- this.DrawCurvyLine(coords, this.hidden(index) ? 'rgba(0,0,0,0)' : color, linewidth, index);
2660
- return;
2661
- }
2662
-
2663
-
2664
-
2665
-
2666
-
2667
-
2668
-
2669
-
2670
- co.beginPath();
2671
-
2672
- var len = coords.length;
2673
- var width = ca.width
2674
- var height = ca.height;
2675
- var penUp = false;
2676
-
2677
- for (var i=0; i<len; ++i) {
2678
-
2679
- var xPos = coords[i][0];
2680
- var yPos = coords[i][1];
2681
-
2682
- if (i > 0) {
2683
- var prevX = coords[i - 1][0];
2684
- var prevY = coords[i - 1][1];
2685
- }
2686
-
2687
-
2688
- if ((
2689
- (i == 0 && coords[i])
2690
- || (yPos < this.gutterTop)
2691
- || (prevY < this.gutterTop)
2692
- || (yPos > (height - this.gutterBottom))
2693
- || (i > 0 && prevX > (width - this.gutterRight))
2694
- || (i > 0 && prevY > (height - this.gutterBottom))
2695
- || prevY == null
2696
- || penUp == true
2697
- ) && (!prop['chart.outofbounds'] || yPos == null || prevY == null) ) {
2698
-
2699
- if (RG.ISOLD && yPos == null) {
2700
- // ...?
2701
- } else {
2702
- co.moveTo(coords[i][0], coords[i][1]);
2703
- }
2704
-
2705
- penUp = false;
2706
-
2707
- } else {
2708
-
2709
- if (prop['chart.stepped'] && i > 0) {
2710
- co.lineTo(coords[i][0], coords[i - 1][1]);
2711
- }
2712
-
2713
- // Don't draw the last bit of a stepped chart. Now DO
2714
- //if (!this.properties['chart.stepped'] || i < (coords.length - 1)) {
2715
- co.lineTo(coords[i][0], coords[i][1]);
2716
- //}
2717
- penUp = false;
2718
- }
2719
- }
2720
-
2721
- /**
2722
- * If two colors are specified instead of one, go over the up bits
2723
- */
2724
- if (prop['chart.colors.alternate'] && typeof(color) == 'object' && color[0] && color[1]) {
2725
- for (var i=1; i<len; ++i) {
2726
-
2727
- var prevX = coords[i - 1][0];
2728
- var prevY = coords[i - 1][1];
2729
-
2730
- if (prevY != null && coords[i][1] != null) {
2731
- co.beginPath();
2732
- co.strokeStyle = color[coords[i][1] < prevY ? 0 : 1];
2733
- co.lineWidth = prop['chart.linewidth'];
2734
- co.moveTo(prevX, prevY);
2735
- co.lineTo(coords[i][0], coords[i][1]);
2736
- co.stroke();
2737
- }
2738
- }
2739
- }
2740
- };
2741
-
2742
-
2743
-
2744
-
2745
- /**
2746
- * This function is used by MSIE only to manually draw the shadow
2747
- *
2748
- * @param array coords The coords for the line
2749
- */
2750
- this.drawIEShadow =
2751
- this.DrawIEShadow = function (coords, color)
2752
- {
2753
- var offsetx = prop['chart.shadow.offsetx'];
2754
- var offsety = prop['chart.shadow.offsety'];
2755
-
2756
- co.lineWidth = prop['chart.linewidth'];
2757
- co.strokeStyle = color;
2758
-
2759
- co.beginPath();
2760
- for (var i=0; i<coords.length; ++i) {
2761
-
2762
- var isNull = RG.isNull(coords[i][1]);
2763
- var prevIsNull = RG.isNull(coords[i-1]) || RG.isNull(coords[i-1][1]);
2764
-
2765
- if (i == 0 || isNull || prevIsNull) {
2766
- if (!isNull) {
2767
- co.moveTo(coords[i][0] + offsetx, coords[i][1] + offsety);
2768
- }
2769
- } else {
2770
- co.lineTo(coords[i][0] + offsetx, coords[i][1] + offsety);
2771
- }
2772
- }
2773
- co.stroke();
2774
- };
2775
-
2776
-
2777
-
2778
-
2779
- /**
2780
- * Draw the backdrop
2781
- */
2782
- this.drawBackdrop =
2783
- this.DrawBackdrop = function (coords, color)
2784
- {
2785
- //var ca = this.canvas;
2786
- //var co = this.context;
2787
- //var prop = this.properties;
2788
-
2789
- var size = prop['chart.backdrop.size'];
2790
- co.lineWidth = size;
2791
- co.globalAlpha = prop['chart.backdrop.alpha'];
2792
- co.strokeStyle = color;
2793
- var yCoords = [];
2794
-
2795
- co.beginPath();
2796
- if (prop['chart.curvy'] && !RG.ISOLD) {
2797
-
2798
- // The DrawSpline function only takes the Y coords so extract them from the coords that have
2799
- // (which are X/Y pairs)
2800
- for (var i=0; i<coords.length; ++i) {
2801
- yCoords.push(coords[i][1])
2802
- }
2803
-
2804
- this.DrawSpline(co, yCoords, color, null);
2805
-
2806
- } else {
2807
- co.moveTo(coords[0][0], coords[0][1]);
2808
- for (var j=1; j<coords.length; ++j) {
2809
- co.lineTo(coords[j][0], coords[j][1]);
2810
- }
2811
- }
2812
- co.stroke();
2813
-
2814
- // Reset the alpha value
2815
- co.globalAlpha = 1;
2816
- RG.NoShadow(this);
2817
- };
2818
-
2819
-
2820
-
2821
-
2822
- /**
2823
- * Returns the linewidth
2824
- */
2825
- this.getLineWidth =
2826
- this.GetLineWidth = function (i)
2827
- {
2828
- var linewidth = prop['chart.linewidth'];
2829
-
2830
- if (typeof(linewidth) == 'number') {
2831
- return linewidth;
2832
-
2833
- } else if (typeof(linewidth) == 'object') {
2834
- if (linewidth[i]) {
2835
- return linewidth[i];
2836
- } else {
2837
- return linewidth[0];
2838
- }
2839
-
2840
- alert('[LINE] Error! chart.linewidth should be a single number or an array of one or more numbers');
2841
- }
2842
- };
2843
-
2844
-
2845
-
2846
-
2847
- /**
2848
- * The getPoint() method - used to get the point the mouse is currently over, if any
2849
- *
2850
- * @param object e The event object
2851
- * @param object OPTIONAL You can pass in the bar object instead of the
2852
- * function getting it from the canvas
2853
- */
2854
- this.getShape =
2855
- this.getPoint = function (e)
2856
- {
2857
- var obj = this,
2858
- RG = RGraph,
2859
- ca = canvas = e.target,
2860
- co = context = this.context,
2861
- prop = this.properties;
2862
-
2863
- var mouseXY = RG.getMouseXY(e),
2864
- mouseX = mouseXY[0],
2865
- mouseY = mouseXY[1];
2866
-
2867
- // This facilitates you being able to pass in the bar object as a parameter instead of
2868
- // the function getting it from the object
2869
- if (arguments[1]) {
2870
- obj = arguments[1];
2871
- }
2872
-
2873
- for (var i=0; i<obj.coords.length; ++i) {
2874
-
2875
- var x = obj.coords[i][0];
2876
- var y = obj.coords[i][1];
2877
-
2878
- // Do this if the hotspot is triggered by the X coord AND the Y coord
2879
- if ( mouseX <= (x + prop['chart.tooltips.hotspot.size'])
2880
- && mouseX >= (x - prop['chart.tooltips.hotspot.size'])
2881
- && mouseY <= (y + prop['chart.tooltips.hotspot.size'])
2882
- && mouseY >= (y - prop['chart.tooltips.hotspot.size'])
2883
- ) {
2884
-
2885
- if (RG.parseTooltipText) {
2886
- var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
2887
- }
2888
-
2889
- // Work out the dataset
2890
- var dataset = 0;
2891
- var idx = i;
2892
- while ((idx + 1) > this.data[dataset].length) {
2893
- idx -= this.data[dataset].length;
2894
- dataset++;
2895
- }
2896
-
2897
- return {0:obj, 1:x, 2:y, 3:i, 'object': obj, 'x': x, 'y': y, 'index': i, 'tooltip': tooltip, 'dataset': dataset, 'index_adjusted': idx};
2898
-
2899
- } else if ( prop['chart.tooltips.hotspot.xonly'] == true
2900
- && mouseX <= (x + prop['chart.tooltips.hotspot.size'])
2901
- && mouseX >= (x - prop['chart.tooltips.hotspot.size'])) {
2902
-
2903
- var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
2904
-
2905
- return {0:obj, 1:x, 2:y, 3:i, 'object': obj, 'x': x, 'y': y, 'index': i, 'tooltip': tooltip};
2906
- }
2907
- }
2908
- };
2909
-
2910
-
2911
-
2912
-
2913
- /**
2914
- * Draws the above line labels
2915
- */
2916
- this.drawAboveLabels =
2917
- this.DrawAboveLabels = function ()
2918
- {
2919
- var size = prop['chart.labels.above.size'],
2920
- font = prop['chart.labels.above.font'] || prop['chart.text.font'],
2921
- units_pre = prop['chart.labels.above.units.pre'],
2922
- units_post = prop['chart.labels.above.units.post'],
2923
- decimals = prop['chart.labels.above.decimals'],
2924
- color = prop['chart.labels.above.color'] || prop['chart.text.color'],
2925
- bgcolor = prop['chart.labels.above.background'] || 'white',
2926
- border = ((
2927
- typeof prop['chart.labels.above.border'] === 'boolean'
2928
- || typeof prop['chart.labels.above.border'] === 'number'
2929
- ) ? prop['chart.labels.above.border'] : true),
2930
- offsety = prop['chart.labels.above.offsety'] + size,
2931
- specific = prop['chart.labels.above.specific'];
2932
-
2933
- // Use this to 'reset' the drawing state
2934
- co.beginPath();
2935
-
2936
- // Don't need to check that chart.labels.above is enabled here, it's been done already
2937
- for (var i=0, len=this.coords.length; i<len; i+=1) {
2938
-
2939
- var coords = this.coords[i];
2940
-
2941
- RG.text2(this, {
2942
- color:color,
2943
- 'font':font,
2944
- 'size':size,
2945
- 'x':coords[0],
2946
- 'y':coords[1] - offsety,
2947
- 'text':(specific && specific[i]) ? specific[i] : (specific ? null : RG.numberFormat(this, typeof decimals === 'number' ? this.data_arr[i].toFixed(decimals) : this.data_arr[i], units_pre, units_post)),
2948
- 'valign':'center',
2949
- 'halign':'center',
2950
- 'bounding':true,
2951
- 'boundingFill':bgcolor,
2952
- 'boundingStroke':border ? 'black' : 'rgba(0,0,0,0)',
2953
- 'tag':'labels.above'
2954
- });
2955
- }
2956
- };
2957
-
2958
-
2959
-
2960
-
2961
- /**
2962
- * Draw a curvy line.
2963
- */
2964
- this.drawCurvyLine =
2965
- this.DrawCurvyLine = function (coords, color, linewidth, index)
2966
- {
2967
- var yCoords = [];
2968
-
2969
- for (var i=0; i<coords.length; ++i) {
2970
- yCoords.push(coords[i][1]);
2971
- }
2972
-
2973
- if (prop['chart.filled']) {
2974
- co.beginPath();
2975
-
2976
- // First, work out the xaxispos
2977
- if (prop['chart.xaxispos'] === 'center') {
2978
- var xaxisY = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
2979
- } else {
2980
- var xaxisY = this.getYCoord(0);
2981
- }
2982
-
2983
-
2984
- co.moveTo(coords[0][0],xaxisY);
2985
- this.drawSpline(co, yCoords, color, index);
2986
-
2987
- if (prop['chart.filled.accumulative'] && index > 0) {
2988
- for (var i=(this.coordsSpline[index - 1].length - 1); i>=0; i-=1) {
2989
- co.lineTo(this.coordsSpline[index - 1][i][0], this.coordsSpline[index - 1][i][1]);
2990
- }
2991
- } else {
2992
- co.lineTo(coords[coords.length - 1][0],xaxisY);
2993
- }
2994
- co.fill();
2995
- }
2996
-
2997
- co.beginPath();
2998
- this.DrawSpline(co, yCoords, color, index);
2999
- co.stroke();
3000
- };
3001
-
3002
-
3003
-
3004
-
3005
- /**
3006
- * When you click on the chart, this method can return the Y value at that point. It works for any point on the
3007
- * chart (that is inside the gutters) - not just points on the Line.
3008
- *
3009
- * @param object e The event object
3010
- */
3011
- this.getValue = function (arg)
3012
- {
3013
- if (arg.length == 2) {
3014
- var mouseX = arg[0];
3015
- var mouseY = arg[1];
3016
- } else {
3017
- var mouseCoords = RG.getMouseXY(arg);
3018
- var mouseX = mouseCoords[0];
3019
- var mouseY = mouseCoords[1];
3020
- }
3021
-
3022
- var obj = this;
3023
- var xaxispos = prop['chart.xaxispos'];
3024
-
3025
- if (mouseY < prop['chart.gutter.top']) {
3026
- return xaxispos == 'bottom' || xaxispos == 'center' ? this.max : this.min;
3027
- } else if (mouseY > (ca.height - prop['chart.gutter.bottom'])) {
3028
- return xaxispos == 'bottom' ? this.min : this.max;
3029
- }
3030
-
3031
- if (prop['chart.xaxispos'] == 'center') {
3032
- var value = (( (obj.grapharea / 2) - (mouseY - prop['chart.gutter.top'])) / obj.grapharea) * (obj.max - obj.min);
3033
- value *= 2;
3034
- value > 0 ? value += this.min : value -= this.min;
3035
- return value;
3036
- } else if (prop['chart.xaxispos'] == 'top') {
3037
- var value = ((obj.grapharea - (mouseY - prop['chart.gutter.top'])) / obj.grapharea) * (obj.max - obj.min);
3038
- value = Math.abs(obj.max - value) * -1;
3039
- return value;
3040
- } else {
3041
- var value = ((obj.grapharea - (mouseY - prop['chart.gutter.top'])) / obj.grapharea) * (obj.max - obj.min)
3042
- value += obj.min;
3043
- return value;
3044
- }
3045
- };
3046
-
3047
-
3048
-
3049
-
3050
- /**
3051
- * Each object type has its own Highlight() function which highlights the appropriate shape
3052
- *
3053
- * @param object shape The shape to highlight
3054
- */
3055
- this.highlight =
3056
- this.Highlight = function (shape)
3057
- {
3058
- if (prop['chart.tooltips.highlight']) {
3059
-
3060
- if (typeof prop['chart.highlight.style'] === 'function') {
3061
- (prop['chart.highlight.style'])(shape);
3062
-
3063
- } else if (prop['chart.highlight.style'] === 'halo') {
3064
-
3065
- var obj = shape.object,
3066
- color = prop['chart.colors'][shape.dataset];
3067
-
3068
- // Clear a space in white first for the tickmark
3069
- RG.path2(obj.context, 'b a % % 13 0 6.2830 false f rgba(255,255,255,0.75)',
3070
- shape.x,
3071
- shape.y
3072
- );
3073
-
3074
- RG.path2(obj.context, 'ga 0.15 b a % % 13 0 6.2830 false f % ga 1',
3075
- shape.x,
3076
- shape.y,
3077
- color
3078
- );
3079
-
3080
- RG.path2(obj.context, 'b a % % 7 0 6.2830 false f white',
3081
- shape.x,
3082
- shape.y
3083
- );
3084
-
3085
- RG.path2(obj.context, 'b a % % 5 0 6.2830 false f %',
3086
- shape.x,
3087
- shape.y,
3088
- color
3089
- );
3090
-
3091
- } else {
3092
- RG.Highlight.Point(this, shape);
3093
- }
3094
- }
3095
- };
3096
-
3097
-
3098
-
3099
-
3100
- /**
3101
- * The getObjectByXY() worker method. Don't call this call:
3102
- *
3103
- * RG.ObjectRegistry.getObjectByXY(e)
3104
- *
3105
- * @param object e The event object
3106
- */
3107
- this.getObjectByXY = function (e)
3108
- {
3109
- //var ca = this.canvas;
3110
- //var prop = this.properties;
3111
- var mouseXY = RG.getMouseXY(e);
3112
-
3113
- // The 5 is so that the cursor doesn't have to be over the graphArea to trigger the hotspot
3114
- if (
3115
- (mouseXY[0] > prop['chart.gutter.left'] - 5)
3116
- && mouseXY[0] < (ca.width - prop['chart.gutter.right'] + 5)
3117
- && mouseXY[1] > (prop['chart.gutter.top'] - 5)
3118
- && mouseXY[1] < (ca.height - prop['chart.gutter.bottom'] + 5)
3119
- ) {
3120
-
3121
- return this;
3122
- }
3123
- };
3124
-
3125
-
3126
-
3127
-
3128
- /**
3129
- * This method handles the adjusting calculation for when the mouse is moved
3130
- *
3131
- * @param object e The event object
3132
- */
3133
- this.adjusting_mousemove =
3134
- this.Adjusting_mousemove = function (e)
3135
- {
3136
- /**
3137
- * Handle adjusting for the Bar
3138
- */
3139
- if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
3140
-
3141
- // Rounding the value to the given number of decimals make the chart step
3142
- var value = Number(this.getValue(e));//.toFixed(this.properties['chart.scale.decimals']);
3143
- var shape = RG.Registry.Get('chart.adjusting.shape');
3144
-
3145
- if (shape) {
3146
-
3147
- RG.Registry.Set('chart.adjusting.shape', shape);
3148
-
3149
- this.original_data[shape['dataset']][shape['index_adjusted']] = Number(value);
3150
-
3151
- RG.redrawCanvas(e.target);
3152
-
3153
- RG.fireCustomEvent(this, 'onadjust');
3154
- }
3155
- }
3156
- };
3157
-
3158
-
3159
-
3160
-
3161
- /**
3162
- * This function can be used when the canvas is clicked on (or similar - depending on the event)
3163
- * to retrieve the relevant Y coordinate for a particular value.
3164
- *
3165
- * @param int value The value to get the Y coordinate for
3166
- */
3167
- this.getYCoord = function (value)
3168
- {
3169
- if (typeof(value) != 'number') {
3170
- return null;
3171
- }
3172
-
3173
- var y;
3174
- var xaxispos = prop['chart.xaxispos'];
3175
-
3176
- // Higher than max
3177
- // Commented out on March 7th 2013 because the tan curve was not showing correctly
3178
- //if (value > this.max) {
3179
- // value = this.max;
3180
- //}
3181
-
3182
- if (xaxispos == 'top') {
3183
-
3184
- // Account for negative numbers
3185
- //if (value < 0) {
3186
- // value = Math.abs(value);
3187
- //}
3188
-
3189
- y = ((value - this.min) / (this.max - this.min)) * this.grapharea;
3190
-
3191
- // Inverted Y labels
3192
- if (prop['chart.scale.invert']) {
3193
- y = this.grapharea - y;
3194
- }
3195
-
3196
- y = y + this.gutterTop
3197
-
3198
- } else if (xaxispos == 'center') {
3199
-
3200
- y = ((value - this.min) / (this.max - this.min)) * (this.grapharea / 2);
3201
- y = (this.grapharea / 2) - y;
3202
- y += this.gutterTop;
3203
-
3204
- } else {
3205
-
3206
- if ((value < this.min || value > this.max) && prop['chart.outofbounds'] == false) {
3207
- return null;
3208
- }
3209
-
3210
- y = ((value - this.min) / (this.max - this.min)) * this.grapharea;
3211
-
3212
- // Inverted Y labels
3213
- if (prop['chart.scale.invert']) {
3214
- y = this.grapharea - y;
3215
- }
3216
-
3217
- y = ca.height - this.gutterBottom - y;
3218
- }
3219
-
3220
- return y;
3221
- };
3222
-
3223
-
3224
-
3225
-
3226
- /**
3227
- * This function positions a tooltip when it is displayed
3228
- *
3229
- * @param obj object The chart object
3230
- * @param int x The X coordinate specified for the tooltip
3231
- * @param int y The Y coordinate specified for the tooltip
3232
- * @param objec tooltip The tooltips DIV element
3233
- */
3234
- this.positionTooltip = function (obj, x, y, tooltip, idx)
3235
- {
3236
-
3237
- var coordX = obj.coords[tooltip.__index__][0];
3238
- var coordY = obj.coords[tooltip.__index__][1];
3239
- var canvasXY = RG.getCanvasXY(obj.canvas);
3240
- var gutterLeft = prop['chart.gutter.left'];
3241
- var gutterTop = prop['chart.gutter.top'];
3242
- var width = tooltip.offsetWidth;
3243
- var height = tooltip.offsetHeight;
3244
- var mouseXY = RG.getMouseXY(window.event);
3245
-
3246
- // Set the top position
3247
- tooltip.style.left = 0;
3248
- tooltip.style.top = window.event.pageY - height - 20 + 'px';
3249
-
3250
- // By default any overflow is hidden
3251
- tooltip.style.overflow = '';
3252
-
3253
- // Reposition the tooltip if at the edges:
3254
-
3255
- // LEFT edge
3256
- if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
3257
- tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + 'px';
3258
-
3259
- // RIGHT edge
3260
- } else if (canvasXY[0] + mouseXY[0] + (width / 2) > doc.body.offsetWidth) {
3261
- tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + 'px';
3262
-
3263
- // Default positioning - CENTERED
3264
- } else {
3265
- tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + 'px';
3266
- }
3267
- };
3268
-
3269
-
3270
-
3271
-
3272
- /**
3273
- * This function draws a curvy line
3274
- *
3275
- * @param object context The 2D context
3276
- * @param array coords The coordinates
3277
- */
3278
- this.drawSpline =
3279
- this.DrawSpline = function (context, coords, color, index)
3280
- {
3281
- this.coordsSpline[index] = [];
3282
- var xCoords = [];
3283
- var gutterLeft = prop['chart.gutter.left'];
3284
- var gutterRight = prop['chart.gutter.right'];
3285
- var hmargin = prop['chart.hmargin'];
3286
- var interval = (ca.width - (gutterLeft + gutterRight) - (2 * hmargin)) / (coords.length - 1);
3287
-
3288
- co.strokeStyle = color;
3289
-
3290
- /**
3291
- * The drawSpline function takes an array of JUST Y coords - not X/Y coords. So the line coords need converting
3292
- * if we've been given X/Y pairs
3293
- */
3294
- for (var i=0,len=coords.length; i<len;i+=1) {
3295
- if (typeof coords[i] == 'object' && coords[i] && coords[i].length == 2) {
3296
- coords[i] = Number(coords[i][1]);
3297
- }
3298
- }
3299
-
3300
-
3301
-
3302
-
3303
- /**
3304
- * Get the Points array in the format we want - first value should be null along with the lst value
3305
- */
3306
- var P = [coords[0]];
3307
- for (var i=0; i<coords.length; ++i) {
3308
- P.push(coords[i]);
3309
- }
3310
- P.push(coords[coords.length - 1] + (coords[coords.length - 1] - coords[coords.length - 2]));
3311
-
3312
- for (var j=1; j<P.length-2; ++j) {
3313
- for (var t=0; t<10; ++t) {
3314
-
3315
- var yCoord = Spline( t/10, P[j-1], P[j], P[j+1], P[j+2] );
3316
-
3317
- xCoords.push(((j-1) * interval) + (t * (interval / 10)) + gutterLeft + hmargin);
3318
-
3319
- co.lineTo(xCoords[xCoords.length - 1], yCoord);
3320
-
3321
-
3322
- if (typeof index == 'number') {
3323
- this.coordsSpline[index].push([xCoords[xCoords.length - 1], yCoord]);
3324
- }
3325
- }
3326
- }
3327
-
3328
-
3329
-
3330
-
3331
-
3332
- // Draw the last section
3333
- co.lineTo(((j-1) * interval) + gutterLeft + hmargin, P[j]);
3334
- if (typeof index == 'number') {
3335
- this.coordsSpline[index].push([((j-1) * interval) + gutterLeft + hmargin, P[j]]);
3336
- }
3337
-
3338
-
3339
-
3340
-
3341
-
3342
-
3343
- function Spline (t, P0, P1, P2, P3)
3344
- {
3345
- return 0.5 * ((2 * P1) +
3346
- ((0-P0) + P2) * t +
3347
- ((2*P0 - (5*P1) + (4*P2) - P3) * (t*t) +
3348
- ((0-P0) + (3*P1)- (3*P2) + P3) * (t*t*t)));
3349
- }
3350
- };
3351
-
3352
-
3353
-
3354
-
3355
- /**
3356
- * This allows for easy specification of gradients
3357
- */
3358
- this.parseColors = function ()
3359
- {
3360
- // Save the original colors so that they can be restored when the canvas is reset
3361
- if (this.original_colors.length === 0) {
3362
- this.original_colors['chart.colors'] = RGraph.array_clone(prop['chart.colors']);
3363
- this.original_colors['chart.fillstyle'] = RGraph.array_clone(prop['chart.fillstyle']);
3364
- this.original_colors['chart.key.colors'] = RGraph.array_clone(prop['chart.key.colors']);
3365
- this.original_colors['chart.background.barcolor1'] = prop['chart.background.barcolor1'];
3366
- this.original_colors['chart.background.barcolor2'] = prop['chart.background.barcolor2'];
3367
- this.original_colors['chart.background.grid.color'] = prop['chart.background.grid.color'];
3368
- this.original_colors['chart.background.color'] = prop['chart.background.color'];
3369
- this.original_colors['chart.text.color'] = prop['chart.text.color'];
3370
- this.original_colors['chart.crosshairs.color'] = prop['chart.crosshairs.color'];
3371
- this.original_colors['chart.annotate.color'] = prop['chart.annotate.color'];
3372
- this.original_colors['chart.title.color'] = prop['chart.title.color'];
3373
- this.original_colors['chart.title.yaxis.color'] = prop['chart.title.yaxis.color'];
3374
- this.original_colors['chart.key.background'] = prop['chart.key.background'];
3375
- this.original_colors['chart.axis.color'] = prop['chart.axis.color'];
3376
- this.original_colors['chart.highlight.fill'] = prop['chart.highlight.fill'];
3377
- }
3378
-
3379
-
3380
-
3381
- for (var i=0; i<prop['chart.colors'].length; ++i) {
3382
- if (typeof(prop['chart.colors'][i]) == 'object' && prop['chart.colors'][i][0] && prop['chart.colors'][i][1]) {
3383
- prop['chart.colors'][i][0] = this.parseSingleColorForGradient(prop['chart.colors'][i][0]);
3384
- prop['chart.colors'][i][1] = this.parseSingleColorForGradient(prop['chart.colors'][i][1]);
3385
- } else {
3386
- prop['chart.colors'][i] = this.parseSingleColorForGradient(prop['chart.colors'][i]);
3387
- }
3388
- }
3389
-
3390
- /**
3391
- * Fillstyle
3392
- */
3393
- if (prop['chart.fillstyle']) {
3394
- if (typeof(prop['chart.fillstyle']) == 'string') {
3395
- prop['chart.fillstyle'] = this.parseSingleColorForGradient(prop['chart.fillstyle'], 'vertical');
3396
- } else {
3397
- for (var i=0; i<prop['chart.fillstyle'].length; ++i) {
3398
- prop['chart.fillstyle'][i] = this.parseSingleColorForGradient(prop['chart.fillstyle'][i], 'vertical');
3399
- }
3400
- }
3401
- }
3402
-
3403
- /**
3404
- * Key colors
3405
- */
3406
- if (!RG.is_null(prop['chart.key.colors'])) {
3407
- for (var i=0; i<prop['chart.key.colors'].length; ++i) {
3408
- prop['chart.key.colors'][i] = this.parseSingleColorForGradient(prop['chart.key.colors'][i]);
3409
- }
3410
- }
3411
-
3412
- /**
3413
- * Parse various properties for colors
3414
- */
3415
- var properties = [
3416
- 'chart.background.barcolor1',
3417
- 'chart.background.barcolor2',
3418
- 'chart.background.grid.color',
3419
- 'chart.background.color',
3420
- 'chart.text.color',
3421
- 'chart.crosshairs.color',
3422
- 'chart.annotate.color',
3423
- 'chart.title.color',
3424
- 'chart.title.yaxis.color',
3425
- 'chart.key.background',
3426
- 'chart.axis.color',
3427
- 'chart.highlight.fill'
3428
- ];
3429
-
3430
- for (var i=0; i<properties.length; ++i) {
3431
- prop[properties[i]] = this.parseSingleColorForGradient(prop[properties[i]]);
3432
- }
3433
- };
3434
-
3435
-
3436
-
3437
-
3438
- /**
3439
- * Use this function to reset the object to the post-constructor state. Eg reset colors if
3440
- * need be etc
3441
- */
3442
- this.reset = function ()
3443
- {
3444
- };
3445
-
3446
-
3447
-
3448
-
3449
- /**
3450
- * This parses a single color value
3451
- */
3452
- this.parseSingleColorForGradient = function (color)
3453
- {
3454
- if (!color || typeof(color) != 'string') {
3455
- return color;
3456
- }
3457
-
3458
- /**
3459
- * Horizontal or vertical gradient
3460
- */
3461
- var dir = typeof(arguments[1]) == 'string' ? arguments[1] : 'vertical';
3462
-
3463
- if (typeof color === 'string' && color.match(/^gradient\((.*)\)$/i)) {
3464
-
3465
- var parts = RegExp.$1.split(':');
3466
-
3467
- // Create the gradient
3468
- if (dir == 'horizontal') {
3469
- var grad = co.createLinearGradient(0,0,ca.width,0);
3470
- } else {
3471
- var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'],0,prop['chart.gutter.top']);
3472
- }
3473
-
3474
- var diff = 1 / (parts.length - 1);
3475
-
3476
- grad.addColorStop(0, RG.trim(parts[0]));
3477
-
3478
- for (var j=1; j<parts.length; ++j) {
3479
- grad.addColorStop(j * diff, RG.trim(parts[j]));
3480
- }
3481
- }
3482
-
3483
- return grad ? grad : color;
3484
- };
3485
-
3486
-
3487
-
3488
-
3489
- /**
3490
- * Sets the appropriate shadow
3491
- */
3492
- this.setShadow =
3493
- this.SetShadow = function (i)
3494
- {
3495
- //var ca = this.canvas;
3496
- //var co = this.context;
3497
- //var prop = this.properties;
3498
-
3499
- if (prop['chart.shadow']) {
3500
- /**
3501
- * Handle the appropriate shadow color. This now facilitates an array of differing
3502
- * shadow colors
3503
- */
3504
- var shadowColor = prop['chart.shadow.color'];
3505
-
3506
- /**
3507
- * Accommodate an array of shadow colors as well as a single string
3508
- */
3509
- if (typeof(shadowColor) == 'object' && shadowColor[i - 1]) {
3510
- co.shadowColor = shadowColor[i];
3511
-
3512
- } else if (typeof(shadowColor) == 'object') {
3513
- co.shadowColor = shadowColor[0];
3514
-
3515
- } else if (typeof(shadowColor) == 'string') {
3516
- co.shadowColor = shadowColor;
3517
- }
3518
-
3519
- co.shadowBlur = prop['chart.shadow.blur'];
3520
- co.shadowOffsetX = prop['chart.shadow.offsetx'];
3521
- co.shadowOffsetY = prop['chart.shadow.offsety'];
3522
- }
3523
- };
3524
-
3525
-
3526
-
3527
-
3528
- /**
3529
- * This function handles highlighting an entire data-series for the interactive
3530
- * key
3531
- *
3532
- * @param int index The index of the data series to be highlighted
3533
- */
3534
- this.interactiveKeyHighlight = function (index)
3535
- {
3536
- var coords = this.coords2[index];
3537
-
3538
- if (coords) {
3539
-
3540
- var pre_linewidth = co.lineWidth;
3541
- var pre_linecap = co.lineCap;
3542
-
3543
- co.lineWidth = prop['chart.linewidth'] + 10;
3544
- co.lineCap = 'round';
3545
- co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
3546
-
3547
-
3548
- co.beginPath();
3549
- if (prop['chart.curvy']) {
3550
- this.DrawSpline(co, coords, prop['chart.key.interactive.highlight.chart'], null);
3551
- } else {
3552
- for (var i=0,len=coords.length; i<len; i+=1) {
3553
- if ( i == 0
3554
- || RG.is_null(coords[i][1])
3555
- || (typeof coords[i - 1][1] != undefined && RG.is_null(coords[i - 1][1]))) {
3556
- co.moveTo(coords[i][0], coords[i][1]);
3557
- } else {
3558
- co.lineTo(coords[i][0], coords[i][1]);
3559
- }
3560
- }
3561
- }
3562
- co.stroke();
3563
-
3564
- // Reset the lineCap and lineWidth
3565
- co.lineWidth = pre_linewidth;
3566
- co.lineCap = pre_linecap;
3567
- }
3568
- };
3569
-
3570
-
3571
-
3572
-
3573
- /**
3574
- * Using a function to add events makes it easier to facilitate method chaining
3575
- *
3576
- * @param string type The type of even to add
3577
- * @param function func
3578
- */
3579
- this.on = function (type, func)
3580
- {
3581
- if (type.substr(0,2) !== 'on') {
3582
- type = 'on' + type;
3583
- }
3584
-
3585
- this[type] = func;
3586
-
3587
- return this;
3588
- };
3589
-
3590
-
3591
-
3592
-
3593
- /**
3594
- * This function runs once only
3595
- * (put at the end of the file (before any effects))
3596
- */
3597
- this.firstDrawFunc = function ()
3598
- {
3599
- };
3600
-
3601
-
3602
-
3603
-
3604
- //
3605
- // Draws error-bars for the Bar and Line charts
3606
- //
3607
- this.drawErrorbars = function ()
3608
- {
3609
- // Save the state of the canvas so that it can be restored at the end
3610
- co.save();
3611
-
3612
- RG.noShadow(this);
3613
-
3614
- var coords = this.coords,
3615
- x = 0,
3616
- errorbars = prop['chart.errorbars'],
3617
- length = 0;
3618
-
3619
- // If not capped set the width of the cap to zero
3620
- if (!prop['chart.errorbars.capped']) {
3621
- prop['chart.errorbars.capped.width'] = 0.001;
3622
- halfwidth = 0.0005;
3623
- }
3624
-
3625
- // Set the linewidth
3626
- co.lineWidth = prop['chart.errorbars.linewidth'];
3627
-
3628
-
3629
-
3630
-
3631
- for (var i=0; i<coords.length; ++i) {
3632
-
3633
- var halfwidth = prop['chart.errorbars.capped.width'] / 2 || 5,
3634
- color = prop['chart.errorbars.color'] || 'black';
3635
-
3636
- // Set the perbar linewidth if the fourth option in the array
3637
- // is specified
3638
- if (errorbars[i] && typeof errorbars[i][3] === 'number') {
3639
- co.lineWidth = errorbars[i][3];
3640
- } else if (typeof prop['chart.errorbars.linewidth'] === 'number') {
3641
- co.lineWidth = prop['chart.errorbars.linewidth'];
3642
- } else {
3643
- co.lineWidth = 1;
3644
- }
3645
-
3646
-
3647
-
3648
- // Calulate the pixel size
3649
- if (typeof errorbars === 'number' || typeof errorbars[i] === 'number') {
3650
-
3651
- if (typeof errorbars === 'number') {
3652
- var positiveLength = this.getYCoord(this.min) - this.getYCoord(this.min + errorbars),
3653
- negativeLength = positiveLength;
3654
- } else {
3655
- var positiveLength = this.getYCoord(this.min) - this.getYCoord(this.min + errorbars[i]),
3656
- negativeLength = positiveLength;
3657
- }
3658
-
3659
- if (positiveLength || negativeLength) {
3660
-
3661
- pa2(
3662
- co,
3663
- 'lj miter lc square b m % % l % % m % % l % % l % % m % % l % % s %',
3664
- coords[i][0] - halfwidth,
3665
- coords[i][1] + negativeLength,
3666
- coords[i][0] + halfwidth,
3667
- coords[i][1] + negativeLength,
3668
- coords[i][0],
3669
- coords[i][1] + negativeLength,
3670
- coords[i][0],
3671
- coords[i][1] - positiveLength,
3672
- coords[i][0] - halfwidth,
3673
- coords[i][1] - positiveLength,
3674
- coords[i][0],
3675
- coords[i][1] - positiveLength,
3676
- coords[i][0] + halfwidth,
3677
- coords[i][1] - positiveLength,
3678
- color
3679
- );
3680
-
3681
- pa2(
3682
- co,
3683
- 'lj miter lc square b m % % l % % s %',
3684
- coords[i][0] - halfwidth,
3685
- coords[i][1] + negativeLength,
3686
- coords[i][0] + halfwidth,
3687
- coords[i][1] + negativeLength,
3688
- color
3689
- );
3690
- }
3691
-
3692
-
3693
-
3694
- } else if (typeof errorbars[i] === 'object' && !RG.isNull(errorbars[i])) {
3695
-
3696
- var positiveLength = this.getYCoord(this.min) - this.getYCoord(this.min + errorbars[i][0]),
3697
- negativeLength = this.getYCoord(this.min) - this.getYCoord(this.min + errorbars[i][1]);
3698
-
3699
-
3700
- // Color
3701
- if (typeof errorbars[i][2] === 'string') {
3702
- color = errorbars[i][2];
3703
- }
3704
-
3705
- // Cap width
3706
- halfwidth = typeof errorbars[i][4] === 'number' ? errorbars[i][4] / 2 : halfwidth;
3707
-
3708
-
3709
- // Set the linewidth
3710
- if (typeof errorbars[i] === 'object' && typeof errorbars[i][3] === 'number') {
3711
- co.lineWidth = errorbars[i][3];
3712
- } else if (typeof prop['chart.errorbars.linewidth'] === 'number') {
3713
- co.lineWidth = prop['chart.errorbars.linewidth'];
3714
- } else {
3715
- co.lineWidth = 1;
3716
- }
3717
-
3718
-
3719
- if (!RG.isNull(errorbars[i][0])) {
3720
-
3721
- pa2(
3722
- co,
3723
- 'lc square b m % % l % % l % % m % % l % % s %',
3724
- coords[i][0],
3725
- coords[i][1],
3726
- coords[i][0],
3727
- coords[i][1] - positiveLength,
3728
- coords[i][0] - halfwidth,
3729
- ma.round(coords[i][1] - positiveLength),
3730
- coords[i][0],
3731
- ma.round(coords[i][1] - positiveLength),
3732
- coords[i][0] + halfwidth,
3733
- ma.round(coords[i][1] - positiveLength),
3734
- color
3735
- );
3736
- }
3737
-
3738
- if (typeof errorbars[i][1] === 'number') {
3739
-
3740
- var negativeLength = ma.abs(this.getYCoord(errorbars[i][1]) - this.getYCoord(0));
3741
-
3742
- pa2(
3743
- co,
3744
- 'b m % % l % % l % % m % % l % % s %',
3745
- coords[i][0],
3746
- coords[i][1],
3747
- coords[i][0],
3748
- coords[i][1] + negativeLength,
3749
- coords[i][0] - halfwidth,
3750
- ma.round(coords[i][1] + negativeLength),
3751
- coords[i][0],
3752
- ma.round(coords[i][1] + negativeLength),
3753
- coords[i][0] + halfwidth,
3754
- ma.round(coords[i][1] + negativeLength),
3755
- color
3756
- );
3757
- }
3758
- }
3759
- }
3760
-
3761
- co.restore();
3762
- };
3763
-
3764
-
3765
-
3766
-
3767
- /**
3768
- * Hides a line by setting the appropriate flag so that the .visible(index)
3769
- * function returns the relevant result.
3770
- *
3771
- * @param int index The index of the line to hide
3772
- */
3773
- this.hide = function ()
3774
- {
3775
- // Hide a single line
3776
- if (typeof arguments[0] === 'number') {
3777
- prop['chart.line.visible'][arguments[0]] = false;
3778
-
3779
- // Hide multiple lines
3780
- } else if (typeof arguments[0] === 'object') {
3781
- for (var i=0; i<arguments[0].length; ++i) {
3782
- prop['chart.line.visible'][arguments[0][i]] = false;
3783
- }
3784
-
3785
- // Hide all lines
3786
- } else {
3787
- for (var i=0; i<this.original_data.length; ++i) {
3788
- prop['chart.line.visible'][i] = false;
3789
- }
3790
- }
3791
-
3792
- RG.redraw();
3793
-
3794
- // Facilitate chaining
3795
- return this;
3796
- };
3797
-
3798
-
3799
-
3800
-
3801
- /**
3802
- * Shows a line by setting the appropriate flag so that the .visible(index)
3803
- * function returns the relevant result.
3804
- *
3805
- * @param int index The index of the line to show
3806
- */
3807
- this.show = function ()
3808
- {
3809
- // Show a single line
3810
- if (typeof arguments[0] === 'number') {
3811
- prop['chart.line.visible'][arguments[0]] = true;
3812
-
3813
- // Show multiple lines
3814
- } else if (typeof arguments[0] === 'object') {
3815
- for (var i=0; i<arguments[0].length; ++i) {
3816
- prop['chart.line.visible'][arguments[0][i]] = true;
3817
- }
3818
-
3819
- // Show all lines
3820
- } else {
3821
- for (var i=0; i<this.original_data.length; ++i) {
3822
- prop['chart.line.visible'][i] = true;
3823
- }
3824
- }
3825
-
3826
- RG.redraw();
3827
-
3828
- // Facilitate chaining
3829
- return this;
3830
- };
3831
-
3832
-
3833
-
3834
-
3835
- /**
3836
- * Returns true/false as to wether a line is hidden or not
3837
- *
3838
- * @param int index The index of the line to hide
3839
- */
3840
- this.hidden = function (index)
3841
- {
3842
- return !prop['chart.line.visible'][index];
3843
- };
3844
-
3845
-
3846
-
3847
- /**
3848
- * Unfold
3849
- *
3850
- * This effect gradually increases the X/Y coordinatesfrom 0
3851
- *
3852
- * @param object obj The chart object
3853
- */
3854
- this.unfold = function ()
3855
- {
3856
- var obj = this;
3857
- var opt = arguments[0] ? arguments[0] : {};
3858
- var frames = opt.frames ? opt.frames : 30;
3859
- var frame = 0;
3860
- var callback = arguments[1] ? arguments[1] : function () {};
3861
- var initial = prop['chart.animation.unfold.initial'];
3862
-
3863
- prop['chart.animation.factor'] = prop['chart.animation.unfold.initial'];
3864
-
3865
- function iterator ()
3866
- {
3867
- prop['chart.animation.factor'] = ((1 - initial) * (frame / frames)) + initial;
3868
-
3869
- RG.clear(obj.canvas);
3870
- RG.redrawCanvas(obj.canvas);
3871
-
3872
- if (frame < frames) {
3873
- frame++;
3874
- RG.Effects.updateCanvas(iterator);
3875
- } else {
3876
- callback(obj);
3877
- }
3878
- }
3879
-
3880
-
3881
- iterator();
3882
-
3883
- return this;
3884
- };
3885
-
3886
-
3887
-
3888
-
3889
- /**
3890
- * Trace2
3891
- *
3892
- * This is a new version of the Trace effect which no longer requires jQuery and is more compatible
3893
- * with other effects (eg Expand). This new effect is considerably simpler and less code.
3894
- *
3895
- * @param object Options for the effect. Currently only "frames" is available.
3896
- * @param int A function that is called when the ffect is complete
3897
- */
3898
- this.trace =
3899
- this.trace2 = function ()
3900
- {
3901
- var obj = this;
3902
- var callback = arguments[2];
3903
- var opt = arguments[0] || {};
3904
- var frames = opt.frames || 30;
3905
- var frame = 0;
3906
- var callback = arguments[1] || function () {};
3907
-
3908
- obj.Set('animation.trace.clip', 0);
3909
-
3910
- function iterator ()
3911
- {
3912
- RG.clear(obj.canvas);
3913
-
3914
- RG.redrawCanvas(obj.canvas);
3915
-
3916
- if (frame++ < frames) {
3917
- obj.Set('animation.trace.clip', frame / frames);
3918
- RG.Effects.updateCanvas(iterator);
3919
- } else {
3920
- callback(obj);
3921
- }
3922
- }
3923
-
3924
- iterator();
3925
-
3926
- return this;
3927
- };
3928
-
3929
-
3930
-
3931
-
3932
- /**
3933
- * FoldToCenter
3934
- *
3935
- * Line chart FoldTocenter
3936
- *
3937
- * @param object OPTIONAL An object map of options
3938
- * @param function OPTIONAL A callback to run when the effect is complete
3939
- */
3940
- this.foldtocenter =
3941
- this.foldToCenter = function ()
3942
- {
3943
- var obj = this;
3944
- var opt = arguments[0] || {};
3945
- var frames = opt.frames || 30;
3946
- var frame = 0;
3947
- var callback = arguments[1] || function () {};
3948
- var center_value = obj.scale2.max / 2;
3949
-
3950
- obj.Set('chart.ymax', obj.scale2.max);
3951
-
3952
- var original_data = RG.array_clone(obj.original_data);
3953
-
3954
- function iterator ()
3955
- {
3956
- for (var i=0,len=obj.data.length; i<len; ++i) {
3957
- if (obj.data[i].length) {
3958
- for (var j=0,len2=obj.data[i].length; j<len2; ++j) {
3959
-
3960
- var dataset = obj.original_data[i];
3961
-
3962
- if (dataset[j] > center_value) {
3963
- dataset[j] = original_data[i][j] - ((original_data[i][j] - center_value) * (frame / frames));
3964
- } else {
3965
- dataset[j] = original_data[i][j] + (((center_value - original_data[i][j]) / frames) * frame);
3966
- }
3967
- }
3968
- }
3969
- }
3970
-
3971
- RG.clear(obj.canvas);
3972
- RG.redrawCanvas(obj.canvas)
3973
-
3974
- if (frame++ < frames) {
3975
- RG.Effects.updateCanvas(iterator);
3976
- } else {
3977
- callback(obj);
3978
- }
3979
- }
3980
-
3981
-
3982
-
3983
- iterator();
3984
-
3985
-
3986
-
3987
- return this;
3988
- };
3989
-
3990
-
3991
-
3992
-
3993
- /**
3994
- * UnfoldFromCenterTrace effect
3995
- *
3996
- * @param object An object containing options
3997
- * @param function A callback function
3998
- */
3999
- this.unfoldFromCenterTrace =
4000
- this.unfoldFromCenterTrace2 = function ()
4001
- {
4002
- var obj = this,
4003
- opt = arguments[0] || {},
4004
- frames = opt.frames || 30,
4005
- frame = 0,
4006
- data = RG.arrayClone(obj.original_data),
4007
- callback = arguments[1] || function () {};
4008
-
4009
-
4010
-
4011
- // Draw the chart once to get the scale values
4012
- obj.canvas.style.visibility = 'hidden';
4013
- obj.draw();
4014
- var max = obj.scale2.max;
4015
- RG.clear(obj.canvas);
4016
- obj.canvas.style.visibility = 'visible';
4017
-
4018
-
4019
-
4020
-
4021
- /**
4022
- * When the Trace function finishes it calls this function
4023
- */
4024
- var unfoldCallback = function ()
4025
- {
4026
- obj.original_data = data;
4027
- obj.unfoldFromCenter({frames: frames / 2}, callback);
4028
- };
4029
-
4030
-
4031
-
4032
- /**
4033
- * Determine the mid-point
4034
- */
4035
- var half = obj.Get('chart.xaxispos') == 'center' ? obj.min : ((obj.max - obj.min) / 2) + obj.min;
4036
- obj.Set('chart.ymax', obj.max);
4037
-
4038
- for (var i=0,len=obj.original_data.length; i<len; ++i) {
4039
- for (var j=0; j<obj.original_data[i].length; ++j) {
4040
- obj.original_data[i][j] = (obj.Get('chart.filled') && obj.Get('chart.filled.accumulative') && i > 0) ? 0 : half;
4041
- }
4042
- }
4043
-
4044
- RG.clear(obj.canvas);
4045
- obj.trace2({frames: frames / 2}, unfoldCallback);
4046
-
4047
- return obj;
4048
- };
4049
-
4050
-
4051
-
4052
-
4053
- /**
4054
- * UnfoldFromCenter
4055
- *
4056
- * Line chart unfold from center
4057
- *
4058
- * @param object An option map of properties. Only frames is supported: {frames: 30}
4059
- * @param function An optional callback
4060
- */
4061
- this.unfoldFromCenter = function ()
4062
- {
4063
- var obj = this;
4064
- var opt = arguments[0] || {};
4065
- var frames = opt.frames || 30;
4066
- var frame = 0;
4067
- var callback = arguments[1] || function () {};
4068
-
4069
- // Draw the chart once to get the scale values
4070
- obj.canvas.style.visibility = 'hidden';
4071
- obj.Draw();
4072
- var max = obj.scale2.max;
4073
- RG.clear(obj.canvas);
4074
- obj.canvas.style.visibility = 'visible';
4075
-
4076
- var center_value = obj.Get('chart.xaxispos') === 'center' ? prop['chart.ymin'] : ((obj.max - obj.min) / 2) + obj.min;
4077
- var original_data = RG.array_clone(obj.original_data);
4078
- var steps = null;
4079
-
4080
- obj.Set('chart.ymax', max);
4081
-
4082
- if (!steps) {
4083
-
4084
- steps = [];
4085
-
4086
- for (var dataset=0,len=original_data.length; dataset<len; ++dataset) {
4087
-
4088
- steps[dataset] = []
4089
-
4090
- for (var i=0,len2=original_data[dataset].length; i<len2; ++i) {
4091
- if (prop['chart.filled'] && prop['chart.filled.accumulative'] && dataset > 0) {
4092
- steps[dataset][i] = original_data[dataset][i] / frames;
4093
- obj.original_data[dataset][i] = center_value;
4094
- } else {
4095
- steps[dataset][i] = (original_data[dataset][i] - center_value) / frames;
4096
- obj.original_data[dataset][i] = center_value;
4097
- }
4098
- }
4099
- }
4100
- }
4101
-
4102
- function unfoldFromCenter ()
4103
- {
4104
- for (var dataset=0; dataset<original_data.length; ++dataset) {
4105
- for (var i=0; i<original_data[dataset].length; ++i) {
4106
- obj.original_data[dataset][i] += steps[dataset][i];
4107
- }
4108
- }
4109
-
4110
- RG.clear(obj.canvas);
4111
- RG.redrawCanvas(obj.canvas);
4112
-
4113
- if (--frames > 0) {
4114
- RG.Effects.updateCanvas(unfoldFromCenter);
4115
- } else {
4116
- obj.original_data = RG.array_clone(original_data);
4117
- RG.clear(obj.canvas);
4118
- RG.redrawCanvas(obj.canvas);
4119
-
4120
- callback(obj);
4121
- }
4122
- }
4123
-
4124
- unfoldFromCenter();
4125
-
4126
- return this;
4127
- };
4128
-
4129
-
4130
-
4131
-
4132
-
4133
-
4134
-
4135
-
4136
- RG.att(ca);
4137
-
4138
-
4139
-
4140
-
4141
- /**
4142
- * Register the object so it is redrawn when necessary
4143
- */
4144
- RG.Register(this);
4145
-
4146
-
4147
-
4148
-
4149
- /**
4150
- * This is the 'end' of the constructor so if the first argument
4151
- * contains configuration data - handle that.
4152
- */
4153
- if (parseConfObjectForOptions) {
4154
- RG.parseObjectStyleConfig(this, conf.options);
4155
- }
4156
-
4157
- /**
4158
- * Allow all lines to start off as visible
4159
- */
4160
- for (var i=0; i<this.original_data.length; ++i) {
4161
- prop['chart.line.visible'][i] = true;
4162
- }
4163
- };
2
+ RGraph=window.RGraph||{isRGraph:true};RGraph.Line=function(conf)
3
+ {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var id=conf.id;var canvas=document.getElementById(id);var data=conf.data;var parseConfObjectForOptions=true;}else{var id=conf;var canvas=document.getElementById(id);var data=arguments[1];}
4
+ this.id=id;this.canvas=canvas;this.context=this.canvas.getContext('2d');this.canvas.__object__=this;this.type='line';this.max=0;this.coords=[];this.coords2=[];this.coords.key=[];this.coordsText=[];this.coordsSpline=[];this.coordsAxes={xaxis:[],yaxis:[]};this.hasnegativevalues=false;this.isRGraph=true;this.uid=RGraph.CreateUID();this.canvas.uid=this.canvas.uid?this.canvas.uid:RGraph.CreateUID();this.colorsParsed=false;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':1,'chart.background.grid.width':1,'chart.background.grid.hsize':25,'chart.background.grid.vsize':25,'chart.background.grid.color':'#ddd','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':null,'chart.background.grid.dashed':false,'chart.background.grid.dotted':false,'chart.background.hbars':null,'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.labels':null,'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':null,'chart.labels.above.color':null,'chart.labels.above.background':'white','chart.labels.above.font':null,'chart.labels.above.border':true,'chart.labels.above.offsety':5,'chart.labels.above.units.pre':'','chart.labels.above.units.post':'','chart.labels.above.specific':null,'chart.labels.offsetx':0,'chart.labels.offsety':0,'chart.xtickgap':20,'chart.smallxticks':3,'chart.largexticks':5,'chart.ytickgap':20,'chart.smallyticks':3,'chart.largeyticks':5,'chart.numyticks':10,'chart.linewidth':2.01,'chart.colors':['red','#0f0','#00f','#f0f','#ff0','#0ff','green','pink','blue','black'],'chart.hmargin':0,'chart.tickmarks.dot.stroke':'white','chart.tickmarks.dot.fill':null,'chart.tickmarks.dot.linewidth':3,'chart.tickmarks':'endcircle','chart.tickmarks.linewidth':null,'chart.tickmarks.image':null,'chart.tickmarks.image.halign':'center','chart.tickmarks.image.valign':'center','chart.tickmarks.image.offsetx':0,'chart.tickmarks.image.offsety':0,'chart.ticksize':3,'chart.gutter.left':25,'chart.gutter.right':25,'chart.gutter.top':25,'chart.gutter.bottom':30,'chart.tickdirection':-1,'chart.yaxispoints':5,'chart.fillstyle':null,'chart.xaxispos':'bottom','chart.xaxispos.value':0,'chart.yaxispos':'left','chart.xticks':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.ymin':0,'chart.ymax':null,'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.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.shadow':true,'chart.shadow.offsetx':2,'chart.shadow.offsety':2,'chart.shadow.blur':3,'chart.shadow.color':'rgba(128,128,128,0.5)','chart.tooltips':null,'chart.tooltips.hotspot.xonly':false,'chart.tooltips.hotspot.size':5,'chart.tooltips.effect':'fade','chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.event':'onmousemove','chart.tooltips.highlight':true,'chart.tooltips.coords.page':false,'chart.highlight.style':null,'chart.highlight.stroke':'gray','chart.highlight.fill':'white','chart.stepped':false,'chart.key':null,'chart.key.background':'white','chart.key.position':'graph','chart.key.halign':null,'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.color.shape':'square','chart.key.rounded':true,'chart.key.linewidth':1,'chart.key.colors':null,'chart.key.interactive':false,'chart.key.interactive.highlight.chart.stroke':'rgba(255,0,0,0.3)','chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)','chart.key.text.color':'black','chart.contextmenu':null,'chart.ylabels':true,'chart.ylabels.count':5,'chart.ylabels.inside':false,'chart.ylabels.offsetx':0,'chart.ylabels.offsety':0,'chart.scale.invert':false,'chart.xlabels.inside':false,'chart.xlabels.inside.color':'rgba(255,255,255,0.5)','chart.noaxes':false,'chart.noyaxis':false,'chart.noxaxis':false,'chart.noendxtick':false,'chart.noendytick':false,'chart.units.post':'','chart.units.pre':'','chart.scale.zerostart':true,'chart.scale.decimals':null,'chart.scale.point':'.','chart.scale.thousand':',','chart.crosshairs':false,'chart.crosshairs.color':'#333','chart.crosshairs.hline':true,'chart.crosshairs.vline':true,'chart.annotatable':false,'chart.annotate.color':'black','chart.axesontop':false,'chart.filled':false,'chart.filled.range':false,'chart.filled.range.threshold':null,'chart.filled.range.threshold.colors':['red','green'],'chart.filled.accumulative':true,'chart.variant':null,'chart.axis.color':'black','chart.axis.linewidth':1,'chart.numxticks':(data&&typeof(data[0])=='number'?data.length-1:(typeof data[0]==='object'&&data[0]&&typeof data[0][0]==='number'?data[0].length-1:20)),'chart.numyticks':10,'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.backdrop':false,'chart.backdrop.size':30,'chart.backdrop.alpha':0.2,'chart.resizable':false,'chart.resize.handle.adjust':[0,0],'chart.resize.handle.background':null,'chart.adjustable':false,'chart.noredraw':false,'chart.outofbounds':false,'chart.outofbounds.clip':false,'chart.chromefix':true,'chart.animation.factor':1,'chart.animation.unfold.x':false,'chart.animation.unfold.y':true,'chart.animation.unfold.initial':2,'chart.animation.trace.clip':1,'chart.curvy':false,'chart.line.visible':[],'chart.events.click':null,'chart.events.mousemove':null,'chart.errorbars':false,'chart.errorbars.color':'black','chart.errorbars.capped':true,'chart.errorbars.capped.width':12,'chart.errorbars.linewidth':1,'chart.combinedchart.effect':null,'chart.combinedchart.effect.options':null,'chart.combinedchart.effect.callback':null,'chart.clearto':'rgba(0,0,0,0)'}
5
+ for(var i=1;i<arguments.length;++i){if(typeof(arguments[i])=='null'||!arguments[i]){arguments[i]=[];}}
6
+ this.original_data=[];if(typeof conf==='object'&&conf.data){if(typeof conf.data[0]==='number'||RGraph.isNull(conf.data[0])){this.original_data[0]=RGraph.arrayClone(conf.data);}else{for(var i=0;i<conf.data.length;++i){this.original_data[i]=RGraph.arrayClone(conf.data[i]);}}}else{for(var i=1;i<arguments.length;++i){if(arguments[1]&&typeof(arguments[1])=='object'&&arguments[1][0]&&typeof(arguments[1][0])=='object'&&arguments[1][0].length){var tmp=[];for(var i=0;i<arguments[1].length;++i){tmp[i]=RGraph.array_clone(arguments[1][i]);}
7
+ for(var j=0;j<tmp.length;++j){this.original_data[j]=RGraph.array_clone(tmp[j]);}}else{this.original_data[i-1]=RGraph.array_clone(arguments[i]);}}}
8
+ if(!this.canvas){alert('[LINE] Fatal error: no canvas support');return;}
9
+ for(var i=0;i<this.original_data.length;++i){for(var j=0;j<this.original_data[i].length;++j){if(typeof this.original_data[i][j]==='string'){this.original_data[i][j]=parseFloat(this.original_data[i][j]);}}}
10
+ this.data_arr=RGraph.arrayLinearize(this.original_data);for(var i=0;i<this.data_arr.length;++i){this['$'+i]={};}
11
+ if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
12
+ var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
13
+ if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
14
+ this.set=this.Set=function(name)
15
+ {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
16
+ if(name.substr(0,6)!='chart.'){name='chart.'+name;}
17
+ while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
18
+ if(name=='chart.tooltips'&&typeof value=='object'&&value){var tooltips=[];for(var i=1;i<arguments.length;i++){if(typeof(arguments[i])=='object'&&arguments[i][0]){for(var j=0;j<arguments[i].length;j++){tooltips.push(arguments[i][j]);}}else if(typeof(arguments[i])=='function'){tooltips=arguments[i];}else{tooltips.push(arguments[i]);}}
19
+ value=tooltips;}
20
+ if(name=='chart.linewidth'&&navigator.userAgent.match(/Chrome/)){if(value==1){value=1.01;}else if(RGraph.is_array(value)){for(var i=0;i<value.length;++i){if(typeof(value[i])=='number'&&value[i]==1){value[i]=1.01;}}}}
21
+ if(name=='chart.xaxispos'){if(value!='bottom'&&value!='center'&&value!='top'){alert('[LINE] ('+this.id+') chart.xaxispos should be top, center or bottom. Tried to set it to: '+value+' Changing it to center');value='center';}}
22
+ if(name=='chart.xticks'){name='chart.numxticks';}
23
+ if(name=='chart.spline'){name='chart.curvy';}
24
+ if(name=='chart.ylabels.invert'){name='chart.scale.invert';}
25
+ this.properties[name]=value;return this;};this.get=this.Get=function(name)
26
+ {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
27
+ while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
28
+ if(name=='chart.spline'){name='chart.curvy';}
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.gutterLeft=prop['chart.gutter.left'];this.gutterRight=prop['chart.gutter.right'];this.gutterTop=prop['chart.gutter.top'];this.gutterBottom=prop['chart.gutter.bottom'];this.data=RG.array_clone(this.original_data);this.max=0;if(prop['chart.filled']&&!prop['chart.filled.range']&&this.data.length>1&&prop['chart.filled.accumulative']){var accumulation=[];for(var set=0;set<this.data.length;++set){for(var point=0;point<this.data[set].length;++point){this.data[set][point]=Number(accumulation[point]?accumulation[point]:0)+this.data[set][point];accumulation[point]=this.data[set][point];}}}
33
+ if(prop['chart.ymax']){this.max=prop['chart.ymax'];this.min=prop['chart.ymin']?prop['chart.ymin']:0;this.scale2=RG.getScale2(this,{'max':this.max,'min':prop['chart.ymin'],'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.scale2.max:0;if(!prop['chart.outofbounds']){for(dataset=0;dataset<this.data.length;++dataset){if(RGraph.isArray(this.data[dataset])){for(var datapoint=0;datapoint<this.data[dataset].length;datapoint++){this.hasnegativevalues=(this.data[dataset][datapoint]<0)||this.hasnegativevalues;}}}}}else{this.min=prop['chart.ymin']?prop['chart.ymin']:0;for(dataset=0;dataset<this.data.length;++dataset){for(var datapoint=0;datapoint<this.data[dataset].length;datapoint++){this.max=Math.max(this.max,this.data[dataset][datapoint]?Math.abs(parseFloat(this.data[dataset][datapoint])):0);if(!prop['chart.outofbounds']){this.hasnegativevalues=(this.data[dataset][datapoint]<0)||this.hasnegativevalues;}}}
34
+ this.scale2=RG.getScale2(this,{'max':this.max,'min':prop['chart.ymin'],'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.scale2.max:0;}
35
+ if(prop['chart.contextmenu']){RG.ShowContext(this);}
36
+ this.coords=[];this.coordsText=[];this.grapharea=ca.height-this.gutterTop-this.gutterBottom;this.halfgrapharea=this.grapharea/2;this.halfTextHeight=prop['chart.text.size']/2;if(prop['chart.variant']=='3d'){RG.Draw3DAxes(this);}
37
+ RG.background.Draw(this);if(prop['chart.background.hbars']&&prop['chart.background.hbars'].length>0){RG.DrawBars(this);}
38
+ if(prop['chart.axesontop']==false){this.DrawAxes();}
39
+ co.save()
40
+ co.beginPath();co.rect(0,0,ca.width*prop['chart.animation.trace.clip'],ca.height);co.clip();for(var i=0,j=0,len=this.data.length;i<len;i++,j++){co.beginPath();if(!prop['chart.filled']){this.SetShadow(i);}
41
+ if(prop['chart.fillstyle']){if(typeof(prop['chart.fillstyle'])=='object'&&prop['chart.fillstyle'][j]){var fill=prop['chart.fillstyle'][j];}else if(typeof(prop['chart.fillstyle'])=='object'&&prop['chart.fillstyle'].toString().indexOf('Gradient')>0){var fill=prop['chart.fillstyle'];}else if(typeof(prop['chart.fillstyle'])=='string'){var fill=prop['chart.fillstyle'];}}else if(prop['chart.filled']){var fill=prop['chart.colors'][j];}else{var fill=null;}
42
+ if(prop['chart.tickmarks']&&typeof(prop['chart.tickmarks'])=='object'){var tickmarks=prop['chart.tickmarks'][i];}else if(prop['chart.tickmarks']&&typeof(prop['chart.tickmarks'])=='string'){var tickmarks=prop['chart.tickmarks'];}else if(prop['chart.tickmarks']&&typeof(prop['chart.tickmarks'])=='function'){var tickmarks=prop['chart.tickmarks'];}else{var tickmarks=null;}
43
+ if(prop['chart.outofbounds.clip']){pa2(co,'sa b r % % % % cl b',0,this.gutterTop,ca.width,ca.height-this.gutterTop-this.gutterBottom);}
44
+ this.drawLine(this.data[i],prop['chart.colors'][j],fill,this.getLineWidth(j),tickmarks,i);if(prop['chart.outofbounds.clip']){co.restore();}
45
+ co.stroke();}
46
+ if(prop['chart.filled']&&prop['chart.filled.accumulative']&&!prop['chart.curvy']){for(var i=0;i<this.coords2.length;++i){co.beginPath();co.lineWidth=this.GetLineWidth(i);co.strokeStyle=!this.hidden(i)?prop['chart.colors'][i]:'rgba(0,0,0,0)';for(var j=0,len=this.coords2[i].length;j<len;++j){if(j==0||this.coords2[i][j][1]==null||(this.coords2[i][j-1]&&this.coords2[i][j-1][1]==null)){co.moveTo(this.coords2[i][j][0],this.coords2[i][j][1]);}else{if(prop['chart.stepped']){co.lineTo(this.coords2[i][j][0],this.coords2[i][j-1][1]);}
47
+ co.lineTo(this.coords2[i][j][0],this.coords2[i][j][1]);}}
48
+ co.stroke();}
49
+ if(prop['chart.tickmarks']){co.beginPath();co.fillStyle='white';for(var i=0,len=this.coords2.length;i<len;++i){co.beginPath();co.strokeStyle=prop['chart.colors'][i];for(var j=0;j<this.coords2[i].length;++j){if(typeof(this.coords2[i][j])=='object'&&typeof(this.coords2[i][j][0])=='number'&&typeof(this.coords2[i][j][1])=='number'){var tickmarks=typeof(prop['chart.tickmarks'])=='object'?prop['chart.tickmarks'][i]:prop['chart.tickmarks'];this.DrawTick(this.coords2[i],this.coords2[i][j][0],this.coords2[i][j][1],co.strokeStyle,false,j==0?0:this.coords2[i][j-1][0],j==0?0:this.coords2[i][j-1][1],tickmarks,j,i);}}}
50
+ co.stroke();co.fill();}}else if(prop['chart.filled']&&prop['chart.filled.accumulative']&&prop['chart.curvy']){for(var i=0;i<this.coordsSpline.length;i+=1){co.beginPath();co.strokeStyle=prop['chart.colors'][i];co.lineWidth=this.GetLineWidth(i);for(var j=0,len=this.coordsSpline[i].length;j<len;j+=1){var point=this.coordsSpline[i][j];j==0?co.moveTo(point[0],point[1]):co.lineTo(point[0],point[1]);}
51
+ co.stroke();}
52
+ for(var i=0,len=this.coords2.length;i<len;i+=1){for(var j=0,len2=this.coords2[i].length;j<len2;++j){if(typeof(this.coords2[i][j])=='object'&&typeof(this.coords2[i][j][0])=='number'&&typeof(this.coords2[i][j][1])=='number'){var tickmarks=typeof prop['chart.tickmarks']=='object'&&!RGraph.is_null(prop['chart.tickmarks'])?prop['chart.tickmarks'][i]:prop['chart.tickmarks'];co.strokeStyle=prop['chart.colors'][i];this.DrawTick(this.coords2[i],this.coords2[i][j][0],this.coords2[i][j][1],prop['chart.colors'][i],false,j==0?0:this.coords2[i][j-1][0],j==0?0:this.coords2[i][j-1][1],tickmarks,j,i);}}}}
53
+ co.restore();co.beginPath();if(prop['chart.axesontop']){this.DrawAxes();}
54
+ this.DrawLabels();this.DrawRange();if(prop['chart.key']&&prop['chart.key'].length&&RG.DrawKey){RG.DrawKey(this,prop['chart.key'],prop['chart.colors']);}
55
+ if(prop['chart.labels.above']){this.drawAboveLabels();}
56
+ RG.DrawInGraphLabels(this);if(prop['chart.filled']&&prop['chart.filled.range']&&this.data.length==2){co.beginPath();var len=this.coords.length/2;co.lineWidth=prop['chart.linewidth'];co.strokeStyle=this.hidden(0)?'rgba(0,0,0,0)':prop['chart.colors'][0];for(var i=0;i<len;++i){if(!RG.isNull(this.coords[i][1])){if(i==0){co.moveTo(this.coords[i][0],this.coords[i][1]);}else{co.lineTo(this.coords[i][0],this.coords[i][1]);}}}
57
+ co.stroke();co.beginPath();if(prop['chart.colors'][1]){co.strokeStyle=this.hidden(1)?'rgba(0,0,0,0)':prop['chart.colors'][1];}
58
+ for(var i=this.coords.length-1;i>=len;--i){if(!RG.is_null(this.coords[i][1])){if(i==(this.coords.length-1)){co.moveTo(this.coords[i][0],this.coords[i][1]);}else{co.lineTo(this.coords[i][0],this.coords[i][1]);}}}
59
+ co.stroke();}else if(prop['chart.filled']&&prop['chart.filled.range']){alert('[LINE] You must have only two sets of data for a filled range chart');}
60
+ if(prop['chart.resizable']){RG.AllowResizing(this);}
61
+ RG.InstallEventListeners(this);if(this.firstDraw){RG.fireCustomEvent(this,'onfirstdraw');this.firstDraw=false;this.firstDrawFunc();}
62
+ RG.FireCustomEvent(this,'ondraw');return this;};this.exec=function(func)
63
+ {func(this);return this;};this.drawAxes=this.DrawAxes=function()
64
+ {if(prop['chart.noaxes']){return;}
65
+ RG.noShadow(this);co.lineWidth=prop['chart.axis.linewidth']+0.001;co.lineCap='square';co.lineJoin='miter';co.strokeStyle=prop['chart.axis.color'];coords={xaxis:{},yaxis:{}};co.beginPath();if(prop['chart.noxaxis']==false){if(prop['chart.xaxispos']=='center'){coords.xaxis=[this.gutterLeft,ma.round((this.grapharea/2)+this.gutterTop),ca.width-this.gutterRight,ma.round((this.grapharea/2)+this.gutterTop)];}else if(prop['chart.xaxispos']==='top'){coords.xaxis=[this.gutterLeft,this.gutterTop,ca.width-this.gutterRight,this.gutterTop];}else{var y=ma.round(this.getYCoord(prop['chart.ymin']!=0?prop['chart.ymin']:0));if(prop['chart.scale.invert']&&prop['chart.ymin']===0){y=this.getYCoord(this.scale2.max);}else if(prop['chart.scale.invert']||prop['chart.ymin']<0){y=this.getYCoord(0);}
66
+ coords.xaxis=[this.gutterLeft,y,ca.width-this.gutterRight,y];}
67
+ co.moveTo(coords.xaxis[0],coords.xaxis[1]);co.lineTo(coords.xaxis[2],coords.xaxis[3]);this.coordsAxes=coords;}
68
+ 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);}}
69
+ if(prop['chart.noxaxis']==false&&prop['chart.numxticks']>0){var xTickInterval=(ca.width-this.gutterLeft-this.gutterRight)/prop['chart.numxticks'];if(!xTickInterval||xTickInterval<=0){xTickInterval=(ca.width-this.gutterLeft-this.gutterRight)/(prop['chart.labels']&&prop['chart.labels'].length?prop['chart.labels'].length-1:10);}
70
+ for(x=this.gutterLeft+(prop['chart.yaxispos']=='left'?xTickInterval:0);x<=(ca.width-this.gutterRight+1);x+=xTickInterval){if(prop['chart.yaxispos']=='right'&&x>=(ca.width-this.gutterRight-1)){break;}
71
+ if(prop['chart.noendxtick']){if(prop['chart.yaxispos']=='left'&&x>=(ca.width-this.gutterRight-1)){break;}else if(prop['chart.yaxispos']=='right'&&x==this.gutterLeft){continue;}}
72
+ var yStart=prop['chart.xaxispos']==='center'?(this.gutterTop+(this.grapharea/2))-3:ca.height-this.gutterBottom;var yEnd=prop['chart.xaxispos']==='center'?yStart+6:ca.height-this.gutterBottom-(x%60==0?prop['chart.largexticks']*prop['chart.tickdirection']:prop['chart.smallxticks']*prop['chart.tickdirection']);if(prop['chart.ymin']>=0&&prop['chart.xaxispos']==='bottom'){var yStart=this.getYCoord(prop['chart.ymin'])-(prop['chart.ymin']>=0?0:3),yEnd=this.getYCoord(prop['chart.ymin'])+3;if(prop['chart.scale.invert']){yStart=ca.height-prop['chart.gutter.bottom'];yEnd=yStart+3;}}else if(prop['chart.xaxispos']=='center'){var yStart=Math.round((this.gutterTop+(this.grapharea/2)))-3,yEnd=yStart+6;}else if(prop['chart.xaxispos']=='bottom'){var yStart=this.getYCoord(0)-(prop['chart.ymin']!==0?3:0),yEnd=this.getYCoord(0)-(x%60==0?prop['chart.largexticks']*prop['chart.tickdirection']:prop['chart.smallxticks']*prop['chart.tickdirection']);yEnd+=0;}else if(prop['chart.xaxispos']=='top'){yStart=this.gutterTop-3;yEnd=this.gutterTop;}
73
+ co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}}else if(prop['chart.noyaxis']==false&&prop['chart.numyticks']>0){if(!prop['chart.noendytick']){if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,Math.round(ca.height-this.gutterBottom));co.lineTo(this.gutterLeft-prop['chart.smallyticks'],Math.round(ca.height-this.gutterBottom));}else{co.moveTo(ca.width-this.gutterRight,Math.round(ca.height-this.gutterBottom));co.lineTo(ca.width-this.gutterRight+prop['chart.smallyticks'],Math.round(ca.height-this.gutterBottom));}}}
74
+ var numyticks=prop['chart.numyticks'];if(prop['chart.noyaxis']==false&&numyticks>0){var counter=0,adjustment=0;if(prop['chart.yaxispos']=='right'){adjustment=(ca.width-this.gutterLeft-this.gutterRight);}
75
+ if(prop['chart.xaxispos']=='center'){var interval=(this.grapharea/numyticks);var lineto=(prop['chart.yaxispos']=='left'?this.gutterLeft:ca.width-this.gutterRight+prop['chart.smallyticks']);for(y=this.gutterTop;y<(this.grapharea/2)+this.gutterTop;y+=interval){if(y<(this.grapharea/2)+this.gutterTop){co.moveTo((prop['chart.yaxispos']=='left'?this.gutterLeft-prop['chart.smallyticks']:ca.width-this.gutterRight),Math.round(y));co.lineTo(lineto,Math.round(y));}}
76
+ for(y=this.gutterTop+(this.halfgrapharea)+interval;y<=this.grapharea+this.gutterTop;y+=interval){co.moveTo((prop['chart.yaxispos']=='left'?this.gutterLeft-prop['chart.smallyticks']:ca.width-this.gutterRight),Math.round(y));co.lineTo(lineto,Math.round(y));}}else if(prop['chart.xaxispos']=='top'){var interval=(this.grapharea/numyticks);var lineto=(prop['chart.yaxispos']=='left'?this.gutterLeft:ca.width-this.gutterRight+prop['chart.smallyticks']);for(y=this.gutterTop+interval;y<=this.grapharea+this.gutterBottom;y+=interval){co.moveTo((prop['chart.yaxispos']=='left'?this.gutterLeft-prop['chart.smallyticks']:ca.width-this.gutterRight),Math.round(y));co.lineTo(lineto,Math.round(y));}
77
+ if(prop['chart.noxaxis']&&prop['chart.noendytick']==false){co.moveTo((prop['chart.yaxispos']=='left'?this.gutterLeft-prop['chart.smallyticks']:ca.width-this.gutterRight),this.gutterTop);co.lineTo(lineto,this.gutterTop);}}else{var lineto=(prop['chart.yaxispos']=='left'?this.gutterLeft-prop['chart.smallyticks']:ca.width-this.gutterRight+prop['chart.smallyticks']);for(y=this.gutterTop;y<(ca.height-this.gutterBottom)&&counter<numyticks;y+=((ca.height-this.gutterTop-this.gutterBottom)/numyticks)){if(ma.round(y)!==ma.round(this.coordsAxes.xaxis[1])){co.moveTo(this.gutterLeft+adjustment,ma.round(y));co.lineTo(lineto,ma.round(y));}
78
+ var counter=counter+1;}
79
+ if(prop['chart.ymin']<0){co.moveTo((prop['chart.yaxispos']=='left'?this.gutterLeft:ca.width-this.gutterRight),ma.round(y));co.lineTo(lineto,ma.round(y));}}}else if(prop['chart.noxaxis']==false&&prop['chart.numxticks']>0){if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,prop['chart.xaxispos']=='top'?this.gutterTop:ca.height-this.gutterBottom);co.lineTo(this.gutterLeft,prop['chart.xaxispos']=='top'?this.gutterTop-prop['chart.smallxticks']:ca.height-this.gutterBottom+prop['chart.smallxticks']);}else{co.moveTo(ca.width-this.gutterRight,ca.height-this.gutterBottom);co.lineTo(ca.width-this.gutterRight,ca.height-this.gutterBottom+prop['chart.smallxticks']);}}
80
+ co.stroke();co.beginPath();};this.drawLabels=this.DrawLabels=function()
81
+ {co.strokeStyle='black';co.fillStyle=prop['chart.text.color'];co.lineWidth=1;RG.NoShadow(this);var font=prop['chart.text.font'];var text_size=prop['chart.text.size'];var decimals=prop['chart.scale.decimals'];var context=co;var canvas=ca;var ymin=prop['chart.ymin'];if(prop['chart.ylabels']&&prop['chart.ylabels.specific']==null){var units_pre=prop['chart.units.pre'];var units_post=prop['chart.units.post'];var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var align=prop['chart.yaxispos']=='left'?'right':'left';var numYLabels=this.scale2.labels.length;var bounding=false;var bgcolor=prop['chart.ylabels.inside']?prop['chart.ylabels.inside.color']:null;var offsetx=prop['chart.ylabels.offsetx'];var offsety=prop['chart.ylabels.offsety'];if(prop['chart.ylabels.inside']==true&&align=='left'){xpos-=10;align='right';bounding=true;}else if(prop['chart.ylabels.inside']==true&&align=='right'){xpos+=10;align='left';bounding=true;}
82
+ if(prop['chart.xaxispos']=='center'){var half=this.grapharea/2;for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+half-(((i+1)/numYLabels)*half)+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':this.scale2.labels[i],'tag':'scale'});}
83
+ for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+half+(((i+1)/numYLabels)*half)+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':'-'+this.scale2.labels[i],'tag':'scale'});}
84
+ if(prop['chart.noxaxis']==true||ymin!=0||prop['chart.scale.zerostart']){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+half+offsety,'text':prop['chart.units.pre']+ymin.toFixed(ymin===0?0:decimals)+prop['chart.units.post'],'bounding':bounding,'boundingFill':bgcolor,'valign':'center','halign':align,'tag':'scale'});}}else if(prop['chart.xaxispos']=='top'){var half=this.grapharea/2;if(prop['chart.scale.invert']){for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+((i/this.scale2.labels.length)*this.grapharea)+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':'-'+this.scale2.labels[this.scale2.labels.length-(i+1)],'tag':'scale'});}}else{for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+(((i+1)/numYLabels)*this.grapharea)+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':'-'+this.scale2.labels[i],'tag':'scale'});}}
85
+ if((prop['chart.ymin']!=0||prop['chart.noxaxis'])||prop['chart.scale.invert']||prop['chart.scale.zerostart']){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':prop['chart.scale.invert']?ca.height-this.gutterBottom+offsety:this.gutterTop+offsety,'text':(prop['chart.ymin']!=0?'-':'')+RG.numberFormat(this,prop['chart.ymin'].toFixed(ymin===0?0:decimals),units_pre,units_post),'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'tag':'scale'});}}else{if(prop['chart.scale.invert']){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':RG.numberFormat(this,this.min.toFixed(prop['chart.ymin']===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});for(var i=0,len=this.scale2.labels.length;i<len;++i){RG.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+(((i+1)/this.scale2.labels.length)*this.grapharea)+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':this.scale2.labels[i],'tag':'scale'});}}else{for(var i=0,len=this.scale2.labels.length;i<len;++i){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+((i/this.scale2.labels.length)*this.grapharea)+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'text':this.scale2.labels[this.scale2.labels.length-(i+1)],'tag':'scale'});}}
86
+ if((prop['chart.ymin']!=0&&!prop['chart.scale.invert']||prop['chart.scale.zerostart'])||prop['chart.noxaxis']){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':prop['chart.scale.invert']?this.gutterTop+offsety:ca.height-this.gutterBottom+offsety,'text':RG.numberFormat(this,prop['chart.ymin'].toFixed(prop['chart.ymin']===0?0:prop['chart.scale.decimals']),units_pre,units_post),'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'tag':'scale'});}}
87
+ if(prop['chart.noxaxis']==true&&prop['chart.ymin']==null&&prop['chart.xaxispos']!='center'&&prop['chart.noendytick']==false){RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':prop['chart.xaxispos']=='top'?this.gutterTop+offsety:(ca.height-this.gutterBottom),'text':prop['chart.units.pre']+Number(0).toFixed(prop['chart.scale.decimals'])+prop['chart.units.post']+offsety,'valign':'center','halign':align,'bounding':bounding,'boundingFill':bgcolor,'tag':'scale'});}}else if(prop['chart.ylabels']&&typeof(prop['chart.ylabels.specific'])=='object'){var gap=this.grapharea/prop['chart.ylabels.specific'].length;var halign=prop['chart.yaxispos']=='left'?'right':'left';var bounding=false;var bgcolor=null;var ymin=prop['chart.ymin']!=null&&prop['chart.ymin'];if(prop['chart.yaxispos']=='left'){var x=this.gutterLeft-5;if(prop['chart.ylabels.inside']){x+=10;halign='left';bounding=true;bgcolor='rgba(255,255,255,0.5)';}}else if(prop['chart.yaxispos']=='right'){var x=ca.width-this.gutterRight+5;if(prop['chart.ylabels.inside']){x-=10;halign='right';bounding=true;bgcolor='rgba(255,255,255,0.5)';}}
88
+ var offsetx=prop['chart.ylabels.offsetx'];var offsety=prop['chart.ylabels.offsety'];if(prop['chart.xaxispos']=='center'){for(var i=0;i<prop['chart.ylabels.specific'].length;++i){var y=this.gutterTop+(this.grapharea/(((prop['chart.ylabels.specific'].length-1))*2)*i);if(ymin&&ymin>0){var y=((this.grapharea/2)/(prop['chart.ylabels.specific'].length-(ymin?1:0)))*i;y+=this.gutterTop;}
89
+ RG.text2(this,{'font':font,'size':text_size,'x':x+offsetx,'y':y+offsety,'text':String(prop['chart.ylabels.specific'][i]),'valign':'center','halign':halign,'bounding':bounding,'boundingFill':bgcolor,'tag':'ylabels.specific'});}
90
+ var reversed_labels=RG.array_reverse(prop['chart.ylabels.specific']);for(var i=0;i<reversed_labels.length;++i){var y=(this.grapharea/2)+this.gutterTop+((this.grapharea/((reversed_labels.length-1)*2))*i);RG.text2(this,{'font':font,'size':text_size,'x':x+offsetx,'y':y+offsety,'text':i==0?'':String(reversed_labels[i]),'valign':'center','halign':halign,'bounding':bounding,'boundingFill':bgcolor,'tag':'ylabels.specific'});}}else if(prop['chart.xaxispos']=='top'){var reversed_labels=RG.array_reverse(prop['chart.ylabels.specific']);for(var i=0;i<reversed_labels.length;++i){var y=(this.grapharea/(reversed_labels.length-1))*i;y=y+this.gutterTop;RG.Text2(this,{'font':font,'size':text_size,'x':x+offsetx,'y':y+offsety,'text':String(reversed_labels[i]),'valign':'center','halign':halign,'bounding':bounding,'boundingFill':bgcolor,'tag':'ylabels.specific'});}}else{for(var i=0;i<prop['chart.ylabels.specific'].length;++i){var y=this.gutterTop+((this.grapharea/(prop['chart.ylabels.specific'].length-1))*i);RG.text2(this,{'font':font,'size':text_size,'x':x+offsetx,'y':y+offsety,'text':String(prop['chart.ylabels.specific'][i]),'valign':'center','halign':halign,'bounding':bounding,'boundingFill':bgcolor,'tag':'ylabels.specific'});}}}
91
+ if(prop['chart.labels']&&prop['chart.labels'].length>0){var yOffset=5,bordered=false,bgcolor=null
92
+ co.fillStyle=prop['chart.labels.color']||prop['chart.text.color'];var angle=0,valign='top',halign='center',bold=prop['chart.labels.bold']
93
+ if(prop['chart.xlabels.inside']){yOffset=-5;bordered=true;bgcolor=prop['chart.xlabels.inside.color'];valign='bottom';}
94
+ if(prop['chart.xaxispos']=='top'){valign='bottom';yOffset+=2;}
95
+ if(typeof(prop['chart.text.angle'])=='number'&&prop['chart.text.angle']>0){angle=-1*prop['chart.text.angle'];valign='center';halign='right';yOffset=10;if(prop['chart.xaxispos']=='top'){yOffset=10;}}
96
+ var numLabels=prop['chart.labels'].length,offsetx=prop['chart.labels.offsetx'],offsety=prop['chart.labels.offsety'];for(i=0;i<numLabels;++i){if(prop['chart.labels'][i]){var labelX=((ca.width-this.gutterLeft-this.gutterRight-(2*prop['chart.hmargin']))/(numLabels-1))*i;labelX+=this.gutterLeft+prop['chart.hmargin'];if(this.data.length===0||!this.data[0]||prop['chart.labels'].length!=this.data[0].length){labelX=this.gutterLeft+prop['chart.hmargin']+((ca.width-this.gutterLeft-this.gutterRight-(2*prop['chart.hmargin']))*(i/(prop['chart.labels'].length-1)));}
97
+ if(!labelX){labelX=this.gutterLeft+prop['chart.hmargin'];}
98
+ if(prop['chart.xaxispos']=='top'&&prop['chart.text.angle']>0){halign='left';}
99
+ if(prop['chart.text.angle']!=0){halign='right';}
100
+ RG.Text2(this,{'font':font,'size':text_size,'bold':bold,'x':labelX+offsetx,'y':(prop['chart.xaxispos']=='top')?this.gutterTop-yOffset-(prop['chart.xlabels.inside']?-22:0)+offsety:(ca.height-this.gutterBottom)+yOffset+offsety,'text':String(prop['chart.labels'][i]),'valign':valign,'halign':halign,'bounding':bordered,'boundingFill':bgcolor,'angle':angle,'tag':'labels'});}}}
101
+ co.stroke();co.fill();}
102
+ this.drawLine=this.DrawLine=function(lineData,color,fill,linewidth,tickmarks,index)
103
+ {if(prop['chart.animation.unfold.y']&&prop['chart.animation.factor']!=1){for(var i=0;i<lineData.length;++i){lineData[i]*=prop['chart.animation.factor'];}}
104
+ var penUp=false;var yPos=null;var xPos=0;co.lineWidth=1;var lineCoords=[];if(index>0){var prevLineCoords=this.coords2[index-1];}
105
+ var xInterval=(ca.width-(2*prop['chart.hmargin'])-this.gutterLeft-this.gutterRight)/(lineData.length-1);for(i=0,len=lineData.length;i<len;i+=1){var data_point=lineData[i];var yPos=this.getYCoord(data_point);if(lineData[i]==null||(prop['chart.xaxispos']=='bottom'&&lineData[i]<this.min&&!prop['chart.outofbounds'])||(prop['chart.xaxispos']=='center'&&lineData[i]<(-1*this.max)&&!prop['chart.outofbounds'])||(((lineData[i]<this.min&&prop['chart.xaxispos']!=='center')||lineData[i]>this.max)&&!prop['chart.outofbounds'])){yPos=null;}
106
+ co.lineCap='round';co.lineJoin='round';if(i>0){xPos=xPos+xInterval;}else{xPos=prop['chart.hmargin']+this.gutterLeft;}
107
+ if(prop['chart.animation.unfold.x']){xPos*=prop['chart.animation.factor'];if(xPos<prop['chart.gutter.left']){xPos=prop['chart.gutter.left'];}}
108
+ this.coords.push([xPos,yPos]);lineCoords.push([xPos,yPos]);}
109
+ co.stroke();this.coords2[index]=lineCoords;if(RG.ISOLD&&prop['chart.shadow']){this.DrawIEShadow(lineCoords,co.shadowColor);}
110
+ co.beginPath();co.strokeStyle='rgba(0,0,0,0)';if(fill){co.fillStyle=fill;}
111
+ var isStepped=prop['chart.stepped'];var isFilled=prop['chart.filled'];if(prop['chart.xaxispos']=='top'){var xAxisPos=this.gutterTop;}else if(prop['chart.xaxispos']=='center'){var xAxisPos=this.gutterTop+(this.grapharea/2);}else if(prop['chart.xaxispos']=='bottom'){var xAxisPos=this.getYCoord(prop['chart.ymin'])}
112
+ for(var i=0,len=lineCoords.length;i<len;i+=1){xPos=lineCoords[i][0];yPos=lineCoords[i][1];var set=index;var prevY=(lineCoords[i-1]?lineCoords[i-1][1]:null);var isLast=(i+1)==lineCoords.length;if(!prop['chart.outofbounds']&&(prevY<this.gutterTop||prevY>(ca.height-this.gutterBottom))){penUp=true;}
113
+ if(i==0||penUp||!yPos||!prevY||prevY<this.gutterTop){if(prop['chart.filled']&&!prop['chart.filled.range']){if(!prop['chart.outofbounds']||prevY===null||yPos===null){co.moveTo(xPos+1,xAxisPos);}
114
+ if(prop['chart.xaxispos']=='top'){co.moveTo(xPos+1,xAxisPos);}
115
+ if(isStepped&&i>0){co.lineTo(xPos,lineCoords[i-1][1]);}
116
+ co.lineTo(xPos,yPos);}else{if(RG.ISOLD&&yPos==null){}else{co.moveTo(xPos+1,yPos);}}
117
+ if(yPos==null){penUp=true;}else{penUp=false;}}else{if(isStepped){co.lineTo(xPos,lineCoords[i-1][1]);}
118
+ if((yPos>=this.gutterTop&&yPos<=(ca.height-this.gutterBottom))||prop['chart.outofbounds']){if(isLast&&prop['chart.filled']&&!prop['chart.filled.range']&&prop['chart.yaxispos']=='right'){xPos-=1;}
119
+ if(!isStepped||!isLast){co.lineTo(xPos,yPos);if(isFilled&&lineCoords[i+1]&&lineCoords[i+1][1]==null){co.lineTo(xPos,xAxisPos);}}else if(isStepped&&isLast){co.lineTo(xPos,yPos);}
120
+ penUp=false;}else{penUp=true;}}}
121
+ if(prop['chart.filled']&&!prop['chart.filled.range']&&!prop['chart.curvy']){var fillStyle=prop['chart.fillstyle'];if(index>0&&prop['chart.filled.accumulative']){co.lineTo(xPos,prevLineCoords?prevLineCoords[i-1][1]:(ca.height-this.gutterBottom-1+(prop['chart.xaxispos']=='center'?(ca.height-this.gutterTop-this.gutterBottom)/2:0)));for(var k=(i-1);k>=0;--k){co.lineTo(k==0?prevLineCoords[k][0]+1:prevLineCoords[k][0],prevLineCoords[k][1]);}}else{if(prop['chart.xaxispos']=='top'){co.lineTo(xPos,prop['chart.gutter.top']+1);co.lineTo(lineCoords[0][0],prop['chart.gutter.top']+1);}else if(typeof(lineCoords[i-1][1])=='number'){var yPosition=this.getYCoord(0);co.lineTo(xPos,yPosition);co.lineTo(lineCoords[0][0],yPosition);}}
122
+ co.fillStyle=!this.hidden(index)?fill:'rgba(0,0,0,0)';co.fill();co.beginPath();}
123
+ co.stroke();if(prop['chart.backdrop']){this.DrawBackdrop(lineCoords,color);}
124
+ co.save();co.beginPath();co.rect(0,0,ca.width*prop['chart.animation.trace.clip'],ca.height);co.clip();if(typeof prop['chart.errorbars']!=='null'){this.drawErrorbars();}
125
+ this.SetShadow(index);this.RedrawLine(lineCoords,color,linewidth,index);co.stroke();RG.NoShadow(this);for(var i=0;i<lineCoords.length;++i){i=Number(i);co.strokeStyle=color;if(isStepped&&i==(lineCoords.length-1)){co.beginPath();}
126
+ if((tickmarks!='endcircle'&&tickmarks!='endsquare'&&tickmarks!='filledendsquare'&&tickmarks!='endtick'&&tickmarks!='endtriangle'&&tickmarks!='arrow'&&tickmarks!='filledarrow')||(i==0&&tickmarks!='arrow'&&tickmarks!='filledarrow')||i==(lineCoords.length-1)){var prevX=(i<=0?null:lineCoords[i-1][0]);var prevY=(i<=0?null:lineCoords[i-1][1]);this.DrawTick(lineData,lineCoords[i][0],lineCoords[i][1],color,false,prevX,prevY,tickmarks,i,index);}}
127
+ co.restore();co.beginPath();co.arc(ca.width+50000,ca.height+50000,2,0,6.38,1);};this.drawTick=this.DrawTick=function(lineData,xPos,yPos,color,isShadow,prevX,prevY,tickmarks,index,dataset)
128
+ {if(this.hidden(dataset)){return;}else if(RG.is_null(yPos)){return false;}else if((yPos>(ca.height-this.gutterBottom))&&!prop['chart.outofbounds']){return;}else if((yPos<this.gutterTop)&&!prop['chart.outofbounds']){return;}
129
+ co.beginPath();var offset=0;co.lineWidth=prop['chart.tickmarks.linewidth']?prop['chart.tickmarks.linewidth']:prop['chart.linewidth'];co.strokeStyle=isShadow?prop['chart.shadow.color']:co.strokeStyle;co.fillStyle=isShadow?prop['chart.shadow.color']:co.strokeStyle;if(tickmarks=='circle'||tickmarks=='filledcircle'||tickmarks=='endcircle'){if(tickmarks=='circle'||tickmarks=='filledcircle'||(tickmarks=='endcircle'&&(index==0||index==(lineData.length-1)))){co.beginPath();co.arc(xPos+offset,yPos+offset,prop['chart.ticksize'],0,360/(180/RG.PI),false);if(tickmarks=='filledcircle'){co.fillStyle=isShadow?prop['chart.shadow.color']:co.strokeStyle;}else{co.fillStyle=isShadow?prop['chart.shadow.color']:'white';}
130
+ co.stroke();co.fill();}}else if(tickmarks=='halftick'){co.beginPath();co.moveTo(Math.round(xPos),yPos);co.lineTo(Math.round(xPos),yPos+prop['chart.ticksize']);co.stroke();}else if(tickmarks=='tick'){co.beginPath();co.moveTo(Math.round(xPos),yPos-prop['chart.ticksize']);co.lineTo(Math.round(xPos),yPos+prop['chart.ticksize']);co.stroke();}else if(tickmarks=='endtick'&&(index==0||index==(lineData.length-1))){co.beginPath();co.moveTo(Math.round(xPos),yPos-prop['chart.ticksize']);co.lineTo(Math.round(xPos),yPos+prop['chart.ticksize']);co.stroke();}else if(tickmarks=='cross'){co.beginPath();var ticksize=prop['chart.ticksize'];co.moveTo(xPos-ticksize,yPos-ticksize);co.lineTo(xPos+ticksize,yPos+ticksize);co.moveTo(xPos+ticksize,yPos-ticksize);co.lineTo(xPos-ticksize,yPos+ticksize);co.stroke();}else if(tickmarks=='triangle'||tickmarks=='filledtriangle'||(tickmarks=='endtriangle'&&(index==0||index==(lineData.length-1)))){co.beginPath();if(tickmarks=='filledtriangle'){co.fillStyle=isShadow?prop['chart.shadow.color']:co.strokeStyle;}else{co.fillStyle='white';}
131
+ co.moveTo(ma.round(xPos-prop['chart.ticksize']),yPos+prop['chart.ticksize']);co.lineTo(ma.round(xPos),yPos-prop['chart.ticksize']);co.lineTo(ma.round(xPos+prop['chart.ticksize']),yPos+prop['chart.ticksize']);co.closePath();co.stroke();co.fill();}else if(tickmarks=='borderedcircle'||tickmarks=='dot'){co.lineWidth=prop['chart.tickmarks.dot.linewidth']||0.00000001;pa2(co,['b','a',xPos,yPos,prop['chart.ticksize'],0,360/(180/RG.PI),false,'c','f',prop['chart.tickmarks.dot.fill']||color,'s',prop['chart.tickmarks.dot.stroke']||color]);}else if(tickmarks=='square'||tickmarks=='filledsquare'||(tickmarks=='endsquare'&&(index==0||index==(lineData.length-1)))||(tickmarks=='filledendsquare'&&(index==0||index==(lineData.length-1)))){co.fillStyle='white';co.strokeStyle=co.strokeStyle;co.beginPath();co.rect(Math.round(xPos-prop['chart.ticksize']),Math.round(yPos-prop['chart.ticksize']),prop['chart.ticksize']*2,prop['chart.ticksize']*2);if(tickmarks=='filledsquare'||tickmarks=='filledendsquare'){co.fillStyle=isShadow?prop['chart.shadow.color']:co.strokeStyle;co.rect(Math.round(xPos-prop['chart.ticksize']),Math.round(yPos-prop['chart.ticksize']),prop['chart.ticksize']*2,prop['chart.ticksize']*2);}else if(tickmarks=='square'||tickmarks=='endsquare'){co.fillStyle=isShadow?prop['chart.shadow.color']:'white';co.rect(Math.round((xPos-prop['chart.ticksize'])+1),Math.round((yPos-prop['chart.ticksize'])+1),(prop['chart.ticksize']*2)-2,(prop['chart.ticksize']*2)-2);}
132
+ co.stroke();co.fill();}else if(tickmarks=='filledarrow'){var x=Math.abs(xPos-prevX);var y=Math.abs(yPos-prevY);if(yPos<prevY){var a=Math.atan(x/y)+1.57;}else{var a=Math.atan(y/x)+3.14;}
133
+ co.beginPath();co.moveTo(Math.round(xPos),Math.round(yPos));co.arc(Math.round(xPos),Math.round(yPos),7,a-0.5,a+0.5,false);co.closePath();co.stroke();co.fill();}else if(tickmarks=='arrow'){var orig_linewidth=co.lineWidth;var x=Math.abs(xPos-prevX);var y=Math.abs(yPos-prevY);co.lineWidth;if(yPos<prevY){var a=Math.atan(x/y)+1.57;}else{var a=Math.atan(y/x)+3.14;}
134
+ co.beginPath();co.moveTo(Math.round(xPos),Math.round(yPos));co.arc(Math.round(xPos),Math.round(yPos),7,a-0.5-(doc.all?0.1:0.01),a-0.4,false);co.moveTo(Math.round(xPos),Math.round(yPos));co.arc(Math.round(xPos),Math.round(yPos),7,a+0.5+(doc.all?0.1:0.01),a+0.5,true);co.stroke();co.fill();co.lineWidth=orig_linewidth;}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;}
135
+ img.onload=function()
136
+ {if(prop['chart.tickmarks.image.halign']==='center')xPos-=(this.width/2);if(prop['chart.tickmarks.image.halign']==='right')xPos-=this.width;if(prop['chart.tickmarks.image.valign']==='center')yPos-=(this.height/2);if(prop['chart.tickmarks.image.valign']==='bottom')yPos-=this.height;xPos+=prop['chart.tickmarks.image.offsetx'];yPos+=prop['chart.tickmarks.image.offsety'];co.drawImage(this,xPos,yPos);};}else if(typeof(tickmarks)=='function'){tickmarks(this,lineData,lineData[index],index,xPos,yPos,color,prevX,prevY);}};this.drawRange=this.DrawRange=function()
137
+ {if(prop['chart.filled.range']&&prop['chart.filled']){if(RG.isNull(prop['chart.filled.range.threshold'])){prop['chart.filled.range.threshold']=this.ymin
138
+ prop['chart.filled.range.threshold.colors']=[prop['chart.fillstyle'],prop['chart.fillstyle']]}
139
+ for(var idx=0;idx<2;++idx){var threshold_colors=prop['chart.filled.range.threshold.colors'];var y=this.getYCoord(prop['chart.filled.range.threshold'])
140
+ co.save();if(idx==0){co.beginPath();co.rect(0,0,ca.width,y);co.clip();}else{co.beginPath();co.rect(0,y,ca.width,ca.height);co.clip();}
141
+ co.beginPath();co.fillStyle=(idx==1?prop['chart.filled.range.threshold.colors'][1]:prop['chart.filled.range.threshold.colors'][0]);co.lineWidth=!this.hidden(idx)?1:0;var len=(this.coords.length/2);for(var i=0;i<len;++i){if(!RG.is_null(this.coords[i][1])){if(i==0){co.moveTo(this.coords[i][0],this.coords[i][1])}else{co.lineTo(this.coords[i][0],this.coords[i][1])}}}
142
+ for(var i=this.coords.length-1;i>=len;--i){if(RG.is_null(this.coords[i][1])){co.moveTo(this.coords[i][0],this.coords[i][1])}else{co.lineTo(this.coords[i][0],this.coords[i][1])}}
143
+ co.fill();co.restore();}}};this.redrawLine=this.RedrawLine=function(coords,color,linewidth,index)
144
+ {if(prop['chart.noredraw']||prop['chart.filled.range']){return;}
145
+ co.strokeStyle=(typeof(color)=='object'&&color&&color.toString().indexOf('CanvasGradient')==-1?color[0]:color);co.lineWidth=linewidth;if(this.hidden(index)){co.strokeStyle='rgba(0,0,0,0)';}
146
+ if(!RG.ISOLD&&(prop['chart.curvy']||prop['chart.spline'])){this.DrawCurvyLine(coords,this.hidden(index)?'rgba(0,0,0,0)':color,linewidth,index);return;}
147
+ co.beginPath();var len=coords.length;var width=ca.width
148
+ var height=ca.height;var penUp=false;for(var i=0;i<len;++i){var xPos=coords[i][0];var yPos=coords[i][1];if(i>0){var prevX=coords[i-1][0];var prevY=coords[i-1][1];}
149
+ if(((i==0&&coords[i])||(yPos<this.gutterTop)||(prevY<this.gutterTop)||(yPos>(height-this.gutterBottom))||(i>0&&prevX>(width-this.gutterRight))||(i>0&&prevY>(height-this.gutterBottom))||prevY==null||penUp==true)&&(!prop['chart.outofbounds']||yPos==null||prevY==null)){if(RG.ISOLD&&yPos==null){}else{co.moveTo(coords[i][0],coords[i][1]);}
150
+ penUp=false;}else{if(prop['chart.stepped']&&i>0){co.lineTo(coords[i][0],coords[i-1][1]);}
151
+ co.lineTo(coords[i][0],coords[i][1]);penUp=false;}}
152
+ if(prop['chart.colors.alternate']&&typeof(color)=='object'&&color[0]&&color[1]){for(var i=1;i<len;++i){var prevX=coords[i-1][0];var prevY=coords[i-1][1];if(prevY!=null&&coords[i][1]!=null){co.beginPath();co.strokeStyle=color[coords[i][1]<prevY?0:1];co.lineWidth=prop['chart.linewidth'];co.moveTo(prevX,prevY);co.lineTo(coords[i][0],coords[i][1]);co.stroke();}}}};this.drawIEShadow=this.DrawIEShadow=function(coords,color)
153
+ {var offsetx=prop['chart.shadow.offsetx'];var offsety=prop['chart.shadow.offsety'];co.lineWidth=prop['chart.linewidth'];co.strokeStyle=color;co.beginPath();for(var i=0;i<coords.length;++i){var isNull=RG.isNull(coords[i][1]);var prevIsNull=RG.isNull(coords[i-1])||RG.isNull(coords[i-1][1]);if(i==0||isNull||prevIsNull){if(!isNull){co.moveTo(coords[i][0]+offsetx,coords[i][1]+offsety);}}else{co.lineTo(coords[i][0]+offsetx,coords[i][1]+offsety);}}
154
+ co.stroke();};this.drawBackdrop=this.DrawBackdrop=function(coords,color)
155
+ {var size=prop['chart.backdrop.size'];co.lineWidth=size;co.globalAlpha=prop['chart.backdrop.alpha'];co.strokeStyle=color;var yCoords=[];co.beginPath();if(prop['chart.curvy']&&!RG.ISOLD){for(var i=0;i<coords.length;++i){yCoords.push(coords[i][1])}
156
+ this.DrawSpline(co,yCoords,color,null);}else{co.moveTo(coords[0][0],coords[0][1]);for(var j=1;j<coords.length;++j){co.lineTo(coords[j][0],coords[j][1]);}}
157
+ co.stroke();co.globalAlpha=1;RG.NoShadow(this);};this.getLineWidth=this.GetLineWidth=function(i)
158
+ {var linewidth=prop['chart.linewidth'];if(typeof(linewidth)=='number'){return linewidth;}else if(typeof(linewidth)=='object'){if(linewidth[i]){return linewidth[i];}else{return linewidth[0];}
159
+ alert('[LINE] Error! chart.linewidth should be a single number or an array of one or more numbers');}};this.getShape=this.getPoint=function(e)
160
+ {var obj=this,RG=RGraph,ca=canvas=e.target,co=context=this.context,prop=this.properties;var mouseXY=RG.getMouseXY(e),mouseX=mouseXY[0],mouseY=mouseXY[1];if(arguments[1]){obj=arguments[1];}
161
+ for(var i=0;i<obj.coords.length;++i){var x=obj.coords[i][0];var y=obj.coords[i][1];if(mouseX<=(x+prop['chart.tooltips.hotspot.size'])&&mouseX>=(x-prop['chart.tooltips.hotspot.size'])&&mouseY<=(y+prop['chart.tooltips.hotspot.size'])&&mouseY>=(y-prop['chart.tooltips.hotspot.size'])){if(RG.parseTooltipText){var tooltip=RG.parseTooltipText(prop['chart.tooltips'],i);}
162
+ var dataset=0;var idx=i;while((idx+1)>this.data[dataset].length){idx-=this.data[dataset].length;dataset++;}
163
+ return{0:obj,1:x,2:y,3:i,'object':obj,'x':x,'y':y,'index':i,'tooltip':tooltip,'dataset':dataset,'index_adjusted':idx};}else if(prop['chart.tooltips.hotspot.xonly']==true&&mouseX<=(x+prop['chart.tooltips.hotspot.size'])&&mouseX>=(x-prop['chart.tooltips.hotspot.size'])){var tooltip=RG.parseTooltipText(prop['chart.tooltips'],i);return{0:obj,1:x,2:y,3:i,'object':obj,'x':x,'y':y,'index':i,'tooltip':tooltip};}}};this.drawAboveLabels=this.DrawAboveLabels=function()
164
+ {var size=prop['chart.labels.above.size'],font=prop['chart.labels.above.font']||prop['chart.text.font'],units_pre=prop['chart.labels.above.units.pre'],units_post=prop['chart.labels.above.units.post'],decimals=prop['chart.labels.above.decimals'],color=prop['chart.labels.above.color']||prop['chart.text.color'],bgcolor=prop['chart.labels.above.background']||'white',border=((typeof prop['chart.labels.above.border']==='boolean'||typeof prop['chart.labels.above.border']==='number')?prop['chart.labels.above.border']:true),offsety=prop['chart.labels.above.offsety']+size,specific=prop['chart.labels.above.specific'];co.beginPath();for(var i=0,len=this.coords.length;i<len;i+=1){var coords=this.coords[i];RG.text2(this,{color:color,'font':font,'size':size,'x':coords[0],'y':coords[1]-offsety,'text':(specific&&specific[i])?specific[i]:(specific?null:RG.numberFormat(this,typeof decimals==='number'?this.data_arr[i].toFixed(decimals):this.data_arr[i],units_pre,units_post)),'valign':'center','halign':'center','bounding':true,'boundingFill':bgcolor,'boundingStroke':border?'black':'rgba(0,0,0,0)','tag':'labels.above'});}};this.drawCurvyLine=this.DrawCurvyLine=function(coords,color,linewidth,index)
165
+ {var yCoords=[];for(var i=0;i<coords.length;++i){yCoords.push(coords[i][1]);}
166
+ if(prop['chart.filled']){co.beginPath();var xaxisY=this.getYCoord(prop['chart.ymin']);co.moveTo(coords[0][0],xaxisY);this.drawSpline(co,yCoords,color,index);if(prop['chart.filled.accumulative']&&index>0){for(var i=(this.coordsSpline[index-1].length-1);i>=0;i-=1){co.lineTo(this.coordsSpline[index-1][i][0],this.coordsSpline[index-1][i][1]);}}else{co.lineTo(coords[coords.length-1][0],xaxisY);}
167
+ co.fill();}
168
+ co.beginPath();this.DrawSpline(co,yCoords,color,index);co.stroke();};this.getValue=function(arg)
169
+ {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];}
170
+ var obj=this;var xaxispos=prop['chart.xaxispos'];if(mouseY<prop['chart.gutter.top']){return xaxispos=='bottom'||xaxispos=='center'?this.max:this.min;}else if(mouseY>(ca.height-prop['chart.gutter.bottom'])){return xaxispos=='bottom'?this.min:this.max;}
171
+ if(prop['chart.xaxispos']=='center'){var value=(((obj.grapharea/2)-(mouseY-prop['chart.gutter.top']))/obj.grapharea)*(obj.max-obj.min);value*=2;value>0?value+=this.min:value-=this.min;return value;}else if(prop['chart.xaxispos']=='top'){var value=((obj.grapharea-(mouseY-prop['chart.gutter.top']))/obj.grapharea)*(obj.max-obj.min);value=Math.abs(obj.max-value)* -1;return value;}else{var value=((obj.grapharea-(mouseY-prop['chart.gutter.top']))/obj.grapharea)*(obj.max-obj.min)
172
+ value+=obj.min;return value;}};this.highlight=this.Highlight=function(shape)
173
+ {if(prop['chart.tooltips.highlight']){if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else if(prop['chart.highlight.style']==='halo'){var obj=shape.object,color=prop['chart.colors'][shape.dataset];RG.path2(obj.context,'b a % % 13 0 6.2830 false f rgba(255,255,255,0.75)',shape.x,shape.y);RG.path2(obj.context,'ga 0.15 b a % % 13 0 6.2830 false f % ga 1',shape.x,shape.y,color);RG.path2(obj.context,'b a % % 7 0 6.2830 false f white',shape.x,shape.y);RG.path2(obj.context,'b a % % 5 0 6.2830 false f %',shape.x,shape.y,color);}else{RG.Highlight.Point(this,shape);}}};this.getObjectByXY=function(e)
174
+ {var mouseXY=RG.getMouseXY(e);if((mouseXY[0]>prop['chart.gutter.left']-5)&&mouseXY[0]<(ca.width-prop['chart.gutter.right']+5)&&mouseXY[1]>(prop['chart.gutter.top']-5)&&mouseXY[1]<(ca.height-prop['chart.gutter.bottom']+5)){return this;}};this.adjusting_mousemove=this.Adjusting_mousemove=function(e)
175
+ {if(prop['chart.adjustable']&&RG.Registry.Get('chart.adjusting')&&RG.Registry.Get('chart.adjusting').uid==this.uid){var value=Number(this.getValue(e));var shape=RG.Registry.Get('chart.adjusting.shape');if(shape){RG.Registry.Set('chart.adjusting.shape',shape);this.original_data[shape['dataset']][shape['index_adjusted']]=Number(value);RG.redrawCanvas(e.target);RG.fireCustomEvent(this,'onadjust');}}};this.getYCoord=function(value)
176
+ {if(typeof(value)!='number'){return null;}
177
+ var y;var xaxispos=prop['chart.xaxispos'];if(xaxispos=='top'){y=((value-this.min)/(this.max-this.min))*this.grapharea;if(prop['chart.scale.invert']){y=this.grapharea-y;}
178
+ y=y+this.gutterTop}else if(xaxispos=='center'){y=((value-this.min)/(this.max-this.min))*(this.grapharea/2);y=(this.grapharea/2)-y;y+=this.gutterTop;}else{if((value<this.min||value>this.max)&&prop['chart.outofbounds']==false){return null;}
179
+ y=((value-this.min)/(this.max-this.min))*this.grapharea;if(prop['chart.scale.invert']){y=this.grapharea-y;}
180
+ y=ca.height-this.gutterBottom-y;}
181
+ return y;};this.drawSpline=this.DrawSpline=function(context,coords,color,index)
182
+ {this.coordsSpline[index]=[];var xCoords=[];var gutterLeft=prop['chart.gutter.left'];var gutterRight=prop['chart.gutter.right'];var hmargin=prop['chart.hmargin'];var interval=(ca.width-(gutterLeft+gutterRight)-(2*hmargin))/(coords.length-1);co.strokeStyle=color;for(var i=0,len=coords.length;i<len;i+=1){if(typeof coords[i]=='object'&&coords[i]&&coords[i].length==2){coords[i]=Number(coords[i][1]);}}
183
+ var P=[coords[0]];for(var i=0;i<coords.length;++i){P.push(coords[i]);}
184
+ P.push(coords[coords.length-1]+(coords[coords.length-1]-coords[coords.length-2]));for(var j=1;j<P.length-2;++j){for(var t=0;t<10;++t){var yCoord=Spline(t/10,P[j-1],P[j],P[j+1],P[j+2]);xCoords.push(((j-1)*interval)+(t*(interval/10))+gutterLeft+hmargin);co.lineTo(xCoords[xCoords.length-1],yCoord);if(typeof index=='number'){this.coordsSpline[index].push([xCoords[xCoords.length-1],yCoord]);}}}
185
+ co.lineTo(((j-1)*interval)+gutterLeft+hmargin,P[j]);if(typeof index=='number'){this.coordsSpline[index].push([((j-1)*interval)+gutterLeft+hmargin,P[j]]);}
186
+ function Spline(t,P0,P1,P2,P3)
187
+ {return 0.5*((2*P1)+
188
+ ((0-P0)+P2)*t+
189
+ ((2*P0-(5*P1)+(4*P2)-P3)*(t*t)+
190
+ ((0-P0)+(3*P1)-(3*P2)+P3)*(t*t*t)));}};this.parseColors=function()
191
+ {if(this.original_colors.length===0){this.original_colors['chart.colors']=RGraph.array_clone(prop['chart.colors']);this.original_colors['chart.fillstyle']=RGraph.array_clone(prop['chart.fillstyle']);this.original_colors['chart.key.colors']=RGraph.array_clone(prop['chart.key.colors']);this.original_colors['chart.background.barcolor1']=prop['chart.background.barcolor1'];this.original_colors['chart.background.barcolor2']=prop['chart.background.barcolor2'];this.original_colors['chart.background.grid.color']=prop['chart.background.grid.color'];this.original_colors['chart.background.color']=prop['chart.background.color'];this.original_colors['chart.text.color']=prop['chart.text.color'];this.original_colors['chart.crosshairs.color']=prop['chart.crosshairs.color'];this.original_colors['chart.annotate.color']=prop['chart.annotate.color'];this.original_colors['chart.title.color']=prop['chart.title.color'];this.original_colors['chart.title.yaxis.color']=prop['chart.title.yaxis.color'];this.original_colors['chart.key.background']=prop['chart.key.background'];this.original_colors['chart.axis.color']=prop['chart.axis.color'];this.original_colors['chart.highlight.fill']=prop['chart.highlight.fill'];}
192
+ for(var i=0;i<prop['chart.colors'].length;++i){if(typeof(prop['chart.colors'][i])=='object'&&prop['chart.colors'][i][0]&&prop['chart.colors'][i][1]){prop['chart.colors'][i][0]=this.parseSingleColorForGradient(prop['chart.colors'][i][0]);prop['chart.colors'][i][1]=this.parseSingleColorForGradient(prop['chart.colors'][i][1]);}else{prop['chart.colors'][i]=this.parseSingleColorForGradient(prop['chart.colors'][i]);}}
193
+ if(prop['chart.fillstyle']){if(typeof(prop['chart.fillstyle'])=='string'){prop['chart.fillstyle']=this.parseSingleColorForGradient(prop['chart.fillstyle'],'vertical');}else{for(var i=0;i<prop['chart.fillstyle'].length;++i){prop['chart.fillstyle'][i]=this.parseSingleColorForGradient(prop['chart.fillstyle'][i],'vertical');}}}
194
+ if(!RG.is_null(prop['chart.key.colors'])){for(var i=0;i<prop['chart.key.colors'].length;++i){prop['chart.key.colors'][i]=this.parseSingleColorForGradient(prop['chart.key.colors'][i]);}}
195
+ var properties=['chart.background.barcolor1','chart.background.barcolor2','chart.background.grid.color','chart.background.color','chart.text.color','chart.crosshairs.color','chart.annotate.color','chart.title.color','chart.title.yaxis.color','chart.key.background','chart.axis.color','chart.highlight.fill'];for(var i=0;i<properties.length;++i){prop[properties[i]]=this.parseSingleColorForGradient(prop[properties[i]]);}};this.reset=function()
196
+ {};this.parseSingleColorForGradient=function(color)
197
+ {if(!color||typeof(color)!='string'){return color;}
198
+ var dir=typeof(arguments[1])=='string'?arguments[1]:'vertical';if(typeof color==='string'&&color.match(/^gradient\((.*)\)$/i)){var parts=RegExp.$1.split(':');if(dir=='horizontal'){var grad=co.createLinearGradient(0,0,ca.width,0);}else{var grad=co.createLinearGradient(0,ca.height-prop['chart.gutter.bottom'],0,prop['chart.gutter.top']);}
199
+ 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]));}}
200
+ return grad?grad:color;};this.setShadow=this.SetShadow=function(i)
201
+ {if(prop['chart.shadow']){var shadowColor=prop['chart.shadow.color'];if(typeof(shadowColor)=='object'&&shadowColor[i-1]){co.shadowColor=shadowColor[i];}else if(typeof(shadowColor)=='object'){co.shadowColor=shadowColor[0];}else if(typeof(shadowColor)=='string'){co.shadowColor=shadowColor;}
202
+ co.shadowBlur=prop['chart.shadow.blur'];co.shadowOffsetX=prop['chart.shadow.offsetx'];co.shadowOffsetY=prop['chart.shadow.offsety'];}};this.interactiveKeyHighlight=function(index)
203
+ {var coords=this.coords2[index];if(coords){var pre_linewidth=co.lineWidth;var pre_linecap=co.lineCap;co.lineWidth=prop['chart.linewidth']+10;co.lineCap='round';co.strokeStyle=prop['chart.key.interactive.highlight.chart.stroke'];co.beginPath();if(prop['chart.curvy']){this.DrawSpline(co,coords,prop['chart.key.interactive.highlight.chart'],null);}else{for(var i=0,len=coords.length;i<len;i+=1){if(i==0||RG.is_null(coords[i][1])||(typeof coords[i-1][1]!=undefined&&RG.is_null(coords[i-1][1]))){co.moveTo(coords[i][0],coords[i][1]);}else{co.lineTo(coords[i][0],coords[i][1]);}}}
204
+ co.stroke();co.lineWidth=pre_linewidth;co.lineCap=pre_linecap;}};this.on=function(type,func)
205
+ {if(type.substr(0,2)!=='on'){type='on'+type;}
206
+ if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
207
+ return this;};this.firstDrawFunc=function()
208
+ {};this.drawErrorbars=function()
209
+ {co.save();RG.noShadow(this);var coords=this.coords,x=0,errorbars=prop['chart.errorbars'],length=0;if(!prop['chart.errorbars.capped']){prop['chart.errorbars.capped.width']=0.001;halfwidth=0.0005;}
210
+ co.lineWidth=prop['chart.errorbars.linewidth'];for(var i=0;i<coords.length;++i){var halfwidth=prop['chart.errorbars.capped.width']/2||5,color=prop['chart.errorbars.color']||'black';if(errorbars[i]&&typeof errorbars[i][3]==='number'){co.lineWidth=errorbars[i][3];}else if(typeof prop['chart.errorbars.linewidth']==='number'){co.lineWidth=prop['chart.errorbars.linewidth'];}else{co.lineWidth=1;}
211
+ if(typeof errorbars==='number'||typeof errorbars[i]==='number'){if(typeof errorbars==='number'){var positiveLength=this.getYCoord(this.min)-this.getYCoord(this.min+errorbars),negativeLength=positiveLength;}else{var positiveLength=this.getYCoord(this.min)-this.getYCoord(this.min+errorbars[i]),negativeLength=positiveLength;}
212
+ if(positiveLength||negativeLength){pa2(co,'lj miter lc square b m % % l % % m % % l % % l % % m % % l % % s %',coords[i][0]-halfwidth,coords[i][1]+negativeLength,coords[i][0]+halfwidth,coords[i][1]+negativeLength,coords[i][0],coords[i][1]+negativeLength,coords[i][0],coords[i][1]-positiveLength,coords[i][0]-halfwidth,coords[i][1]-positiveLength,coords[i][0],coords[i][1]-positiveLength,coords[i][0]+halfwidth,coords[i][1]-positiveLength,color);pa2(co,'lj miter lc square b m % % l % % s %',coords[i][0]-halfwidth,coords[i][1]+negativeLength,coords[i][0]+halfwidth,coords[i][1]+negativeLength,color);}}else if(typeof errorbars[i]==='object'&&!RG.isNull(errorbars[i])){var positiveLength=this.getYCoord(this.min)-this.getYCoord(this.min+errorbars[i][0]),negativeLength=this.getYCoord(this.min)-this.getYCoord(this.min+errorbars[i][1]);if(typeof errorbars[i][2]==='string'){color=errorbars[i][2];}
213
+ halfwidth=typeof errorbars[i][4]==='number'?errorbars[i][4]/2:halfwidth;if(typeof errorbars[i]==='object'&&typeof errorbars[i][3]==='number'){co.lineWidth=errorbars[i][3];}else if(typeof prop['chart.errorbars.linewidth']==='number'){co.lineWidth=prop['chart.errorbars.linewidth'];}else{co.lineWidth=1;}
214
+ if(!RG.isNull(errorbars[i][0])){pa2(co,'lc square b m % % l % % l % % m % % l % % s %',coords[i][0],coords[i][1],coords[i][0],coords[i][1]-positiveLength,coords[i][0]-halfwidth,ma.round(coords[i][1]-positiveLength),coords[i][0],ma.round(coords[i][1]-positiveLength),coords[i][0]+halfwidth,ma.round(coords[i][1]-positiveLength),color);}
215
+ if(typeof errorbars[i][1]==='number'){var negativeLength=ma.abs(this.getYCoord(errorbars[i][1])-this.getYCoord(0));pa2(co,'b m % % l % % l % % m % % l % % s %',coords[i][0],coords[i][1],coords[i][0],coords[i][1]+negativeLength,coords[i][0]-halfwidth,ma.round(coords[i][1]+negativeLength),coords[i][0],ma.round(coords[i][1]+negativeLength),coords[i][0]+halfwidth,ma.round(coords[i][1]+negativeLength),color);}}}
216
+ co.restore();};this.hide=function()
217
+ {if(typeof arguments[0]==='number'){prop['chart.line.visible'][arguments[0]]=false;}else if(typeof arguments[0]==='object'){for(var i=0;i<arguments[0].length;++i){prop['chart.line.visible'][arguments[0][i]]=false;}}else{for(var i=0;i<this.original_data.length;++i){prop['chart.line.visible'][i]=false;}}
218
+ RG.redraw();return this;};this.show=function()
219
+ {if(typeof arguments[0]==='number'){prop['chart.line.visible'][arguments[0]]=true;}else if(typeof arguments[0]==='object'){for(var i=0;i<arguments[0].length;++i){prop['chart.line.visible'][arguments[0][i]]=true;}}else{for(var i=0;i<this.original_data.length;++i){prop['chart.line.visible'][i]=true;}}
220
+ RG.redraw();return this;};this.hidden=function(index)
221
+ {return!prop['chart.line.visible'][index];};this.unfold=function()
222
+ {var obj=this;var opt=arguments[0]?arguments[0]:{};var frames=opt.frames?opt.frames:30;var frame=0;var callback=arguments[1]?arguments[1]:function(){};var initial=prop['chart.animation.unfold.initial'];prop['chart.animation.factor']=prop['chart.animation.unfold.initial'];function iterator()
223
+ {prop['chart.animation.factor']=((1-initial)*(frame/frames))+initial;RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(frame<frames){frame++;RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
224
+ iterator();return this;};this.trace=this.trace2=function()
225
+ {var obj=this;var callback=arguments[2];var opt=arguments[0]||{};var frames=opt.frames||30;var frame=0;var callback=arguments[1]||function(){};obj.Set('animation.trace.clip',0);function iterator()
226
+ {RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(frame++<frames){obj.Set('animation.trace.clip',frame/frames);RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
227
+ iterator();return this;};this.foldtocenter=this.foldToCenter=function()
228
+ {var obj=this;var opt=arguments[0]||{};var frames=opt.frames||30;var frame=0;var callback=arguments[1]||function(){};var center_value=obj.scale2.max/2;obj.Set('chart.ymax',obj.scale2.max);var original_data=RG.array_clone(obj.original_data);function iterator()
229
+ {for(var i=0,len=obj.data.length;i<len;++i){if(obj.data[i].length){for(var j=0,len2=obj.data[i].length;j<len2;++j){var dataset=obj.original_data[i];if(dataset[j]>center_value){dataset[j]=original_data[i][j]-((original_data[i][j]-center_value)*(frame/frames));}else{dataset[j]=original_data[i][j]+(((center_value-original_data[i][j])/frames)*frame);}}}}
230
+ RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas)
231
+ if(frame++<frames){RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
232
+ iterator();return this;};this.unfoldFromCenterTrace=this.unfoldFromCenterTrace2=function()
233
+ {var obj=this,opt=arguments[0]||{},frames=opt.frames||30,frame=0,data=RG.arrayClone(obj.original_data),callback=arguments[1]||function(){};obj.canvas.style.visibility='hidden';obj.draw();var max=obj.scale2.max;RG.clear(obj.canvas);obj.canvas.style.visibility='visible';var unfoldCallback=function()
234
+ {obj.original_data=data;obj.unfoldFromCenter({frames:frames/2},callback);};var half=obj.Get('chart.xaxispos')=='center'?obj.min:((obj.max-obj.min)/2)+obj.min;obj.Set('chart.ymax',obj.max);for(var i=0,len=obj.original_data.length;i<len;++i){for(var j=0;j<obj.original_data[i].length;++j){obj.original_data[i][j]=(obj.Get('chart.filled')&&obj.Get('chart.filled.accumulative')&&i>0)?0:half;}}
235
+ RG.clear(obj.canvas);obj.trace2({frames:frames/2},unfoldCallback);return obj;};this.unfoldFromCenter=function()
236
+ {var obj=this;var opt=arguments[0]||{};var frames=opt.frames||30;var frame=0;var callback=arguments[1]||function(){};obj.canvas.style.visibility='hidden';obj.Draw();var max=obj.scale2.max;RG.clear(obj.canvas);obj.canvas.style.visibility='visible';var center_value=obj.Get('chart.xaxispos')==='center'?prop['chart.ymin']:((obj.max-obj.min)/2)+obj.min;var original_data=RG.array_clone(obj.original_data);var steps=null;obj.Set('chart.ymax',max);if(!steps){steps=[];for(var dataset=0,len=original_data.length;dataset<len;++dataset){steps[dataset]=[]
237
+ for(var i=0,len2=original_data[dataset].length;i<len2;++i){if(prop['chart.filled']&&prop['chart.filled.accumulative']&&dataset>0){steps[dataset][i]=original_data[dataset][i]/frames;obj.original_data[dataset][i]=center_value;}else{steps[dataset][i]=(original_data[dataset][i]-center_value)/frames;obj.original_data[dataset][i]=center_value;}}}}
238
+ function unfoldFromCenter()
239
+ {for(var dataset=0;dataset<original_data.length;++dataset){for(var i=0;i<original_data[dataset].length;++i){obj.original_data[dataset][i]+=steps[dataset][i];}}
240
+ RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(--frames>0){RG.Effects.updateCanvas(unfoldFromCenter);}else{obj.original_data=RG.array_clone(original_data);RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);callback(obj);}}
241
+ unfoldFromCenter();return this;};RG.att(ca);RG.Register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}
242
+ for(var i=0;i<this.original_data.length;++i){prop['chart.line.visible'][i]=true;}};