rgraph-rails 4.62 → 4.64

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