rgraph-rails 1.0.5 → 1.0.6

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