rgraph-rails 4.62 → 4.64

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