rgraph-rails 5.00 → 6.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish-geml.yaml +46 -0
  3. data/.gitignore +1 -0
  4. data/README.md +4 -5
  5. data/lib/rgraph-rails/version.rb +1 -1
  6. data/rgraph-rails.gemspec +4 -4
  7. data/vendor/assets/javascripts/RGraph.activity.js +1691 -0
  8. data/vendor/assets/javascripts/RGraph.bar.js +4253 -236
  9. data/vendor/assets/javascripts/RGraph.bipolar.js +3958 -162
  10. data/vendor/assets/javascripts/RGraph.common.annotate.js +414 -35
  11. data/vendor/assets/javascripts/RGraph.common.context.js +635 -30
  12. data/vendor/assets/javascripts/RGraph.common.core.js +10485 -419
  13. data/vendor/assets/javascripts/RGraph.common.csv.js +508 -27
  14. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1693 -90
  15. data/vendor/assets/javascripts/RGraph.common.effects.js +1629 -89
  16. data/vendor/assets/javascripts/RGraph.common.key.js +1003 -53
  17. data/vendor/assets/javascripts/RGraph.common.moment.js +5670 -0
  18. data/vendor/assets/javascripts/RGraph.common.sheets.js +541 -31
  19. data/vendor/assets/javascripts/RGraph.common.sheets.php +351 -0
  20. data/vendor/assets/javascripts/RGraph.common.starburst.js +382 -0
  21. data/vendor/assets/javascripts/RGraph.common.table.js +386 -0
  22. data/vendor/assets/javascripts/RGraph.common.tooltips.js +1433 -32
  23. data/vendor/assets/javascripts/RGraph.drawing.background.js +660 -35
  24. data/vendor/assets/javascripts/RGraph.drawing.circle.js +618 -34
  25. data/vendor/assets/javascripts/RGraph.drawing.image.js +857 -52
  26. data/vendor/assets/javascripts/RGraph.drawing.line.js +712 -0
  27. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +760 -38
  28. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +740 -37
  29. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +573 -36
  30. data/vendor/assets/javascripts/RGraph.drawing.poly.js +667 -36
  31. data/vendor/assets/javascripts/RGraph.drawing.rect.js +638 -34
  32. data/vendor/assets/javascripts/RGraph.drawing.text.js +672 -37
  33. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +653 -52
  34. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +714 -51
  35. data/vendor/assets/javascripts/RGraph.fuel.js +1149 -59
  36. data/vendor/assets/javascripts/RGraph.funnel.js +1277 -56
  37. data/vendor/assets/javascripts/RGraph.gantt.js +1646 -82
  38. data/vendor/assets/javascripts/RGraph.gauge.js +1773 -89
  39. data/vendor/assets/javascripts/RGraph.hbar.js +3869 -159
  40. data/vendor/assets/javascripts/RGraph.horseshoe.js +970 -0
  41. data/vendor/assets/javascripts/RGraph.hprogress.js +1829 -81
  42. data/vendor/assets/javascripts/RGraph.line.js +5293 -244
  43. data/vendor/assets/javascripts/RGraph.meter.js +1570 -77
  44. data/vendor/assets/javascripts/RGraph.modaldialog.js +300 -19
  45. data/vendor/assets/javascripts/RGraph.odo.js +1553 -68
  46. data/vendor/assets/javascripts/RGraph.pie.js +3273 -129
  47. data/vendor/assets/javascripts/RGraph.radar.js +2333 -108
  48. data/vendor/assets/javascripts/RGraph.rose.js +2685 -114
  49. data/vendor/assets/javascripts/RGraph.rscatter.js +1920 -80
  50. data/vendor/assets/javascripts/RGraph.scatter.js +4215 -171
  51. data/vendor/assets/javascripts/RGraph.segmented.js +1006 -0
  52. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1980 -59
  53. data/vendor/assets/javascripts/RGraph.svg.activity.js +1696 -0
  54. data/vendor/assets/javascripts/RGraph.svg.bar.js +2575 -77
  55. data/vendor/assets/javascripts/RGraph.svg.bipolar.js +3533 -106
  56. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +240 -21
  57. data/vendor/assets/javascripts/RGraph.svg.common.core.js +7105 -299
  58. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +408 -28
  59. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1291 -68
  60. data/vendor/assets/javascripts/RGraph.svg.common.key.js +451 -20
  61. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +543 -31
  62. data/vendor/assets/javascripts/RGraph.svg.common.table.js +391 -0
  63. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +1072 -23
  64. data/vendor/assets/javascripts/RGraph.svg.funnel.js +1151 -32
  65. data/vendor/assets/javascripts/RGraph.svg.gauge.js +1429 -34
  66. data/vendor/assets/javascripts/RGraph.svg.hbar.js +2692 -65
  67. data/vendor/assets/javascripts/RGraph.svg.horseshoe.js +969 -0
  68. data/vendor/assets/javascripts/RGraph.svg.line.js +2855 -86
  69. data/vendor/assets/javascripts/RGraph.svg.pie.js +1630 -58
  70. data/vendor/assets/javascripts/RGraph.svg.radar.js +1772 -58
  71. data/vendor/assets/javascripts/RGraph.svg.rose.js +2419 -83
  72. data/vendor/assets/javascripts/RGraph.svg.scatter.js +2280 -65
  73. data/vendor/assets/javascripts/RGraph.svg.segmented.js +930 -0
  74. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +1612 -29
  75. data/vendor/assets/javascripts/RGraph.svg.waterfall.js +1525 -50
  76. data/vendor/assets/javascripts/RGraph.thermometer.js +1411 -64
  77. data/vendor/assets/javascripts/RGraph.vprogress.js +1915 -81
  78. data/vendor/assets/javascripts/RGraph.waterfall.js +1896 -89
  79. data/vendor/assets/javascripts/financial-data.js +1067 -0
  80. metadata +37 -16
  81. data/.travis.yml +0 -11
  82. data/vendor/assets/javascripts/RGraph.common.deprecated.js +0 -35
  83. data/vendor/assets/javascripts/RGraph.common.resizing.js +0 -38
  84. data/vendor/assets/javascripts/RGraph.common.zoom.js +0 -15
  85. data/vendor/assets/javascripts/RGraph.cornergauge.js +0 -71
@@ -1,87 +1,2856 @@
1
+ 'version:2023-09-16 (6.14)';
2
+ //
3
+ // o--------------------------------------------------------------------------------o
4
+ // | This file is part of the RGraph package - you can learn more at: |
5
+ // | |
6
+ // | https://www.rgraph.net |
7
+ // | |
8
+ // | RGraph is licensed under the Open Source MIT license. That means that it's |
9
+ // | totally free to use and there are no restrictions on what you can do with it! |
10
+ // o--------------------------------------------------------------------------------o
11
+
12
+ RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
13
+ RGraph.SVG = RGraph.SVG || {};
14
+
15
+ // Module pattern
16
+ (function (win, doc, undefined)
17
+ {
18
+ RGraph.SVG.Line = function (conf)
19
+ {
20
+ //
21
+ // A setter that the constructor uses (at the end)
22
+ // to set all of the properties
23
+ //
24
+ // @param string name The name of the property to set
25
+ // @param string value The value to set the property to
26
+ //
27
+ this.set = function (name, value)
28
+ {
29
+ if (arguments.length === 1 && typeof name === 'object') {
30
+ for (i in arguments[0]) {
31
+ if (typeof i === 'string') {
32
+
33
+ name = ret.name;
34
+ value = ret.value;
35
+
36
+ this.set(name, value);
37
+ }
38
+ }
39
+ } else {
40
+
41
+ var ret = RGraph.SVG.commonSetter({
42
+ object: this,
43
+ name: name,
44
+ value: value
45
+ });
46
+
47
+ name = ret.name;
48
+ value = ret.value;
49
+
50
+ this.properties[name] = value;
51
+
52
+ // If setting the colors, update the originalColors
53
+ // property too
54
+ if (name === 'colors') {
55
+ this.originalColors = RGraph.SVG.arrayClone(value);
56
+ this.colorsParsed = false;
57
+ }
58
+ }
59
+
60
+ return this;
61
+ };
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+ //
71
+ // A getter.
72
+ //
73
+ // @param name string The name of the property to get
74
+ //
75
+ this.get = function (name)
76
+ {
77
+ return this.properties[name];
78
+ };
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+ this.id = conf.id;
88
+ this.uid = RGraph.SVG.createUID();
89
+ this.container = document.getElementById(this.id);
90
+ this.layers = {}; // MUST be before the SVG tag is created!
91
+ this.svg = RGraph.SVG.createSVG({object: this,container: this.container});
92
+ this.isRGraph = true;
93
+ this.isrgraph = true;
94
+ this.rgraph = true;
95
+ this.width = Number(this.svg.getAttribute('width'));
96
+ this.height = Number(this.svg.getAttribute('height'));
97
+ this.firstDraw = true; // After the first draw this will be false
98
+
99
+
100
+
101
+
102
+ // Convert strings to numbers
103
+ conf.data = RGraph.SVG.stringsToNumbers(conf.data);
104
+
105
+
106
+
107
+
108
+
109
+ // Convert single datasets to a multi-dimensional format
110
+ if (RGraph.SVG.isArray(conf.data) && RGraph.SVG.isArray(conf.data[0])) {
111
+ this.data = RGraph.SVG.arrayClone(conf.data);
112
+ } else if (RGraph.SVG.isArray(conf.data)) {
113
+ this.data = [RGraph.SVG.arrayClone(conf.data)];
114
+ } else {
115
+ this.data = [[]];
116
+ }
117
+
118
+ this.type = 'line';
119
+ this.coords = [];
120
+ this.coords2 = [];
121
+ this.coordsSpline = [];
122
+ this.hasMultipleDatasets = typeof this.data[0] === 'object' && typeof this.data[1] === 'object' ? true : false;
123
+ this.colorsParsed = false;
124
+ this.originalColors = {};
125
+ this.gradientCounter = 1;
126
+ this.originalData = RGraph.SVG.arrayClone(this.data);
127
+ this.filledGroups = [];
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+ // Add this object to the ObjectRegistry
136
+ RGraph.SVG.OR.add(this);
137
+
138
+ this.container.style.display = 'inline-block';
139
+
140
+ this.properties =
141
+ {
142
+ marginLeft: 35,
143
+ marginRight: 35,
144
+ marginTop: 35,
145
+ marginBottom: 35,
146
+ marginInner: 0,
147
+
148
+ backgroundColor: null,
149
+ backgroundImage: null,
150
+ backgroundImageStretch: true,
151
+ backgroundImageAspect: 'none',
152
+ backgroundImageOpacity: null,
153
+ backgroundImageX: null,
154
+ backgroundImageY: null,
155
+ backgroundImageW: null,
156
+ backgroundImageH: null,
157
+ backgroundGrid: true,
158
+ backgroundGridColor: '#ddd',
159
+ backgroundGridLinewidth: 1,
160
+ backgroundGridHlines: true,
161
+ backgroundGridHlinesCount: null,
162
+ backgroundGridVlines: true,
163
+ backgroundGridVlinesCount: null,
164
+ backgroundGridBorder: true,
165
+ backgroundGridDashed: false,
166
+ backgroundGridDotted: false,
167
+ backgroundGridDashArray: null,
168
+
169
+ colors: ['red', '#0f0', 'blue', '#ff0', '#0ff', 'green'],
170
+
171
+ filled: false,
172
+ filledDualColor: false,
173
+ filledColors: [],
174
+ filledClick: null,
175
+ filledOpacity: 1,
176
+ filledAccumulative: false,
177
+
178
+ yaxis: true,
179
+ yaxisTickmarks: true,
180
+ yaxisTickmarksLength: 3,
181
+ yaxisColor: 'black',
182
+ yaxisScale: true,
183
+ yaxisLabels: null,
184
+ yaxisLabelsOffsetx: 0,
185
+ yaxisLabelsOffsety: 0,
186
+ yaxisLabelsCount: 5,
187
+ yaxisScaleUnitsPre: '',
188
+ yaxisScaleUnitsPost: '',
189
+ yaxisScaleStrict: false,
190
+ yaxisScaleDecimals: 0,
191
+ yaxisScalePoint: '.',
192
+ yaxisScaleThousand: ',',
193
+ yaxisScaleRound: false,
194
+ yaxisScaleMax: null,
195
+ yaxisScaleMin: 0,
196
+ yaxisScaleFormatter: null,
197
+ yaxisLabelsFont: null,
198
+ yaxisLabelsSize: null,
199
+ yaxisLabelsColor: null,
200
+ yaxisLabelsBold: null,
201
+ yaxisLabelsItalic: null,
202
+ yaxisTitle: '',
203
+ yaxisTitleBold: null,
204
+ yaxisTitleSize: null,
205
+ yaxisTitleFont: null,
206
+ yaxisTitleColor: null,
207
+ yaxisTitleItalic: null,
208
+ yaxisTitleOffsetx: 0,
209
+ yaxisTitleOffsety: 0,
210
+ yaxisTitleX: null,
211
+ yaxisTitleY: null,
212
+ yaxisTitleHalign: null,
213
+ yaxisTitleValign: null,
214
+
215
+ xaxis: true,
216
+ xaxisTickmarks: true,
217
+ xaxisTickmarksLength: 5,
218
+ xaxisLabels: null,
219
+ xaxisLabelsOffsetx: 0,
220
+ xaxisLabelsOffsety: 0,
221
+ xaxisLabelsPosition: 'edge',
222
+ xaxisLabelsPositionEdgeTickmarksCount: null,
223
+ xaxisColor: 'black',
224
+ xaxisLabelsFont: null,
225
+ xaxisLabelsSize: null,
226
+ xaxisLabelsColor: null,
227
+ xaxisLabelsBold: null,
228
+ xaxisLabelsItalic: null,
229
+ xaxisLabelsFormattedDecimals: 0,
230
+ xaxisLabelsFormattedPoint: '.',
231
+ xaxisLabelsFormattedThousand: ',',
232
+ xaxisLabelsFormattedUnitsPre: '',
233
+ xaxisLabelsFormattedUnitsPost: '',
234
+ xaxisTitle: '',
235
+ xaxisTitleBold: null,
236
+ xaxisTitleSize: null,
237
+ xaxisTitleFont: null,
238
+ xaxisTitleColor: null,
239
+ xaxisTitleItalic: null,
240
+ xaxisTitleOffsetx: 0,
241
+ xaxisTitleOffsety: 0,
242
+ xaxisTitleX: null,
243
+ xaxisTitleY: null,
244
+ xaxisTitleHalign: null,
245
+ xaxisTitleValign: null,
246
+
247
+ textColor: 'black',
248
+ textFont: 'Arial, Verdana, sans-serif',
249
+ textSize: 12,
250
+ textBold: false,
251
+ textItalic: false,
252
+ text: null,
253
+
254
+ linewidth: 1,
255
+ linejoin: 'round',
256
+ linecap: 'round',
257
+
258
+ tooltips: null,
259
+ tooltipsOverride: null,
260
+ tooltipsEffect: 'fade',
261
+ tooltipsCssClass: 'RGraph_tooltip',
262
+ tooltipsCss: null,
263
+ tooltipsEvent: 'mousemove',
264
+ tooltipsFormattedThousand: ',',
265
+ tooltipsFormattedPoint: '.',
266
+ tooltipsFormattedDecimals: 0,
267
+ tooltipsFormattedUnitsPre: '',
268
+ tooltipsFormattedUnitsPost: '',
269
+ tooltipsFormattedKeyColors: null,
270
+ tooltipsFormattedKeyColorsShape: 'square',
271
+ tooltipsFormattedKeyLabels: [],
272
+ tooltipsFormattedTableHeaders: null,
273
+ tooltipsFormattedTableData: null,
274
+ tooltipsPointer: true,
275
+ tooltipsPointerOffsetx: 0,
276
+ tooltipsPointerOffsety: 0,
277
+ tooltipsPositionStatic: true,
278
+ tooltipsDataset: null,
279
+
280
+ //highlightStroke: 'rgba(0,0,0,0)',
281
+ //highlightFill: 'rgba(255,255,255,0.7)',
282
+ //highlightLinewidth: 1,
283
+
284
+ tickmarksStyle: 'none',
285
+ tickmarksSize: 5,
286
+ tickmarksFill: 'white',
287
+ tickmarksLinewidth: 1,
288
+
289
+ labelsAbove: false,
290
+ labelsAboveFont: null,
291
+ labelsAboveSize: null,
292
+ labelsAboveBold: null,
293
+ labelsAboveItalic: null,
294
+ labelsAboveColor: null,
295
+ labelsAboveBackground: 'rgba(255,255,255,0.7)',
296
+ labelsAboveBackgroundPadding: 2,
297
+ labelsAboveUnitsPre: null,
298
+ labelsAboveUnitsPost: null,
299
+ labelsAbovePoint: null,
300
+ labelsAboveThousand: null,
301
+ labelsAboveFormatter: null,
302
+ labelsAboveDecimals: null,
303
+ labelsAboveOffsetx: 0,
304
+ labelsAboveOffsety: -10,
305
+ labelsAboveHalign: 'center',
306
+ labelsAboveValign: 'bottom',
307
+ labelsAboveSpecific: null,
308
+
309
+ shadow: false,
310
+ shadowOffsetx: 2,
311
+ shadowOffsety: 2,
312
+ shadowBlur: 2,
313
+ shadowColor: 'rgba(0,0,0,0.25)',
314
+
315
+ spline: false,
316
+ stepped: false,
317
+
318
+ title: '',
319
+ titleX: null,
320
+ titleY: null,
321
+ titleHalign: 'center',
322
+ titleValign: null,
323
+ titleSize: null,
324
+ titleColor: null,
325
+ titleFont: null,
326
+ titleBold: null,
327
+ titleItalic: null,
328
+
329
+ titleSubtitle: null,
330
+ titleSubtitleSize: null,
331
+ titleSubtitleColor: '#aaa',
332
+ titleSubtitleFont: null,
333
+ titleSubtitleBold: null,
334
+ titleSubtitleItalic: null,
335
+
336
+ errorbars: null,
337
+ errorbarsColor: 'black',
338
+ errorbarsLinewidth: 1,
339
+ errorbarsCapwidth: 10,
340
+
341
+ key: null,
342
+ keyColors: null,
343
+ keyOffsetx: 0,
344
+ keyOffsety: 0,
345
+ keyLabelsOffsetx: 0,
346
+ keyLabelsOffsety: -1,
347
+ keyLabelsSize: null,
348
+ keyLabelsBold: null,
349
+ keyLabelsItalic: null,
350
+ keyLabelsFont: null,
351
+ keyLabelsColor: null,
352
+
353
+ dasharray: [1,0],
354
+ dashed: false,
355
+ dotted: false,
356
+
357
+ highlightFill: null,
358
+
359
+ trendline: false,
360
+ trendlineColors: ['#666'],
361
+ trendlineLinewidth: 1,
362
+ trendlineMargin: 25,
363
+ trendlineDashed: false,
364
+ trendlineDotted: false,
365
+ trendlineDashArray: null,
366
+ trendlineClip: true,
367
+
368
+ nullBridge: false,
369
+ nullBridgeLinewidth: null,
370
+ nullBridgeColors: null, // Can be null, a string or an object
371
+ nullBridgeDashArray: [5,5]
372
+ };
373
+
374
+
375
+
376
+
377
+ //
378
+ // Copy the global object properties to this instance
379
+ //
380
+ RGraph.SVG.getGlobals(this);
381
+
382
+
383
+
384
+
385
+
386
+ //
387
+ // "Decorate" the object with the generic effects if the effects library has been included
388
+ //
389
+ if (RGraph.SVG.FX && typeof RGraph.SVG.FX.decorate === 'function') {
390
+ RGraph.SVG.FX.decorate(this);
391
+ }
392
+
393
+
394
+
395
+
396
+
397
+ // Add the responsive function to the object
398
+ this.responsive = RGraph.SVG.responsive;
399
+
400
+
401
+
402
+
403
+
404
+ var properties = this.properties;
405
+
406
+
407
+
408
+
409
+
410
+
411
+
412
+
413
+ //
414
+ // The draw method draws the Bar chart
415
+ //
416
+ this.draw = function ()
417
+ {
418
+ // Fire the beforedraw event
419
+ RGraph.SVG.fireCustomEvent(this, 'onbeforedraw');
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+ // Should the first thing that's done inthe.draw() function
437
+ // except for the onbeforedraw event
438
+ this.width = Number(this.svg.getAttribute('width'));
439
+ this.height = Number(this.svg.getAttribute('height'));
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+ // Create the defs tag
457
+ RGraph.SVG.createDefs(this);
458
+
459
+
460
+
461
+
462
+
463
+ this.graphWidth = this.width - properties.marginLeft - properties.marginRight;
464
+ this.graphHeight = this.height - properties.marginTop - properties.marginBottom;
465
+
466
+
467
+
468
+ // Parse the colors for gradients
469
+ RGraph.SVG.resetColorsToOriginalValues({object:this});
470
+ this.parseColors();
471
+
472
+ // Clear the coords arrays
473
+ this.coords = [];
474
+ this.coords2 = [];
475
+ this.coordsSpline = [];
476
+
477
+ // Reset the data back to the original
478
+ this.data = RGraph.SVG.arrayClone(this.originalData);
479
+
480
+
481
+ // Set this to zero
482
+ this.tooltipsSequentialIndex = 0;
483
+
484
+
485
+
486
+
487
+ // If the line is set to be dotted or dashed then set the dash array
488
+ if (properties.dashed) {
489
+ properties.dasharray = [5,5];
490
+ }
491
+ if (properties.dotted) {
492
+ properties.dasharray = [1,4];
493
+ }
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+ // Make the data sequential first
504
+ this.data_seq = RGraph.SVG.arrayLinearize(this.data);
505
+
506
+ // This allows the errorbars to be a variety of formats and convert
507
+ // them all into an array of objects which have the min and max
508
+ // properties set
509
+ if (properties.errorbars) {
510
+ // Go through the error bars and convert numbers to objects
511
+ for (var i=0; i<this.data_seq.length; ++i) {
512
+
513
+ if (typeof properties.errorbars[i] === 'undefined' || RGraph.SVG.isNull(properties.errorbars[i]) ) {
514
+ properties.errorbars[i] = {max: null, min: null};
515
+
516
+ } else if (typeof properties.errorbars[i] === 'number') {
517
+ properties.errorbars[i] = {
518
+ min: properties.errorbars[i],
519
+ max: properties.errorbars[i]
520
+ };
521
+
522
+ // Max is undefined
523
+ } else if (typeof properties.errorbars[i] === 'object' && typeof properties.errorbars[i].max === 'undefined') {
524
+ properties.errorbars[i].max = null;
525
+
526
+ // Min is not defined
527
+ } else if (typeof properties.errorbars[i] === 'object' && typeof properties.errorbars[i].min === 'undefined') {
528
+ properties.errorbars[i].min = null;
529
+ }
530
+ }
531
+ }
532
+
533
+
534
+
535
+ // Go through all the data working out the max value
536
+ // whilst taking errorbars into account
537
+ for (var i=0,tmp=[]; i<this.data.length; ++i) {
538
+ for (var j=0; j<this.data[i].length; ++j) {
539
+
540
+ // Init the tmp array slot
541
+ if (typeof tmp[j] === 'undefined') {
542
+ tmp[j] = 0;
543
+ }
544
+
545
+ if (properties.filled && properties.filledAccumulative) {
546
+ tmp[j] += this.data[i][j];
547
+
548
+ // Only add this once (from the last dataset)
549
+ if (i === (this.data.length - 1) ) {
550
+ tmp[j] += (properties.errorbars ? properties.errorbars[RGraph.SVG.groupedIndexToSequential({object: this, dataset: i, index: j})].max : 0)
551
+ }
552
+ } else {
553
+ tmp[j] = Math.max(
554
+ tmp[j],
555
+ this.data[i][j] + (properties.errorbars ? properties.errorbars[RGraph.SVG.groupedIndexToSequential({object: this, dataset: i, index: j})].max : 0)
556
+ );
557
+ }
558
+
559
+ }
560
+ }
561
+
562
+
563
+ // Go through the data and work out the maximum value
564
+ var values = [];
565
+
566
+ // Go thru each dataset
567
+ for (var i=0,max=0; i<this.data.length; ++i) {
568
+
569
+ if (RGraph.SVG.isArray(this.data[i]) && !properties.filledAccumulative) {
570
+ values.push(RGraph.SVG.arrayMax(tmp));
571
+
572
+ } else if (RGraph.SVG.isArray(this.data[i]) && properties.filled && properties.filledAccumulative) {
573
+
574
+ for (var j=0; j<this.data[i].length; ++j) {
575
+ values[j] = values[j] || 0;
576
+ values[j] = values[j] + this.data[i][j];
577
+
578
+ // This adds values to prior values in order
579
+ // to create the stacking effect.
580
+ this.data[i][j] = values[j];
581
+ }
582
+ }
583
+ }
584
+
585
+
586
+
587
+ if (properties.filled && properties.filledAccumulative) {
588
+ var max = RGraph.SVG.arrayMax(tmp)
589
+ } else {
590
+ var max = RGraph.SVG.arrayMax(values);
591
+ }
592
+
593
+
594
+
595
+
596
+
597
+
598
+
599
+
600
+
601
+
602
+
603
+
604
+
605
+
606
+
607
+
608
+ // A custom, user-specified maximum value
609
+ if (typeof properties.yaxisScaleMax === 'number') {
610
+ max = properties.yaxisScaleMax;
611
+ }
612
+
613
+ // Set the ymin to zero if it's set mirror
614
+ if (properties.yaxisScaleMin === 'mirror') {
615
+ this.mirrorScale = true;
616
+ properties.yaxisScaleMin = 0;
617
+ }
618
+
619
+
620
+ //
621
+ // Generate an appropiate scale
622
+ //
623
+
624
+ this.scale = RGraph.SVG.getScale({
625
+ object: this,
626
+ numlabels: properties.yaxisLabelsCount,
627
+ unitsPre: properties.yaxisScaleUnitsPre,
628
+ unitsPost: properties.yaxisScaleUnitsPost,
629
+ max: max,
630
+ min: properties.yaxisScaleMin,
631
+ point: properties.yaxisScalePoint,
632
+ round: properties.yaxisScaleRound,
633
+ thousand: properties.yaxisScaleThousand,
634
+ decimals: properties.yaxisScaleDecimals,
635
+ strict: typeof properties.yaxisScaleMax === 'number',
636
+ formatter: properties.yaxisScaleFormatter
637
+ });
638
+
639
+
640
+
641
+ //
642
+ // Get the scale a second time if the ymin should be mirored
643
+ //
644
+ // Set the ymin to zero if it's szet mirror
645
+ if (this.mirrorScale) {
646
+ this.scale = RGraph.SVG.getScale({
647
+ object: this,
648
+ numlabels: properties.yaxisLabelsCount,
649
+ unitsPre: properties.yaxisScaleUnitsPre,
650
+ unitsPost: properties.yaxisScaleUnitsPost,
651
+ max: this.scale.max,
652
+ min: this.scale.max * -1,
653
+ point: properties.yaxisScalePoint,
654
+ round: false,
655
+ thousand: properties.yaxisScaleThousand,
656
+ decimals: properties.yaxisScaleDecimals,
657
+ strict: typeof properties.yaxisScaleMax === 'number',
658
+ formatter: properties.yaxisScaleFormatter
659
+ });
660
+ }
661
+
662
+ // Now the scale has been generated adopt its max value
663
+ this.max = this.scale.max;
664
+ this.min = this.scale.min;
665
+
666
+ // Taken out 14/01/18 so that the scale is not fixed across draws
667
+ //properties.yaxisScaleMax = this.scale.max;
668
+ //properties.yaxisScaleMin = this.scale.min;
669
+
670
+
671
+
672
+
673
+ // Draw the background first
674
+ RGraph.SVG.drawBackground(this);
675
+
676
+
677
+
678
+ //
679
+ // If the xaxisLabels option is a string then turn it
680
+ // into an array.
681
+ //
682
+ if (properties.xaxisLabels && properties.xaxisLabels.length) {
683
+ if (typeof properties.xaxisLabels === 'string') {
684
+ properties.xaxisLabels = RGraph.SVG.arrayPad(
685
+ [],
686
+ this.originalData[0].length,
687
+ properties.xaxisLabels
688
+ );
689
+ }
690
+
691
+ // Label substitution
692
+ //
693
+ for (var i=0; i<properties.xaxisLabels.length; ++i) {
694
+ properties.xaxisLabels[i] = RGraph.SVG.labelSubstitution({
695
+ object: this,
696
+ text: properties.xaxisLabels[i],
697
+ index: i,
698
+ value: this.originalData[0][i],
699
+ decimals: properties.xaxisLabelsFormattedDecimals || 0,
700
+ unitsPre: properties.xaxisLabelsFormattedUnitsPre || '',
701
+ unitsPost: properties.xaxisLabelsFormattedUnitsPost || '',
702
+ thousand: properties.xaxisLabelsFormattedThousand || ',',
703
+ point: properties.xaxisLabelsFormattedPoint || '.'
704
+ });
705
+ }
706
+ }
707
+
708
+
709
+
710
+ // Draw the axes over the bars
711
+ RGraph.SVG.drawXAxis(this);
712
+ RGraph.SVG.drawYAxis(this);
713
+
714
+
715
+ for (var i=0; i<this.data.length; ++i) {
716
+ this.drawLine(this.data[i], i);
717
+ }
718
+
719
+ // Always redraw the liines now so that tickmarks are drawn
720
+ this.redrawLines();
721
+
722
+
723
+
724
+
725
+
726
+
727
+
728
+
729
+
730
+ // Add trendlines if they have been enabled
731
+ for (let i=0; i<this.data.length; ++i) {
732
+
733
+ if ( (RGraph.SVG.isArray(properties.trendline) && properties.trendline[i])
734
+ || (!RGraph.SVG.isArray(properties.trendline) && properties.trendline)) {
735
+ this.drawTrendline({dataset: i});
736
+ }
737
+ }
738
+
739
+ //
740
+ // Bridge the null gaps if requested
741
+ //
742
+ if (properties.nullBridge) {
743
+ for (var i=0; i<this.data.length; ++i) {
744
+ this.nullBridge(i, this.data[i]);
745
+ }
746
+ }
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
+
756
+ // Draw the key
757
+ if (typeof properties.key !== null && RGraph.SVG.drawKey) {
758
+ RGraph.SVG.drawKey(this);
759
+ } else if (!RGraph.SVG.isNull(properties.key)) {
760
+ alert('The drawKey() function does not exist - have you forgotten to include the key library?');
761
+ }
762
+
763
+
764
+
765
+
766
+
767
+
768
+
769
+ // Draw the labelsAbove labels
770
+ this.drawLabelsAbove();
771
+
772
+
773
+
774
+
775
+
776
+ // Add the dataset nodes and the event listener
777
+ if (properties.tooltipsDataset) {
778
+ this.addDatasetTooltip();
779
+ }
1
780
 
2
- RGraph=window.RGraph||{isRGraph:true};RGraph.SVG=RGraph.SVG||{};(function(win,doc,undefined)
3
- {var RG=RGraph,ua=navigator.userAgent,ma=Math,win=window,doc=document;RG.SVG.Line=function(conf)
4
- {this.set=function(name,value)
5
- {if(arguments.length===1&&typeof name==='object'){for(i in arguments[0]){if(typeof i==='string'){name=ret.name;value=ret.value;this.set(name,value);}}}else{var ret=RG.SVG.commonSetter({object:this,name:name,value:value});name=ret.name;value=ret.value;this.properties[name]=value;if(name==='colors'){this.originalColors=RG.SVG.arrayClone(value);this.colorsParsed=false;}}
6
- return this;};this.get=function(name)
7
- {return this.properties[name];};this.id=conf.id;this.uid=RG.SVG.createUID();this.container=document.getElementById(this.id);this.layers={};this.svg=RG.SVG.createSVG({object:this,container:this.container});this.isRGraph=true;this.width=Number(this.svg.getAttribute('width'));this.height=Number(this.svg.getAttribute('height'));if(RG.SVG.isArray(conf.data)&&RG.SVG.isArray(conf.data[0])){this.data=RG.SVG.arrayClone(conf.data);}else if(RG.SVG.isArray(conf.data)){this.data=[RG.SVG.arrayClone(conf.data)];}else{this.data=[[]];}
8
- this.type='line';this.coords=[];this.coords2=[];this.coordsSpline=[];this.hasMultipleDatasets=typeof this.data[0]==='object'&&typeof this.data[1]==='object'?true:false;this.colorsParsed=false;this.originalColors={};this.gradientCounter=1;this.originalData=RG.SVG.arrayClone(this.data);this.filledGroups=[];this.propertyNameAliases={};RG.SVG.OR.add(this);this.container.style.display='inline-block';this.properties={marginLeft:35,marginRight:35,marginTop:35,marginBottom:35,backgroundColor:null,backgroundImage:null,backgroundImageStretch:true,backgroundImageAspect:'none',backgroundImageOpacity:null,backgroundImageX:null,backgroundImageY:null,backgroundImageW:null,backgroundImageH:null,backgroundGrid:true,backgroundGridColor:'#ddd',backgroundGridLinewidth:1,backgroundGridHlines:true,backgroundGridHlinesCount:null,backgroundGridVlines:true,backgroundGridVlinesCount:null,backgroundGridBorder:true,backgroundGridDashed:false,backgroundGridDotted:false,backgroundGridDashArray:null,colors:['red','#0f0','blue','#ff0','#0ff','green'],filled:false,filledColors:[],filledClick:null,filledOpacity:1,filledAccumulative:false,hmargin:0,yaxis:true,yaxisTickmarks:true,yaxisTickmarksLength:3,yaxisColor:'black',yaxisScale:true,yaxisLabels:null,yaxisLabelsOffsetx:0,yaxisLabelsOffsety:0,yaxisLabelsCount:5,yaxisScaleUnitsPre:'',yaxisScaleUnitsPost:'',yaxisScaleStrict:false,yaxisScaleDecimals:0,yaxisScalePoint:'.',yaxisScaleThousand:',',yaxisScaleRound:false,yaxisScaleMax:null,yaxisScaleMin:0,yaxisScaleFormatter:null,yaxisLabelsFont:null,yaxisLabelsSize:null,yaxisLabelsColor:null,yaxisLabelsBold:null,yaxisLabelsItalic:null,xaxis:true,xaxisTickmarks:true,xaxisTickmarksLength:5,xaxisLabels:null,xaxisLabelsOffsetx:0,xaxisLabelsOffsety:0,xaxisLabelsPosition:'edge',xaxisLabelsPositionEdgeTickmarksCount:null,xaxisColor:'black',xaxisLabelsFont:null,xaxisLabelsSize:null,xaxisLabelsColor:null,xaxisLabelsBold:null,xaxisLabelsItalic:null,textColor:'black',textFont:'Arial, Verdana, sans-serif',textSize:12,textBold:false,textItalic:false,linewidth:1,tooltips:null,tooltipsOverride:null,tooltipsEffect:'fade',tooltipsCssClass:'RGraph_tooltip',tooltipsEvent:'mousemove',tickmarksStyle:'none',tickmarksSize:5,tickmarksFill:'white',tickmarksLinewidth:1,labelsAbove:false,labelsAboveFont:null,labelsAboveSize:null,labelsAboveBold:null,labelsAboveItalic:null,labelsAboveColor:null,labelsAboveBackground:'rgba(255,255,255,0.7)',labelsAboveBackgroundPadding:2,labelsAboveUnitsPre:null,labelsAboveUnitsPost:null,labelsAbovePoint:null,labelsAboveThousand:null,labelsAboveFormatter:null,labelsAboveDecimals:null,labelsAboveOffsetx:0,labelsAboveOffsety:-10,labelsAboveHalign:'center',labelsAboveValign:'bottom',labelsAboveSpecific:null,shadow:false,shadowOffsetx:2,shadowOffsety:2,shadowBlur:2,shadowOpacity:0.25,spline:false,stepped:false,title:'',titleX:null,titleY:null,titleHalign:'center',titleValign:null,titleSize:null,titleColor:null,titleFont:null,titleBold:null,titleItalic:null,titleSubtitle:null,titleSubtitleX:null,titleSubtitleY:null,titleSubtitleHalign:'center',titleSubtitleValign:null,titleSubtitleSize:null,titleSubtitleColor:'#aaa',titleSubtitleFont:null,titleSubtitleBold:null,titleSubtitleItalic:null,errorbars:null,errorbarsColor:'black',errorbarsLinewidth:1,errorbarsCapwidth:10,key:null,keyColors:null,keyOffsetx:0,keyOffsety:0,keyLabelsOffsetx:0,keyLabelsOffsety:-1,keyLabelsSize:null,keyLabelsBold:null,keyLabelsItalic:null,keyLabelsFont:null,keyLabelsColor:null};RG.SVG.getGlobals(this);if(RG.SVG.FX&&typeof RG.SVG.FX.decorate==='function'){RG.SVG.FX.decorate(this);}
9
- var prop=this.properties;this.draw=function()
10
- {RG.SVG.fireCustomEvent(this,'onbeforedraw');this.width=Number(this.svg.getAttribute('width'));this.height=Number(this.svg.getAttribute('height'));RG.SVG.createDefs(this);this.graphWidth=this.width-prop.marginLeft-prop.marginRight;this.graphHeight=this.height-prop.marginTop-prop.marginBottom;RG.SVG.resetColorsToOriginalValues({object:this});this.parseColors();this.coords=[];this.coords2=[];this.coordsSpline=[];this.data=RG.SVG.arrayClone(this.originalData);this.tooltipsSequentialIndex=0;this.data_seq=RG.SVG.arrayLinearize(this.data);if(prop.errorbars){for(var i=0;i<this.data_seq.length;++i){if(typeof prop.errorbars[i]==='undefined'||RG.SVG.isNull(prop.errorbars[i])){prop.errorbars[i]={max:null,min:null};}else if(typeof prop.errorbars[i]==='number'){prop.errorbars[i]={min:prop.errorbars[i],max:prop.errorbars[i]};}else if(typeof prop.errorbars[i]==='object'&&typeof prop.errorbars[i].max==='undefined'){prop.errorbars[i].max=null;}else if(typeof prop.errorbars[i]==='object'&&typeof prop.errorbars[i].min==='undefined'){prop.errorbars[i].min=null;}}}
11
- for(var i=0,tmp=[];i<this.data.length;++i){for(var j=0;j<this.data[i].length;++j){if(typeof tmp[j]==='undefined'){tmp[j]=0;}
12
- if(prop.filled&&prop.filledAccumulative){tmp[j]+=this.data[i][j];if(i===(this.data.length-1)){tmp[j]+=(prop.errorbars?prop.errorbars[RG.SVG.groupedIndexToSequential({object:this,dataset:i,index:j})].max:0)}}else{tmp[j]=ma.max(tmp[j],this.data[i][j]+(prop.errorbars?prop.errorbars[RG.SVG.groupedIndexToSequential({object:this,dataset:i,index:j})].max:0));}}}
13
- var values=[];for(var i=0,max=0;i<this.data.length;++i){if(RG.SVG.isArray(this.data[i])&&!prop.filledAccumulative){values.push(RG.SVG.arrayMax(tmp));}else if(RG.SVG.isArray(this.data[i])&&prop.filled&&prop.filledAccumulative){for(var j=0;j<this.data[i].length;++j){values[j]=values[j]||0;values[j]=values[j]+this.data[i][j];this.data[i][j]=values[j];}}}
14
- if(prop.filled&&prop.filledAccumulative){var max=RG.SVG.arrayMax(tmp)}else{var max=RG.SVG.arrayMax(values);}
15
- if(typeof prop.yaxisScaleMax==='number'){max=prop.yaxisScaleMax;}
16
- if(prop.yaxisScaleMin==='mirror'){var mirrorScale=true;prop.yaxisScaleMin=0;}
17
- this.scale=RG.SVG.getScale({object:this,numlabels:prop.yaxisLabelsCount,unitsPre:prop.yaxisScaleUnitsPre,unitsPost:prop.yaxisScaleUnitsPost,max:max,min:prop.yaxisScaleMin,point:prop.yaxisScalePoint,round:prop.yaxisScaleRound,thousand:prop.yaxisScaleThousand,decimals:prop.yaxisScaleDecimals,strict:typeof prop.yaxisScaleMax==='number',formatter:prop.yaxisScaleFormatter});if(mirrorScale){this.scale=RG.SVG.getScale({object:this,numlabels:prop.yaxisLabelsCount,unitsPre:prop.yaxisScaleUnitsPre,unitsPost:prop.yaxisScaleUnitsPost,max:this.scale.max,min:this.scale.max* -1,point:prop.yaxisScalePoint,round:false,thousand:prop.yaxisScaleThousand,decimals:prop.yaxisScaleDecimals,strict:typeof prop.yaxisScaleMax==='number',formatter:prop.yaxisScaleFormatter});}
18
- this.max=this.scale.max;this.min=this.scale.min;RG.SVG.drawBackground(this);RG.SVG.drawXAxis(this);RG.SVG.drawYAxis(this);for(var i=0;i<this.data.length;++i){this.drawLine(this.data[i],i);}
19
- this.redrawLines();if(typeof prop.key!==null&&RG.SVG.drawKey){RG.SVG.drawKey(this);}else if(!RGraph.SVG.isNull(prop.key)){alert('The drawKey() function does not exist - have you forgotten to include the key library?');}
20
- this.drawLabelsAbove();var obj=this;document.body.addEventListener('mousedown',function(e)
21
- {RG.SVG.removeHighlight(obj);},false);RG.SVG.fireCustomEvent(this,'ondraw');return this;};this.drawLine=function(data,index)
22
- {var coords=[],path=[];for(var i=0,len=data.length;i<len;++i){var val=data[i],x=(((this.graphWidth-prop.hmargin-prop.hmargin)/(len-1))*i)+prop.marginLeft+prop.hmargin,y=this.getYCoord(val);coords.push([x,y]);}
23
- for(var i=0;i<coords.length;++i){if(i===0||RG.SVG.isNull(data[i])||RG.SVG.isNull(data[i-1])){var action='M';}else{if(prop.stepped){path.push('L {1} {2}'.format(coords[i][0],coords[i-1][1]));}
24
- var action='L';}
25
- path.push(action+'{1} {2}'.format(coords[i][0],RG.SVG.isNull(data[i])?0:coords[i][1]));}
26
- for(var k=0;k<coords.length;++k){this.coords.push(RG.SVG.arrayClone(coords[k]));this.coords[this.coords.length-1].x=coords[k][0];this.coords[this.coords.length-1].y=coords[k][1];this.coords[this.coords.length-1].object=this;this.coords[this.coords.length-1].value=data[k];this.coords[this.coords.length-1].index=k;this.coords[this.coords.length-1].path=path;}
27
- this.coords2[index]=RG.SVG.arrayClone(coords);for(var k=0;k<coords.length;++k){var seq=RG.SVG.groupedIndexToSequential({object:this,dataset:index,index:k});this.coords2[index][k].x=coords[k][0];this.coords2[index][k].y=coords[k][1];this.coords2[index][k].object=this;this.coords2[index][k].value=data[k];this.coords2[index][k].index=k;this.coords2[index][k].path=path;this.coords2[index][k].sequential=seq;if(prop.errorbars){this.drawErrorbar({object:this,dataset:index,index:k,sequential:seq,x:x,y:y});}}
28
- if(prop.spline){this.coordsSpline[index]=this.drawSpline(coords);}
29
- if(prop.filled===true||(typeof prop.filled==='object'&&prop.filled[index])){if(prop.spline){var fillPath=['M{1} {2}'.format(this.coordsSpline[index][0][0],this.coordsSpline[index][0][1])];for(var i=1;i<this.coordsSpline[index].length;++i){fillPath.push('L{1} {2}'.format(this.coordsSpline[index][i][0]+((i===(this.coordsSpline[index].length)-1)?1:0),this.coordsSpline[index][i][1]));}}else{var fillPath=RG.SVG.arrayClone(path);}
30
- fillPath.push('L{1} {2}'.format(this.coords2[index][this.coords2[index].length-1][0]+1,index>0&&prop.filledAccumulative?(prop.spline?this.coordsSpline[index-1][this.coordsSpline[index-1].length-1][1]:this.coords2[index-1][this.coords2[index-1].length-1][1]):this.getYCoord(prop.yaxisScaleMin>0?prop.yaxisScaleMin:0)+(prop.xaxis?0:1)));if(index>0&&prop.filledAccumulative){var path2=RG.SVG.arrayClone(path);if(index>0&&prop.filledAccumulative){if(prop.spline){for(var i=this.coordsSpline[index-1].length-1;i>=0;--i){fillPath.push('L{1} {2}'.format(this.coordsSpline[index-1][i][0],this.coordsSpline[index-1][i][1]));}}else{for(var i=this.coords2[index-1].length-1;i>=0;--i){fillPath.push('L{1} {2}'.format(this.coords2[index-1][i][0],this.coords2[index-1][i][1]));if(prop.stepped&&i>0){fillPath.push('L{1} {2}'.format(this.coords2[index-1][i][0],this.coords2[index-1][i-1][1]));}}}}}else{fillPath.push('L{1} {2}'.format(this.coords2[index][0][0]+(prop.yaxis?0:0),this.getYCoord(prop.yaxisScaleMin>0?prop.yaxisScaleMin:0)+(prop.xaxis?0:1)));}
31
- fillPath.push('L{1} {2}'.format(this.coords2[index][0][0]+(prop.yaxis?1:0),this.coords2[index][0][1]));for(var i=0;i<this.data[index].length;++i){if(!RG.SVG.isNull(this.data[index][i])){fillPath.push('L{1} {2}'.format(this.coords2[index][i][0],this.getYCoord(0)));break;}}
32
- this.filledGroups[index]=RG.SVG.create({svg:this.svg,type:'g',parent:this.svg.all,attr:{'class':'rgraph_filled_line_'+index}});var fillPathObject=RG.SVG.create({svg:this.svg,parent:this.filledGroups[index],type:'path',attr:{d:fillPath.join(' '),stroke:'rgba(0,0,0,0)','fill':prop.filledColors&&prop.filledColors[index]?prop.filledColors[index]:prop.colors[index],'fill-opacity':prop.filledOpacity,'stroke-width':1,'clip-path':this.isTrace?'url(#trace-effect-clip)':''}});if(prop.filledClick){var obj=this;fillPathObject.addEventListener('click',function(e)
33
- {prop.filledClick(e,obj,index);},false);fillPathObject.addEventListener('mousemove',function(e)
34
- {e.target.style.cursor='pointer';},false);}}
35
- if(prop.shadow){RG.SVG.setShadow({object:this,offsetx:prop.shadowOffsetx,offsety:prop.shadowOffsety,blur:prop.shadowBlur,opacity:prop.shadowOpacity,id:'dropShadow'});}
36
- if(prop.spline){var str=['M{1} {2}'.format(this.coordsSpline[index][0][0],this.coordsSpline[index][0][1])];for(var i=1;i<this.coordsSpline[index].length;++i){str.push('L{1} {2}'.format(this.coordsSpline[index][i][0],this.coordsSpline[index][i][1]));}
37
- str=str.join(' ');var line=RG.SVG.create({svg:this.svg,parent:prop.filled?this.filledGroups[index]:this.svg.all,type:'path',attr:{d:str,stroke:prop['colors'][index],'fill':'none','stroke-width':this.hasMultipleDatasets&&prop.filled&&prop.filledAccumulative?0.1:(RG.SVG.isArray(prop.linewidth)?prop.linewidth[index]:prop.linewidth+0.01),'stroke-linecap':'round','stroke-linejoin':'round',filter:prop.shadow?'url(#dropShadow)':'','clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}else{var path2=RG.SVG.arrayClone(path);if(prop.filled&&prop.filledAccumulative&&index>0){for(var i=this.coords2[index-1].length-1;i>=0;--i){path2.push('L{1} {2}'.format(this.coords2[index-1][i][0],this.coords2[index-1][i][1]));}}
38
- path2=path2.join(' ');var line=RG.SVG.create({svg:this.svg,parent:prop.filled?this.filledGroups[index]:this.svg.all,type:'path',attr:{d:path2,stroke:prop.colors[index],'fill':'none','stroke-width':this.hasMultipleDatasets&&prop.filled&&prop.filledAccumulative?0.1:(RG.SVG.isArray(prop.linewidth)?prop.linewidth[index]:prop.linewidth+0.01),'stroke-linecap':'round','stroke-linejoin':'round',filter:prop.shadow?'url(#dropShadow)':'','clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}
39
- if(prop.tooltips&&prop.tooltips.length){if(!this.svg.all.line_tooltip_hotspots){var group=RG.SVG.create({svg:this.svg,type:'g',attr:{'fill':'transparent',className:"rgraph_hotspots"},style:{cursor:'pointer'}});this.svg.all.line_tooltip_hotspots=group;}else{group=this.svg.all.line_tooltip_hotspots;}
40
- for(var i=0;i<this.coords2[index].length&&this.tooltipsSequentialIndex<prop.tooltips.length;++i,++this.tooltipsSequentialIndex){if(!RG.SVG.isNull(this.originalData[index][i])&&prop.tooltips[this.tooltipsSequentialIndex]&&this.coords2[index][i][0]&&this.coords2[index][i][1]){var hotspot=RG.SVG.create({svg:this.svg,parent:group,type:'circle',attr:{cx:this.coords2[index][i][0],cy:this.coords2[index][i][1],r:10,fill:'transparent','data-dataset':index,'data-index':i},style:{cursor:'pointer'}});var obj=this;(function(sequentialIndex)
41
- {hotspot.addEventListener(prop.tooltipsEvent,function(e)
42
- {var indexes=RG.SVG.sequentialIndexToGrouped(sequentialIndex,obj.data),index=indexes[1],dataset=indexes[0];if(RG.SVG.REG.get('tooltip')&&RG.SVG.REG.get('tooltip').__index__===index&&RG.SVG.REG.get('tooltip').__dataset__===dataset){return;}
43
- obj.removeHighlight();RG.SVG.hideTooltip();if(prop.tooltips[sequentialIndex]){var text=prop.tooltips[sequentialIndex];}
44
- RG.SVG.tooltip({object:obj,index:index,dataset:dataset,sequentialIndex:sequentialIndex,text:text,event:e});var outer_highlight1=RG.SVG.create({svg:obj.svg,parent:obj.svg.all,type:'circle',attr:{cx:obj.coords2[dataset][index][0],cy:obj.coords2[dataset][index][1],r:5,fill:obj.properties.colors[dataset],'fill-opacity':0.5},style:{cursor:'pointer'}});var outer_highlight2=RG.SVG.create({svg:obj.svg,parent:obj.svg.all,type:'circle',attr:{cx:obj.coords2[dataset][index][0],cy:obj.coords2[dataset][index][1],r:14,fill:'white','fill-opacity':0.75},style:{cursor:'pointer'}});var inner_highlight1=RG.SVG.create({svg:obj.svg,parent:obj.svg.all,type:'circle',attr:{cx:obj.coords2[dataset][index][0],cy:obj.coords2[dataset][index][1],r:6,fill:'white'},style:{cursor:'pointer'}});var inner_highlight2=RG.SVG.create({svg:obj.svg,parent:obj.svg.all,type:'circle',attr:{cx:obj.coords2[dataset][index][0],cy:obj.coords2[dataset][index][1],r:5,fill:obj.properties.colors[dataset]},style:{cursor:'pointer'}});RG.SVG.REG.set('highlight',[outer_highlight1,outer_highlight2,inner_highlight1,inner_highlight2]);},false);})(this.tooltipsSequentialIndex);}}}};this.drawTickmarks=function(index,data,coords)
45
- {var style=typeof prop.tickmarksStyle==='object'?prop.tickmarksStyle[index]:prop.tickmarksStyle,size=typeof prop.tickmarksSize==='object'?prop.tickmarksSize[index]:prop.tickmarksSize,fill=typeof prop.tickmarksFill==='object'?prop.tickmarksFill[index]:prop.tickmarksFill,linewidth=typeof prop.tickmarksLinewidth==='object'?prop.tickmarksLinewidth[index]:prop.tickmarksLinewidth;for(var i=0;i<data.length;++i){if(typeof data[i]==='number'){switch(style){case'filledcircle':case'filledendcircle':if(style==='filledcircle'||(i===0||i===data.length-1)){var circle=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'circle',attr:{cx:coords[index][i][0],cy:coords[index][i][1],r:size,'fill':prop.colors[index],filter:prop.shadow?'url(#dropShadow)':'','clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}
46
- break;case'circle':case'endcircle':if(style==='circle'||(style==='endcircle'&&(i===0||i===data.length-1))){var outerCircle=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'circle',attr:{cx:coords[index][i][0],cy:coords[index][i][1],r:size+this.get('linewidth'),'fill':prop.colors[index],filter:prop.shadow?'url(#dropShadow)':'','clip-path':this.isTrace?'url(#trace-effect-clip)':''}});var innerCircle=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'circle',attr:{cx:coords[index][i][0],cy:coords[index][i][1],r:size,'fill':fill,'clip-path':this.isTrace?'url(#trace-effect-clip)':''}});break;}
47
- break;case'endrect':case'rect':if(style==='rect'||(style==='endrect'&&(i===0||i===data.length-1))){var fill=typeof fill==='object'&&typeof fill[index]==='string'?fill[index]:fill;var rect=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'rect',attr:{x:coords[index][i][0]-size,y:coords[index][i][1]-size,width:size+size+linewidth,height:size+size+linewidth,'stroke-width':this.get('linewidth'),'stroke':prop.colors[index],'fill':fill,'clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}
48
- break;case'filledendrect':case'filledrect':if(style==='filledrect'||(style==='filledendrect'&&(i===0||i===data.length-1))){var fill=prop.colors[index];var rect=RG.SVG.create({svg:this.svg,parent:this.svg.all,type:'rect',attr:{x:coords[index][i][0]-size,y:coords[index][i][1]-size,width:size+size+linewidth,height:size+size+linewidth,'fill':fill,'clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}}}}};this.redrawLines=function()
49
- {if(prop.spline){for(var i=0;i<this.coordsSpline.length;++i){var linewidth=RG.SVG.isArray(prop.linewidth)?prop.linewidth[i]:prop.linewidth,color=prop['colors'][i],path='';for(var j=0;j<this.coordsSpline[i].length;++j){if(j===0){path+='M{1} {2} '.format(this.coordsSpline[i][j][0],this.coordsSpline[i][j][1]);}else{path+='L{1} {2} '.format(this.coordsSpline[i][j][0],this.coordsSpline[i][j][1]);}}
50
- RG.SVG.create({svg:this.svg,parent:prop.filled?this.filledGroups[i]:this.svg.all,type:'path',attr:{d:path,stroke:color,'fill':'none','stroke-width':linewidth+0.01,'stroke-linecap':'round','stroke-linejoin':'round',filter:prop.shadow?'url(#dropShadow)':'','clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}
51
- for(var dataset=0;dataset<this.coords2.length;++dataset){this.drawTickmarks(dataset,this.data[dataset],this.coords2);}}else{for(var i=0;i<this.coords2.length;++i){var linewidth=RG.SVG.isArray(prop.linewidth)?prop.linewidth[i]:prop.linewidth,color=prop['colors'][i],path='';for(var j=0;j<this.coords2[i].length;++j){if(j===0||RG.SVG.isNull(this.data[i][j])||RG.SVG.isNull(this.data[i][j-1])){path+='M{1} {2} '.format(this.coords2[i][j][0],RG.SVG.isNull(this.data[i][j])?0:this.coords2[i][j][1]);}else{if(prop.stepped){path+='L{1} {2} '.format(this.coords2[i][j][0],this.coords2[i][j-1][1]);}
52
- path+='L{1} {2} '.format(this.coords2[i][j][0],this.coords2[i][j][1]);}}
53
- RG.SVG.create({svg:this.svg,parent:prop.filled?this.filledGroups[i]:this.svg.all,type:'path',attr:{d:path,stroke:color,'fill':'none','stroke-width':linewidth+0.01,'stroke-linecap':'round','stroke-linejoin':'round',filter:prop.shadow?'url(#dropshadow)':'','clip-path':this.isTrace?'url(#trace-effect-clip)':''}});}
54
- for(var dataset=0;dataset<this.coords2.length;++dataset){this.drawTickmarks(dataset,this.data[dataset],this.coords2);}}};this.getYCoord=function(value)
55
- {var prop=this.properties,y;if(value>this.scale.max){return null;}
56
- if(value<this.scale.min){return null;}
57
- y=((value-this.scale.min)/(this.scale.max-this.scale.min));y*=(this.height-prop.marginTop-prop.marginBottom);y=this.height-prop.marginBottom-y;return y;};this.highlight=function(rect)
58
- {var x=rect.getAttribute('x'),y=rect.getAttribute('y');};this.removeHighlight=function()
59
- {var highlight=RG.SVG.REG.get('highlight');if(highlight&&highlight.parentNode){highlight.parentNode.removeChild(highlight);}else if(highlight){for(var i=0;i<highlight.length;++i){if(highlight[i]&&highlight[i].parentNode){highlight[i].parentNode.removeChild(highlight[i]);}}}
60
- RG.SVG.REG.set('highlight',null);};this.drawSpline=function(coords)
61
- {var xCoords=[];marginLeft=prop.marginLeft,marginRight=prop.marginRight,hmargin=prop.hmargin,interval=(this.graphWidth-(2*hmargin))/(coords.length-1),coordsSpline=[];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]);}}
62
- var P=[coords[0]];for(var i=0;i<coords.length;++i){P.push(coords[i]);}
63
- 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))+marginLeft+hmargin);coordsSpline.push([xCoords[xCoords.length-1],yCoord]);if(typeof index==='number'){coordsSpline[index].push([xCoords[xCoords.length-1],yCoord]);}}}
64
- coordsSpline.push([((j-1)*interval)+marginLeft+hmargin,P[j]]);if(typeof index==='number'){coordsSpline.push([((j-1)*interval)+marginLeft+hmargin,P[j]]);}
65
- function spline(t,P0,P1,P2,P3)
66
- {return 0.5*((2*P1)+
67
- ((0-P0)+P2)*t+
68
- ((2*P0-(5*P1)+(4*P2)-P3)*(t*t)+
69
- ((0-P0)+(3*P1)-(3*P2)+P3)*(t*t*t)));}
70
- for(var i=0;i<coordsSpline.length;++i){coordsSpline[i].object=this;coordsSpline[i].x=this;coordsSpline[i].y=this;}
71
- return coordsSpline;};this.parseColors=function()
72
- {if(!Object.keys(this.originalColors).length){this.originalColors={colors:RG.SVG.arrayClone(prop.colors),filledColors:RG.SVG.arrayClone(prop.filledColors),backgroundGridColor:RG.SVG.arrayClone(prop.backgroundGridColor),backgroundColor:RG.SVG.arrayClone(prop.backgroundColor)}}
73
- var colors=prop.colors;if(colors){for(var i=0;i<colors.length;++i){colors[i]=RG.SVG.parseColorLinear({object:this,color:colors[i]});}}
74
- var filledColors=prop.filledColors;if(filledColors){for(var i=0;i<filledColors.length;++i){filledColors[i]=RG.SVG.parseColorLinear({object:this,color:filledColors[i]});}}
75
- prop.backgroundGridColor=RG.SVG.parseColorLinear({object:this,color:prop.backgroundGridColor});prop.backgroundColor=RG.SVG.parseColorLinear({object:this,color:prop.backgroundColor});};this.drawLabelsAbove=function()
76
- {if(prop.labelsAbove){var data_seq=RG.SVG.arrayLinearize(this.data),seq=0;for(var dataset=0;dataset<this.coords2.length;++dataset,seq++){for(var i=0;i<this.coords2[dataset].length;++i,seq++){var str=RG.SVG.numberFormat({object:this,num:this.data[dataset][i].toFixed(prop.labelsAboveDecimals),prepend:typeof prop.labelsAboveUnitsPre==='string'?prop.labelsAboveUnitsPre:null,append:typeof prop.labelsAboveUnitsPost==='string'?prop.labelsAboveUnitsPost:null,point:typeof prop.labelsAbovePoint==='string'?prop.labelsAbovePoint:null,thousand:typeof prop.labelsAboveThousand==='string'?prop.labelsAboveThousand:null,formatter:typeof prop.labelsAboveFormatter==='function'?prop.labelsAboveFormatter:null});if(prop.labelsAboveSpecific&&prop.labelsAboveSpecific.length&&(typeof prop.labelsAboveSpecific[seq]==='string'||typeof prop.labelsAboveSpecific[seq]==='number')){str=prop.labelsAboveSpecific[seq];}else if(prop.labelsAboveSpecific&&prop.labelsAboveSpecific.length&&typeof prop.labelsAboveSpecific[seq]!=='string'&&typeof prop.labelsAboveSpecific[seq]!=='number'){continue;}
77
- RG.SVG.text({object:this,parent:this.svg.all,tag:'labels.above',text:str,x:parseFloat(this.coords2[dataset][i][0])+prop.labelsAboveOffsetx,y:parseFloat(this.coords2[dataset][i][1])+prop.labelsAboveOffsety,halign:prop.labelsAboveHalign,valign:prop.labelsAboveValign,font:prop.labelsAboveFont||prop.textFont,size:typeof prop.labelsAboveSize==='number'?prop.labelsAboveSize:prop.textSize,bold:typeof prop.labelsAboveBold==='boolean'?prop.labelsAboveBold:prop.textBold,italic:typeof prop.labelsAboveItalic==='boolean'?prop.labelsAboveItalic:prop.textItalic,color:prop.labelsAboveColor||prop.textColor,background:prop.labelsAboveBackground||null,padding:prop.labelsAboveBackgroundPadding||0});}
78
- seq--;}}};this.on=function(type,func)
79
- {if(type.substr(0,2)!=='on'){type='on'+type;}
80
- RG.SVG.addCustomEventListener(this,type,func);return this;};this.exec=function(func)
81
- {func(this);return this;};this.drawErrorbar=function(opt)
82
- {var linewidth=RG.SVG.getErrorbarsLinewidth({object:this,index:opt.index}),color=RG.SVG.getErrorbarsColor({object:this,index:opt.sequential}),capwidth=RG.SVG.getErrorbarsCapWidth({object:this,index:opt.index}),index=opt.index,dataset=opt.dataset,x=opt.x,y=opt.y,value=this.data[dataset][index];var y=this.getYCoord(y);var max=RG.SVG.getErrorbarsMaxValue({object:this,index:opt.sequential});var min=RG.SVG.getErrorbarsMinValue({object:this,index:opt.sequential});if(!max&&!min){return;}
83
- var x=this.coords2[dataset][index].x,y=this.coords2[dataset][index].y,halfCapWidth=capwidth/2,y1=this.getYCoord(value+max),y3=this.getYCoord(value-min)===null?y:this.getYCoord(value-min);if(max>0){var errorbarLine=RG.SVG.create({svg:this.svg,type:'line',parent:this.svg.all,attr:{x1:x,y1:y,x2:x,y2:y1,stroke:color,'stroke-width':linewidth}});var errorbarCap=RG.SVG.create({svg:this.svg,type:'line',parent:this.svg.all,attr:{x1:x-halfCapWidth,y1:y1,x2:x+halfCapWidth,y2:y1,stroke:color,'stroke-width':linewidth}});}
84
- if(typeof min==='number'){var errorbarLine=RG.SVG.create({svg:this.svg,type:'line',parent:this.svg.all,attr:{x1:x,y1:y,x2:x,y2:y3,stroke:color,'stroke-width':linewidth}});var errorbarCap=RG.SVG.create({svg:this.svg,type:'line',parent:this.svg.all,attr:{x1:x-halfCapWidth,y1:y3,x2:x+halfCapWidth,y2:y3,stroke:color,'stroke-width':linewidth}});}};this.trace=function()
85
- {var opt=arguments[0]||{},frame=1,frames=opt.frames||60,obj=this;this.isTrace=true;this.draw();var clippath=RG.SVG.create({svg:this.svg,parent:this.svg.defs,type:'clipPath',attr:{id:'trace-effect-clip'}});var clippathrect=RG.SVG.create({svg:this.svg,parent:clippath,type:'rect',attr:{x:0,y:0,width:0,height:this.height}});var iterator=function()
86
- {var width=(frame++)/frames*obj.width;clippathrect.setAttribute("width",width);if(frame<=frames){RG.SVG.FX.update(iterator);}else{clippath.parentNode.removeChild(clippath);if(opt.callback){(opt.callback)(obj);}}};iterator();return this;};for(i in conf.options){if(typeof i==='string'){this.set(i,conf.options[i]);}}}
87
- return this;})(window,document);
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+ // Add the event listener that clears the highlight if
789
+ // there is any. Must be MOUSEDOWN (ie before the click event)
790
+ var obj = this;
791
+ document.body.addEventListener('mousedown', function (e)
792
+ {
793
+ RGraph.SVG.removeHighlight(obj);
794
+
795
+ }, false);
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+
804
+ //
805
+ // Allow the addition of custom text via the
806
+ // text: property.
807
+ //
808
+ RGraph.SVG.addCustomText(this);
809
+
810
+
811
+
812
+
813
+
814
+ // Draw any custom lines that have been defined
815
+ RGraph.SVG.drawHorizontalLines(this);
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+ //
824
+ // Fire the onfirstdraw event
825
+ //
826
+ if (this.firstDraw) {
827
+ this.firstDraw = false;
828
+ RGraph.SVG.fireCustomEvent(this, 'onfirstdraw');
829
+ }
830
+
831
+
832
+
833
+
834
+ // Fire the draw event
835
+ RGraph.SVG.fireCustomEvent(this, 'ondraw');
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+ //
844
+ // Install any inline responsive configuration. This
845
+ // should be last in the draw function - even after
846
+ // the draw events.
847
+ //
848
+ RGraph.SVG.installInlineResponsive(this);
849
+
850
+
851
+
852
+
853
+
854
+
855
+
856
+
857
+
858
+
859
+
860
+ return this;
861
+ };
862
+
863
+
864
+
865
+
866
+
867
+
868
+
869
+
870
+ //
871
+ // New create() shortcut function
872
+ // For example:
873
+ // this.create('rect,x:0,y:0,width:100,height:100'[,parent]);
874
+ //
875
+ // @param str string The tag definition to parse and create
876
+ // @param object The (optional) parent element
877
+ //
878
+ // @return object The new tag
879
+ //
880
+ this.create = function (str)
881
+ {
882
+ var def = RGraph.SVG.create.parseStr(this, str);
883
+ def.svg = this.svg;
884
+
885
+ // By default the parent is the SVG tag - but if
886
+ // requested then change it to the tag that has
887
+ // been given
888
+ if (arguments[1]) {
889
+ def.parent = arguments[1];
890
+ }
891
+
892
+ return RGraph.SVG.create(def);
893
+ };
894
+
895
+
896
+
897
+
898
+
899
+
900
+
901
+
902
+ //
903
+ // Draws the bars
904
+ //
905
+ this.drawLine = function (data, index)
906
+ {
907
+ var coords = [],
908
+ path = [];
909
+
910
+ // Generate the coordinates
911
+ for (var i=0,len=data.length; i<len; ++i) {
912
+
913
+ var val = data[i],
914
+ x = (( (this.graphWidth - properties.marginInner - properties.marginInner) / (len - 1) ) * i) + properties.marginLeft + properties.marginInner,
915
+ y = this.getYCoord(val);
916
+
917
+ coords.push([x,y]);
918
+ }
919
+
920
+
921
+ // Go through the coordinates and create the path that draws the line
922
+ for (var i=0; i<coords.length; ++i) {
923
+
924
+ if (i === 0 || RGraph.SVG.isNull(data[i]) || RGraph.SVG.isNull(data[i - 1])) {
925
+ var action = 'M';
926
+
927
+ } else {
928
+ // STEPPED Add extra lines
929
+ if (properties.stepped) {
930
+ path.push('L {1} {2}'.format(
931
+ coords[i][0],
932
+ coords[i - 1][1]
933
+ ));
934
+ }
935
+ var action = 'L';
936
+ }
937
+
938
+ path.push(action + '{1} {2}'.format(
939
+ coords[i][0],
940
+ RGraph.SVG.isNull(data[i]) ? 0 : coords[i][1]
941
+ ));
942
+ }
943
+
944
+
945
+
946
+
947
+
948
+
949
+
950
+ //
951
+ // Add the coordinates to the coords array, coords2 array and if
952
+ // necessary, the coordsSpline array
953
+ //
954
+
955
+ // The coords array
956
+
957
+ for (var k=0; k<coords.length; ++k) {
958
+
959
+ this.coords.push(RGraph.SVG.arrayClone(coords[k]));
960
+
961
+ this.coords[this.coords.length - 1].x = coords[k][0];
962
+ this.coords[this.coords.length - 1].y = coords[k][1];
963
+ this.coords[this.coords.length - 1].object = this;
964
+ this.coords[this.coords.length - 1].value = data[k];
965
+ this.coords[this.coords.length - 1].index = k;
966
+ this.coords[this.coords.length - 1].path = path;
967
+ }
968
+
969
+ // The coords2 array
970
+ this.coords2[index] = RGraph.SVG.arrayClone(coords);
971
+
972
+ for (var k=0; k<coords.length; ++k) {
973
+
974
+ //Get the sequential index
975
+ var seq = RGraph.SVG.groupedIndexToSequential({
976
+ object: this,
977
+ dataset: index,
978
+ index: k
979
+ });
980
+
981
+ this.coords2[index][k].x = coords[k][0];
982
+ this.coords2[index][k].y = coords[k][1];
983
+ this.coords2[index][k].object = this;
984
+ this.coords2[index][k].value = data[k];
985
+ this.coords2[index][k].index = k;
986
+ this.coords2[index][k].path = path;
987
+ this.coords2[index][k].sequential = seq;
988
+
989
+ // Draw the errorbar if required
990
+ if (properties.errorbars) {
991
+ this.drawErrorbar({
992
+ object: this,
993
+ dataset: index,
994
+ index: k,
995
+ sequential: seq,
996
+ x: x,
997
+ y: y
998
+ });
999
+ }
1000
+ }
1001
+
1002
+
1003
+
1004
+
1005
+ // The coordsSpline array
1006
+ if (properties.spline) {
1007
+ this.coordsSpline[index] = this.drawSpline(coords);
1008
+ }
1009
+
1010
+
1011
+
1012
+
1013
+ // If the line should be filled, draw the fill part
1014
+ if (properties.filled === true || (typeof properties.filled === 'object' && properties.filled[index]) ) {
1015
+
1016
+ if (properties.spline) {
1017
+
1018
+ var fillPath = ['M{1} {2}'.format(
1019
+ this.coordsSpline[index][0][0],
1020
+ this.coordsSpline[index][0][1]
1021
+ )];
1022
+
1023
+ for (var i=1; i<this.coordsSpline[index].length; ++i) {
1024
+ fillPath.push('L{1} {2}'.format(
1025
+ this.coordsSpline[index][i][0] + ((i === (this.coordsSpline[index].length) - 1) ? 1 : 0),
1026
+ this.coordsSpline[index][i][1]
1027
+ ));
1028
+ }
1029
+
1030
+ } else {
1031
+ var fillPath = RGraph.SVG.arrayClone(path);
1032
+ }
1033
+
1034
+
1035
+ // Draw a line down to the X axis
1036
+ fillPath.push('L{1} {2}'.format(
1037
+ this.coords2[index][this.coords2[index].length - 1][0] + 1,
1038
+ index > 0 && properties.filledAccumulative ? (properties.spline ? this.coordsSpline[index - 1][this.coordsSpline[index - 1].length - 1][1] : this.coords2[index - 1][this.coords2[index - 1].length - 1][1]) : this.getYCoord(properties.yaxisScaleMin > 0 ? properties.yaxisScaleMin : 0) + (properties.xaxis ? 0 : 1)
1039
+ ));
1040
+
1041
+ if (index > 0 && properties.filledAccumulative) {
1042
+
1043
+ var path2 = RGraph.SVG.arrayClone(path);
1044
+
1045
+ if (index > 0 && properties.filledAccumulative) {
1046
+ if (properties.spline) {
1047
+ for (var i=this.coordsSpline[index - 1].length-1; i>=0; --i) {
1048
+ fillPath.push('L{1} {2}'.format(
1049
+ this.coordsSpline[index - 1][i][0],
1050
+ this.coordsSpline[index - 1][i][1]
1051
+ ));
1052
+ }
1053
+ } else {
1054
+ for (var i=this.coords2[index - 1].length-1; i>=0; --i) {
1055
+
1056
+ fillPath.push('L{1} {2}'.format(
1057
+ this.coords2[index - 1][i][0],
1058
+ this.coords2[index - 1][i][1]
1059
+ ));
1060
+
1061
+ // For STEPPED charts
1062
+ if (properties.stepped && i > 0) {
1063
+ fillPath.push('L{1} {2}'.format(
1064
+ this.coords2[index - 1][i][0],
1065
+ this.coords2[index - 1][i - 1][1]
1066
+ ));
1067
+ }
1068
+ }
1069
+ }
1070
+ }
1071
+ } else {
1072
+
1073
+ // This is the bottom left corner. The +1 is so that
1074
+ // the fill doesn't go over the axis
1075
+ fillPath.push('L{1} {2}'.format(
1076
+ this.coords2[index][0][0] + (properties.yaxis ? 0 : 0),
1077
+ // this.coords2[index][0][0] + (properties.yaxis ? 1 : 0),
1078
+ this.getYCoord(properties.yaxisScaleMin > 0 ? properties.yaxisScaleMin : 0) + (properties.xaxis ? 0 : 1)
1079
+ ));
1080
+ }
1081
+
1082
+ // Find the first none-null value and use that
1083
+ // values X value
1084
+ fillPath.push('L{1} {2}'.format(
1085
+ this.coords2[index][0][0] + (properties.yaxis ? 1 : 0),
1086
+ this.coords2[index][0][1]
1087
+ ));
1088
+
1089
+ for (var i=0; i<this.data[index].length; ++i) {
1090
+ if (!RGraph.SVG.isNull(this.data[index][i])) {
1091
+ fillPath.push('L{1} {2}'.format(
1092
+ this.coords2[index][i][0],
1093
+ this.getYCoord(0)
1094
+ ));
1095
+ break;
1096
+ }
1097
+ }
1098
+
1099
+ // Create a group that the fill is added to. Later the line
1100
+ // will also be added to it
1101
+ this.filledGroups[index] = RGraph.SVG.create({
1102
+ svg: this.svg,
1103
+ type: 'g',
1104
+ parent: this.svg.all,
1105
+ attr: {
1106
+ 'class': 'rgraph_filled_line_' + index
1107
+ }
1108
+ });
1109
+
1110
+ // Add the fill path to the scene
1111
+ var fillPathObject = RGraph.SVG.create({
1112
+ svg: this.svg,
1113
+ parent: this.filledGroups[index],
1114
+ type: 'path',
1115
+ attr: {
1116
+ d: fillPath.join(' '),
1117
+ stroke: 'rgba(0,0,0,0)',
1118
+ 'fill': properties.filledColors && properties.filledColors[index] ? properties.filledColors[index] : properties.colors[index],
1119
+ 'fill-opacity': properties.filledOpacity,
1120
+ 'stroke-width': 1,
1121
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1122
+ }
1123
+ });
1124
+
1125
+
1126
+ if (properties.filledClick) {
1127
+
1128
+ var obj = this;
1129
+ fillPathObject.addEventListener('click', function (e)
1130
+ {
1131
+ properties.filledClick(e, obj, index);
1132
+ }, false);
1133
+
1134
+ fillPathObject.addEventListener('mousemove', function (e)
1135
+ {
1136
+ e.target.style.cursor = 'pointer';
1137
+ }, false);
1138
+ }
1139
+ }
1140
+
1141
+
1142
+
1143
+
1144
+
1145
+
1146
+
1147
+
1148
+
1149
+ //
1150
+ // Create the drop shadow effect if its required
1151
+ //
1152
+ if (properties.shadow) {
1153
+ RGraph.SVG.setShadow({
1154
+ object: this,
1155
+ offsetx: properties.shadowOffsetx,
1156
+ offsety: properties.shadowOffsety,
1157
+ blur: properties.shadowBlur,
1158
+ color: properties.shadowColor,
1159
+ id: 'dropShadow'
1160
+ });
1161
+ }
1162
+
1163
+
1164
+
1165
+
1166
+
1167
+
1168
+
1169
+
1170
+
1171
+
1172
+
1173
+
1174
+
1175
+
1176
+ // Add the path to the scene
1177
+ if (properties.spline) {
1178
+
1179
+ // Make the raw coords into a path
1180
+ var str = ['M{1} {2}'.format(
1181
+ this.coordsSpline[index][0][0],
1182
+ this.coordsSpline[index][0][1]
1183
+ )];
1184
+
1185
+ for (var i=1; i<this.coordsSpline[index].length; ++i) {
1186
+ str.push('L{1} {2}'.format(
1187
+ this.coordsSpline[index][i][0],
1188
+ this.coordsSpline[index][i][1]
1189
+ ));
1190
+ }
1191
+
1192
+ str = str.join(' ');
1193
+
1194
+ var line = RGraph.SVG.create({
1195
+ svg: this.svg,
1196
+ parent: properties.filled ? this.filledGroups[index] : this.svg.all,
1197
+ type: 'path',
1198
+ attr: {
1199
+ d: str,
1200
+ stroke: properties['colors'][index],
1201
+ 'fill':'none',
1202
+ 'stroke-width': this.hasMultipleDatasets && properties.filled && properties.filledAccumulative ? 0.1 : (RGraph.SVG.isArray(properties.linewidth) ? properties.linewidth[index] : properties.linewidth + 0.01),
1203
+ 'stroke-dasharray': properties.dasharray,
1204
+ 'stroke-linecap': this.getLinecap({index: index}),
1205
+ 'stroke-linejoin': this.getLinejoin({index: index}),
1206
+ filter: properties.shadow ? 'url(#dropShadow)' : '',
1207
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1208
+ }
1209
+ });
1210
+
1211
+ } else {
1212
+
1213
+ var path2 = RGraph.SVG.arrayClone(path);
1214
+
1215
+ if (properties.filled && properties.filledAccumulative && index > 0) {
1216
+ for (var i=this.coords2[index - 1].length-1; i>=0; --i) {
1217
+ path2.push('L{1} {2}'.format(
1218
+ this.coords2[index - 1][i][0],
1219
+ this.coords2[index - 1][i][1]
1220
+ ));
1221
+ }
1222
+ }
1223
+
1224
+ path2 = path2.join(' ');
1225
+
1226
+ var line = RGraph.SVG.create({
1227
+ svg: this.svg,
1228
+ parent: properties.filled ? this.filledGroups[index] : this.svg.all,
1229
+ type: 'path',
1230
+ attr: {
1231
+ d: path2,
1232
+ stroke: properties.colors[index],
1233
+ 'fill':'none',
1234
+ 'stroke-dasharray': properties.dasharray,
1235
+ 'stroke-width': this.hasMultipleDatasets && properties.filled && properties.filledAccumulative ? 0.1 : (RGraph.SVG.isArray(properties.linewidth) ? properties.linewidth[index]: properties.linewidth + 0.01),
1236
+ 'stroke-linecap': this.getLinecap({index: index}),
1237
+ 'stroke-linejoin': this.getLinejoin({index: index}),
1238
+ filter: properties.shadow ? 'url(#dropShadow)' : '',
1239
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1240
+ }
1241
+ });
1242
+ }
1243
+
1244
+
1245
+
1246
+
1247
+
1248
+
1249
+ if (properties.tooltips && properties.tooltips.length) {
1250
+
1251
+ if (!this.svg.all.line_tooltip_hotspots) {
1252
+
1253
+ var group = RGraph.SVG.create({
1254
+ svg: this.svg,
1255
+
1256
+ // Taken out on 11/12/17 so that hotspots can sit in this group
1257
+ //parent: this.svg.all,
1258
+
1259
+ type: 'g',
1260
+ attr: {
1261
+ 'fill': 'transparent',
1262
+ className: "rgraph_hotspots"
1263
+ },
1264
+ style: {
1265
+ cursor: 'pointer'
1266
+ }
1267
+ });
1268
+
1269
+ // Store the group so it can be easily got to later on
1270
+ this.svg.all.line_tooltip_hotspots = group;
1271
+ } else {
1272
+ group = this.svg.all.line_tooltip_hotspots;
1273
+ }
1274
+
1275
+
1276
+ //for (var i=0; i<this.coords2[index].length; ++i,++this.tooltipsSequentialIndex) {
1277
+ for (var i=0;
1278
+ i<this.coords2[index].length && (typeof properties.tooltips === 'string' ? true : this.tooltipsSequentialIndex < properties.tooltips.length);
1279
+ ++i,++this.tooltipsSequentialIndex
1280
+ ) {
1281
+
1282
+ if (!RGraph.SVG.isNull(this.originalData[index][i]) && (properties.tooltips[this.tooltipsSequentialIndex] || typeof properties.tooltips === 'string') && this.coords2[index][i][0] && this.coords2[index][i][1]) {
1283
+
1284
+ var hotspot = RGraph.SVG.create({
1285
+ svg: this.svg,
1286
+ parent: group,
1287
+ type: 'circle',
1288
+ attr: {
1289
+ cx: this.coords2[index][i][0],
1290
+ cy: this.coords2[index][i][1],
1291
+ r: 10,
1292
+ fill: 'transparent',
1293
+ 'data-dataset': index,
1294
+ 'data-index': i
1295
+ },
1296
+ style: {
1297
+ cursor: 'pointer'
1298
+ }
1299
+ });
1300
+
1301
+ var obj = this;
1302
+ (function (sequentialIndex)
1303
+ {
1304
+ hotspot.addEventListener(properties.tooltipsEvent, function (e)
1305
+ {
1306
+ var indexes = RGraph.SVG.sequentialIndexToGrouped(sequentialIndex, obj.data),
1307
+ index = indexes[1],
1308
+ dataset = indexes[0];
1309
+
1310
+
1311
+ if (RGraph.SVG.REG.get('tooltip') &&
1312
+ RGraph.SVG.REG.get('tooltip').__index__ === index &&
1313
+ RGraph.SVG.REG.get('tooltip').__dataset__ === dataset &&
1314
+ RGraph.SVG.REG.get('tooltip').__object__.uid === obj.uid) { // Added on the 27th/6/2019
1315
+ return;
1316
+ }
1317
+
1318
+ obj.removeHighlight();
1319
+
1320
+ RGraph.SVG.hideTooltip();
1321
+
1322
+ RGraph.SVG.tooltip({
1323
+ object: obj,
1324
+ index: index,
1325
+ dataset: dataset,
1326
+ sequentialIndex: sequentialIndex,
1327
+ text: typeof properties.tooltips === 'string' ? properties.tooltips : properties.tooltips[sequentialIndex],
1328
+ event: e
1329
+ });
1330
+
1331
+
1332
+ // Highlight the chart here
1333
+ var outer_highlight1 = RGraph.SVG.create({
1334
+ svg: obj.svg,
1335
+ parent: obj.svg.all,
1336
+ type: 'circle',
1337
+ attr: {
1338
+ cx: obj.coords2[dataset][index][0],
1339
+ cy: obj.coords2[dataset][index][1],
1340
+ r: 5,
1341
+ fill: obj.properties.colors[dataset],
1342
+ 'fill-opacity': 0.5
1343
+ },
1344
+ style: {
1345
+ cursor: 'pointer'
1346
+ }
1347
+ });
1348
+
1349
+
1350
+ var outer_highlight2 = RGraph.SVG.create({
1351
+ svg: obj.svg,
1352
+ parent: obj.svg.all,
1353
+ type: 'circle',
1354
+ attr: {
1355
+ cx: obj.coords2[dataset][index][0],
1356
+ cy: obj.coords2[dataset][index][1],
1357
+ r: 14,
1358
+ fill: 'white',
1359
+ 'fill-opacity': 0.75
1360
+ },
1361
+ style: {
1362
+ cursor: 'pointer'
1363
+ }
1364
+ });
1365
+
1366
+
1367
+ var inner_highlight1 = RGraph.SVG.create({
1368
+ svg: obj.svg,
1369
+ parent: obj.svg.all,
1370
+ type: 'circle',
1371
+ attr: {
1372
+ cx: obj.coords2[dataset][index][0],
1373
+ cy: obj.coords2[dataset][index][1],
1374
+ r: 6,
1375
+ fill: 'white'
1376
+ },
1377
+ style: {
1378
+ cursor: 'pointer'
1379
+ }
1380
+ });
1381
+
1382
+
1383
+ var inner_highlight2 = RGraph.SVG.create({
1384
+ svg: obj.svg,
1385
+ parent: obj.svg.all,
1386
+ type: 'circle',
1387
+ attr: {
1388
+ cx: obj.coords2[dataset][index][0],
1389
+ cy: obj.coords2[dataset][index][1],
1390
+ r: 5,
1391
+ fill: typeof obj.properties.highlightFill === 'string' ? obj.properties.highlightFill : obj.properties.colors[dataset]
1392
+ },
1393
+ style: {
1394
+ cursor: 'pointer'
1395
+ }
1396
+ });
1397
+
1398
+ // Set the highlight in the registry
1399
+ RGraph.SVG.REG.set('highlight', [
1400
+ outer_highlight1,
1401
+ outer_highlight2,
1402
+ inner_highlight1,
1403
+ inner_highlight2
1404
+ ]);
1405
+
1406
+ }, false);
1407
+ })(this.tooltipsSequentialIndex);
1408
+
1409
+ }
1410
+ }
1411
+ }
1412
+ };
1413
+
1414
+
1415
+
1416
+
1417
+
1418
+
1419
+
1420
+
1421
+ //
1422
+ // Draws tickmarks
1423
+ //
1424
+ // @param number index The index of the line/dataset of coordinates
1425
+ // @param object data The origvinal line data points
1426
+ // @param object coords The coordinates of the points
1427
+ //
1428
+ this.drawTickmarks = function (index, data, coords)
1429
+ {
1430
+ var style = typeof properties.tickmarksStyle === 'object' ? properties.tickmarksStyle[index] : properties.tickmarksStyle,
1431
+ size = typeof properties.tickmarksSize === 'object' ? properties.tickmarksSize[index] : properties.tickmarksSize,
1432
+ fill = typeof properties.tickmarksFill === 'object' ? properties.tickmarksFill[index] : properties.tickmarksFill,
1433
+ linewidth = typeof properties.tickmarksLinewidth === 'object' ? properties.tickmarksLinewidth[index] : properties.tickmarksLinewidth;
1434
+
1435
+ for (var i=0; i<data.length; ++i) {
1436
+
1437
+ if (typeof data[i] === 'number') {
1438
+ switch (style) {
1439
+ case 'filledcircle':
1440
+ case 'filledendcircle':
1441
+ if (style === 'filledcircle' || (i === 0 || i === data.length - 1) ) {
1442
+ var circle = RGraph.SVG.create({
1443
+ svg: this.svg,
1444
+ parent: this.svg.all,
1445
+ type: 'circle',
1446
+ attr: {
1447
+ cx: coords[index][i][0],
1448
+ cy: coords[index][i][1],
1449
+ r: size,
1450
+ 'fill': properties.colors[index],
1451
+ filter: properties.shadow? 'url(#dropShadow)' : '',
1452
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1453
+ }
1454
+ });
1455
+ }
1456
+
1457
+
1458
+ break;
1459
+
1460
+ case 'circle':
1461
+ case 'endcircle':
1462
+
1463
+ if (style === 'circle' || (style === 'endcircle' && (i === 0 || i === data.length - 1)) ) {
1464
+
1465
+ var outerCircle = RGraph.SVG.create({
1466
+ svg: this.svg,
1467
+ parent: this.svg.all,
1468
+ type: 'circle',
1469
+ attr: {
1470
+ cx: coords[index][i][0],
1471
+ cy: coords[index][i][1],
1472
+ r: size + this.get('linewidth'),
1473
+ 'fill': properties.colors[index],
1474
+ filter: properties.shadow? 'url(#dropShadow)' : '',
1475
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1476
+ }
1477
+ });
1478
+
1479
+ var innerCircle = RGraph.SVG.create({
1480
+ svg: this.svg,
1481
+ parent: this.svg.all,
1482
+ type: 'circle',
1483
+ attr: {
1484
+ cx: coords[index][i][0],
1485
+ cy: coords[index][i][1],
1486
+ r: size,
1487
+ 'fill': fill,
1488
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1489
+ }
1490
+ });
1491
+
1492
+ break;
1493
+ }
1494
+ break;
1495
+
1496
+ case 'endrect':
1497
+ case 'rect':
1498
+ if (style === 'rect' || (style === 'endrect' && (i === 0 || i === data.length - 1)) ) {
1499
+
1500
+ var fill = typeof fill === 'object'&& typeof fill[index] === 'string' ? fill[index] : fill;
1501
+
1502
+ var rect = RGraph.SVG.create({
1503
+ svg: this.svg,
1504
+ parent: this.svg.all,
1505
+ type: 'rect',
1506
+ attr: {
1507
+ x: coords[index][i][0] - size,
1508
+ y: coords[index][i][1] - size,
1509
+ width: size + size + linewidth,
1510
+ height: size + size + linewidth,
1511
+ 'stroke-width': this.get('linewidth'),
1512
+ 'stroke': properties.colors[index],
1513
+ 'fill': fill,
1514
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1515
+ }
1516
+ });
1517
+ }
1518
+
1519
+ break;
1520
+
1521
+ case 'filledendrect':
1522
+ case 'filledrect':
1523
+ if (style === 'filledrect' || (style === 'filledendrect' && (i === 0 || i === data.length - 1)) ) {
1524
+
1525
+ var fill = properties.colors[index];
1526
+
1527
+ var rect = RGraph.SVG.create({
1528
+ svg: this.svg,
1529
+ parent: this.svg.all,
1530
+ type: 'rect',
1531
+ attr: {
1532
+ x: coords[index][i][0] - size,
1533
+ y: coords[index][i][1] - size,
1534
+ width: size + size + linewidth,
1535
+ height: size + size + linewidth,
1536
+ 'fill': fill,
1537
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1538
+ }
1539
+ });
1540
+ }
1541
+ }
1542
+ }
1543
+ }
1544
+ };
1545
+
1546
+
1547
+
1548
+
1549
+
1550
+
1551
+
1552
+
1553
+ //
1554
+ // Redraws the line in certain circumstances:
1555
+ // o filled
1556
+ // o filledAccumulative
1557
+ // o Multiple lines
1558
+ //
1559
+ this.redrawLines = function ()
1560
+ {
1561
+ if (properties.spline) {
1562
+ for (var i=0; i<this.coordsSpline.length; ++i) {
1563
+
1564
+ var linewidth = RGraph.SVG.isArray(properties.linewidth) ? properties.linewidth[i] : properties.linewidth,
1565
+ color = properties['colors'][i],
1566
+ path = '';
1567
+
1568
+ // Create the path from the spline coordinates
1569
+ for (var j=0; j<this.coordsSpline[i].length; ++j) {
1570
+ if (j === 0) {
1571
+ path += 'M{1} {2} '.format(
1572
+ this.coordsSpline[i][j][0],
1573
+ this.coordsSpline[i][j][1]
1574
+ );
1575
+ } else {
1576
+ path += 'L{1} {2} '.format(
1577
+ this.coordsSpline[i][j][0],
1578
+ this.coordsSpline[i][j][1]
1579
+ );
1580
+ }
1581
+ }
1582
+
1583
+
1584
+
1585
+ RGraph.SVG.create({
1586
+ svg: this.svg,
1587
+ parent: properties.filled ? this.filledGroups[i] : this.svg.all,
1588
+ type: 'path',
1589
+ attr: {
1590
+ d: path,
1591
+ stroke: color,
1592
+ 'fill':'none',
1593
+ 'stroke-dasharray': properties.dasharray,
1594
+ 'stroke-width': linewidth + 0.01,
1595
+ 'stroke-linecap': this.getLinecap({index: i}),
1596
+ 'stroke-linejoin': this.getLinejoin({index: i}),
1597
+ filter: properties.shadow ? 'url(#dropShadow)' : '',
1598
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1599
+ }
1600
+ });
1601
+ }
1602
+
1603
+
1604
+ // Now draw the tickmarks
1605
+ for (var dataset=0; dataset<this.coords2.length; ++dataset) {
1606
+ this.drawTickmarks(
1607
+ dataset,
1608
+ this.data[dataset],
1609
+ this.coords2
1610
+ );
1611
+ }
1612
+
1613
+ } else {
1614
+
1615
+
1616
+ for (var i=0; i<this.coords2.length; ++i) {
1617
+
1618
+ var linewidth = RGraph.SVG.isArray(properties.linewidth) ? properties.linewidth[i] : properties.linewidth,
1619
+ color = properties['colors'][i],
1620
+ path = '';
1621
+
1622
+ // Create the path from the coordinates
1623
+ for (var j=0; j<this.coords2[i].length; ++j) {
1624
+ if (j === 0 || RGraph.SVG.isNull(this.data[i][j]) || RGraph.SVG.isNull(this.data[i][j - 1])) {
1625
+ path += 'M{1} {2} '.format(
1626
+ this.coords2[i][j][0],
1627
+ RGraph.SVG.isNull(this.data[i][j]) ? 0 : this.coords2[i][j][1]
1628
+ );
1629
+ } else {
1630
+ if (properties.stepped) {
1631
+ path += 'L{1} {2} '.format(
1632
+ this.coords2[i][j][0],
1633
+ this.coords2[i][j - 1][1]
1634
+ );
1635
+ }
1636
+
1637
+ path += 'L{1} {2} '.format(
1638
+ this.coords2[i][j][0],
1639
+ this.coords2[i][j][1]
1640
+ );
1641
+ }
1642
+ }
1643
+
1644
+
1645
+
1646
+ RGraph.SVG.create({
1647
+ svg: this.svg,
1648
+ parent: properties.filled ? this.filledGroups[i] : this.svg.all,
1649
+ type: 'path',
1650
+ attr: {
1651
+ d: path,
1652
+ stroke: color,
1653
+ 'fill':'none',
1654
+ 'stroke-dasharray': properties.dasharray,
1655
+ 'stroke-width': linewidth + 0.01,
1656
+ 'stroke-linecap': this.getLinecap({index: i}),
1657
+ 'stroke-linejoin': this.getLinejoin({index: i}),
1658
+ filter: properties.shadow ? 'url(#dropshadow)' : '',
1659
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
1660
+ }
1661
+ });
1662
+
1663
+ }
1664
+
1665
+ // Now draw the tickmarks
1666
+ for (var dataset=0; dataset<this.coords2.length; ++dataset) {
1667
+ this.drawTickmarks(
1668
+ dataset,
1669
+ this.data[dataset],
1670
+ this.coords2
1671
+ );
1672
+ }
1673
+ }
1674
+ };
1675
+
1676
+
1677
+
1678
+
1679
+
1680
+
1681
+
1682
+
1683
+ //
1684
+ // This function can be used to retrieve the relevant Y coordinate for a
1685
+ // particular value.
1686
+ //
1687
+ // @param int value The value to get the Y coordinate for
1688
+ //
1689
+ this.getYCoord = function (value)
1690
+ {
1691
+ // You can now give a boolean true value to stipulate
1692
+ // that outofbounds values are allowed
1693
+ if (arguments[1] === true) {
1694
+ var allowOutOfBounds = true;
1695
+ }
1696
+
1697
+ var y;
1698
+
1699
+ if (!allowOutOfBounds && RGraph.SVG.isNull(value)) {
1700
+ return null;
1701
+ }
1702
+
1703
+ if (!allowOutOfBounds && value > this.scale.max) {
1704
+ return null;
1705
+ }
1706
+
1707
+ if (!allowOutOfBounds && value < this.scale.min) {
1708
+ return null;
1709
+ }
1710
+
1711
+ y = ((value - this.scale.min) / (this.scale.max - this.scale.min));
1712
+ y *= (this.height - properties.marginTop - properties.marginBottom);
1713
+
1714
+ y = this.height - properties.marginBottom - y;
1715
+
1716
+ return y;
1717
+ };
1718
+
1719
+
1720
+
1721
+
1722
+
1723
+
1724
+
1725
+
1726
+ //
1727
+ // This function can be used to highlight a bar on the chart
1728
+ //
1729
+ // TODO This function looks like its needs updating
1730
+ //
1731
+ // @param object rect The rectangle to highlight
1732
+ //
1733
+ this.highlight = function (rect)
1734
+ {
1735
+ var x = rect.getAttribute('x'),
1736
+ y = rect.getAttribute('y');
1737
+ };
1738
+
1739
+
1740
+
1741
+
1742
+
1743
+
1744
+
1745
+
1746
+ //
1747
+ // Remove highlight from the chart (tooltips)
1748
+ //
1749
+ this.removeHighlight = function ()
1750
+ {
1751
+ //var highlight = RGraph.SVG.REG.get('highlight');
1752
+
1753
+ //if (highlight && highlight.parentNode) {
1754
+ // highlight.parentNode.removeChild(highlight);
1755
+ //
1756
+ //// The highlight is an array
1757
+ //} else if (highlight && RGraph.SVG.isArray(highlight)) {
1758
+ // for (var i=0; i<highlight.length; ++i) {
1759
+ // if (highlight[i] && highlight[i].parentNode) {
1760
+ // highlight[i].parentNode.removeChild(highlight[i]);
1761
+ // }
1762
+ // }
1763
+ //}
1764
+
1765
+ //RGraph.SVG.REG.set('highlight', null);
1766
+
1767
+ RGraph.SVG.removeHighlight();
1768
+ };
1769
+
1770
+
1771
+
1772
+
1773
+
1774
+
1775
+
1776
+
1777
+ //
1778
+ // Draw a spline Line chart
1779
+ //
1780
+ // @param array coords The coords for the line
1781
+ //
1782
+ this.drawSpline = function (coords)
1783
+ {
1784
+ var xCoords = [];
1785
+ marginLeft = properties.marginLeft,
1786
+ marginRight = properties.marginRight,
1787
+ hmargin = properties.marginInner,
1788
+ interval = (this.graphWidth - (2 * hmargin)) / (coords.length - 1),
1789
+ coordsSpline = [];
1790
+
1791
+ //
1792
+ // The drawSpline function takes an array of JUST Y coords - not X/Y coords. So the line coords need converting
1793
+ // if we've been given X/Y pairs
1794
+ //
1795
+ for (var i=0,len=coords.length; i<len;i+=1) {
1796
+ if (typeof coords[i] == 'object' && coords[i] && coords[i].length == 2) {
1797
+ coords[i] = Number(coords[i][1]);
1798
+ }
1799
+ }
1800
+
1801
+ //
1802
+ // Get the Points array in the format we want - the first value should
1803
+ // be null along with the lst value
1804
+ //
1805
+ var P = [coords[0]];
1806
+ for (var i=0; i<coords.length; ++i) {
1807
+ P.push(coords[i]);
1808
+ }
1809
+ P.push(coords[coords.length - 1] + (coords[coords.length - 1] - coords[coords.length - 2]));
1810
+
1811
+ for (var j=1; j<P.length-2; ++j) {
1812
+ for (var t=0; t<10; ++t) {
1813
+
1814
+ var yCoord = spline( t/10, P[j-1], P[j], P[j+1], P[j+2] );
1815
+
1816
+ xCoords.push(((j-1) * interval) + (t * (interval / 10)) + marginLeft + hmargin);
1817
+
1818
+ coordsSpline.push([
1819
+ xCoords[xCoords.length - 1],
1820
+ yCoord
1821
+ ]);
1822
+
1823
+ if (typeof index === 'number') {
1824
+ coordsSpline[index].push([
1825
+ xCoords[xCoords.length - 1],
1826
+ yCoord
1827
+ ]);
1828
+ }
1829
+ }
1830
+ }
1831
+
1832
+
1833
+ // Draw the last section
1834
+ coordsSpline.push([
1835
+ ((j-1) * interval) + marginLeft + hmargin,
1836
+ P[j]
1837
+ ]);
1838
+
1839
+ if (typeof index === 'number') {
1840
+ coordsSpline.push([
1841
+ ((j-1) * interval) + marginLeft + hmargin,
1842
+ P[j]
1843
+ ]);
1844
+ }
1845
+
1846
+ function spline (t, P0, P1, P2, P3)
1847
+ {
1848
+ return 0.5 * ((2 * P1) +
1849
+ ((0-P0) + P2) * t +
1850
+ ((2*P0 - (5*P1) + (4*P2) - P3) * (t*t) +
1851
+ ((0-P0) + (3*P1)- (3*P2) + P3) * (t*t*t)));
1852
+ }
1853
+
1854
+ // Add some properties to the coordinates
1855
+ for (var i=0; i<coordsSpline.length; ++i) {
1856
+ coordsSpline[i].object = this;
1857
+ coordsSpline[i].x = this;
1858
+ coordsSpline[i].y = this;
1859
+ }
1860
+
1861
+ return coordsSpline;
1862
+ };
1863
+
1864
+
1865
+
1866
+
1867
+
1868
+
1869
+
1870
+
1871
+ //
1872
+ // This allows for easy specification of gradients
1873
+ //
1874
+ this.parseColors = function ()
1875
+ {
1876
+ if (!Object.keys(this.originalColors).length) {
1877
+ this.originalColors = {
1878
+ colors: RGraph.SVG.arrayClone(properties.colors),
1879
+ filledColors: RGraph.SVG.arrayClone(properties.filledColors),
1880
+ backgroundGridColor: RGraph.SVG.arrayClone(properties.backgroundGridColor),
1881
+ //highlightFill: RGraph.SVG.arrayClone(properties.highlightFill),
1882
+ backgroundColor: RGraph.SVG.arrayClone(properties.backgroundColor)
1883
+ }
1884
+ }
1885
+
1886
+ // colors
1887
+ var colors = properties.colors;
1888
+
1889
+ if (colors) {
1890
+ for (var i=0; i<colors.length; ++i) {
1891
+ colors[i] = RGraph.SVG.parseColorLinear({
1892
+ object: this,
1893
+ color: colors[i]
1894
+ });
1895
+ }
1896
+ }
1897
+
1898
+
1899
+ // Fill colors
1900
+ var filledColors = properties.filledColors;
1901
+
1902
+ if (filledColors) {
1903
+ for (var i=0; i<filledColors.length; ++i) {
1904
+ filledColors[i] = RGraph.SVG.parseColorLinear({
1905
+ object: this,
1906
+ color: filledColors[i]
1907
+ });
1908
+ }
1909
+ }
1910
+
1911
+ properties.backgroundGridColor = RGraph.SVG.parseColorLinear({object: this, color: properties.backgroundGridColor});
1912
+ //properties.highlightFill = RGraph.SVG.parseColorLinear({object: this, color: properties.highlightFill});
1913
+ properties.backgroundColor = RGraph.SVG.parseColorLinear({object: this, color: properties.backgroundColor});
1914
+ };
1915
+
1916
+
1917
+
1918
+
1919
+
1920
+
1921
+
1922
+
1923
+ //
1924
+ // Draws the labelsAbove
1925
+ //
1926
+ this.drawLabelsAbove = function ()
1927
+ {
1928
+ // Go through the above labels
1929
+ if (properties.labelsAbove) {
1930
+
1931
+ var data_seq = RGraph.SVG.arrayLinearize(this.data),
1932
+ seq = 0;
1933
+
1934
+ for (var dataset=0; dataset<this.coords2.length; ++dataset,seq++) {
1935
+ for (var i=0; i<this.coords2[dataset].length; ++i,seq++) {
1936
+
1937
+ var str = RGraph.SVG.numberFormat({
1938
+ object: this,
1939
+ num: this.data[dataset][i].toFixed(properties.labelsAboveDecimals ),
1940
+ prepend: typeof properties.labelsAboveUnitsPre === 'string' ? properties.labelsAboveUnitsPre : null,
1941
+ append: typeof properties.labelsAboveUnitsPost === 'string' ? properties.labelsAboveUnitsPost : null,
1942
+ point: typeof properties.labelsAbovePoint === 'string' ? properties.labelsAbovePoint : null,
1943
+ thousand: typeof properties.labelsAboveThousand === 'string' ? properties.labelsAboveThousand : null,
1944
+ formatter: typeof properties.labelsAboveFormatter === 'function' ? properties.labelsAboveFormatter : null
1945
+ });
1946
+
1947
+ // Facilitate labelsAboveSpecific
1948
+ if (properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && (typeof properties.labelsAboveSpecific[seq] === 'string' || typeof properties.labelsAboveSpecific[seq] === 'number') ) {
1949
+ str = properties.labelsAboveSpecific[seq];
1950
+ } else if ( properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && typeof properties.labelsAboveSpecific[seq] !== 'string' && typeof properties.labelsAboveSpecific[seq] !== 'number') {
1951
+ continue;
1952
+ }
1953
+
1954
+ // Get the text configuration for the above labels
1955
+ var textConf = RGraph.SVG.getTextConf({
1956
+ object: this,
1957
+ prefix: 'labelsAbove'
1958
+ });
1959
+
1960
+ RGraph.SVG.text({
1961
+ object: this,
1962
+ parent: this.svg.all,
1963
+ tag: 'labels.above',
1964
+ text: str,
1965
+ x: parseFloat(this.coords2[dataset][i][0]) + properties.labelsAboveOffsetx,
1966
+ y: parseFloat(this.coords2[dataset][i][1]) + properties.labelsAboveOffsety,
1967
+ halign: properties.labelsAboveHalign,
1968
+ valign: properties.labelsAboveValign,
1969
+ font: textConf.font,
1970
+ size: textConf.size,
1971
+ bold: textConf.bold,
1972
+ italic: textConf.italic,
1973
+ color: textConf.color,
1974
+ background: properties.labelsAboveBackground || null,
1975
+ padding: properties.labelsAboveBackgroundPadding || 0
1976
+ });
1977
+ }
1978
+
1979
+ // Necessary so that the seq doesn't get incremented twice
1980
+ seq--;
1981
+ }
1982
+ }
1983
+ };
1984
+
1985
+
1986
+
1987
+
1988
+
1989
+
1990
+
1991
+
1992
+ //
1993
+ // Using a function to add events makes it easier to facilitate method
1994
+ // chaining
1995
+ //
1996
+ // @param string type The type of even to add
1997
+ // @param function func
1998
+ //
1999
+ this.on = function (type, func)
2000
+ {
2001
+ if (type.substr(0,2) !== 'on') {
2002
+ type = 'on' + type;
2003
+ }
2004
+
2005
+ RGraph.SVG.addCustomEventListener(this, type, func);
2006
+
2007
+ return this;
2008
+ };
2009
+
2010
+
2011
+
2012
+
2013
+
2014
+
2015
+
2016
+
2017
+ //
2018
+ // Used in chaining. Runs a function there and then - not waiting for
2019
+ // the events to fire (eg the onbeforedraw event)
2020
+ //
2021
+ // @param function func The function to execute
2022
+ //
2023
+ this.exec = function (func)
2024
+ {
2025
+ func(this);
2026
+
2027
+ return this;
2028
+ };
2029
+
2030
+
2031
+
2032
+
2033
+
2034
+
2035
+
2036
+
2037
+ // This function is used to draw the errorbar. Its in the common
2038
+ // file because it's used by multiple chart libraries
2039
+ this.drawErrorbar = function (opt)
2040
+ {
2041
+ var linewidth = RGraph.SVG.getErrorbarsLinewidth({object: this, index: opt.index}),
2042
+ color = RGraph.SVG.getErrorbarsColor({object: this, index: opt.sequential}),
2043
+ capwidth = RGraph.SVG.getErrorbarsCapWidth({object: this, index: opt.index}),
2044
+ index = opt.index,
2045
+ dataset = opt.dataset,
2046
+ x = opt.x,
2047
+ y = opt.y,
2048
+ value = this.data[dataset][index];
2049
+
2050
+
2051
+ // Get the Y coord of the point
2052
+ var y = this.getYCoord(y);
2053
+
2054
+
2055
+
2056
+ // Get the error bar value
2057
+ var max = RGraph.SVG.getErrorbarsMaxValue({
2058
+ object: this,
2059
+ index: opt.sequential
2060
+ });
2061
+
2062
+
2063
+
2064
+ // Get the error bar value
2065
+ var min = RGraph.SVG.getErrorbarsMinValue({
2066
+ object: this,
2067
+ index: opt.sequential
2068
+ });
2069
+
2070
+
2071
+
2072
+
2073
+ if (!max && !min) {
2074
+ return;
2075
+ }
2076
+
2077
+
2078
+
2079
+
2080
+
2081
+
2082
+ var x = this.coords2[dataset][index].x,
2083
+ y = this.coords2[dataset][index].y,
2084
+ halfCapWidth = capwidth / 2,
2085
+ y1 = this.getYCoord(value + max),
2086
+ y3 = this.getYCoord(value - min) === null ? y : this.getYCoord(value - min);
2087
+
2088
+
2089
+ if (max > 0) {
2090
+
2091
+ // Draw the UPPER vertical line
2092
+ var errorbarLine = RGraph.SVG.create({
2093
+ svg: this.svg,
2094
+ type: 'line',
2095
+ parent: this.svg.all,
2096
+ attr: {
2097
+ x1: x,
2098
+ y1: y,
2099
+ x2: x,
2100
+ y2: y1,
2101
+ stroke: color,
2102
+ 'stroke-width': linewidth
2103
+ }
2104
+ });
2105
+
2106
+
2107
+ // Draw the cap to the UPPER line
2108
+ var errorbarCap = RGraph.SVG.create({
2109
+ svg: this.svg,
2110
+ type: 'line',
2111
+ parent: this.svg.all,
2112
+ attr: {
2113
+ x1: x - halfCapWidth,
2114
+ y1: y1,
2115
+ x2: x + halfCapWidth,
2116
+ y2: y1,
2117
+ stroke: color,
2118
+ 'stroke-width': linewidth
2119
+ }
2120
+ });
2121
+ }
2122
+
2123
+
2124
+
2125
+
2126
+
2127
+
2128
+
2129
+
2130
+
2131
+
2132
+
2133
+
2134
+ // Draw the minimum errorbar if necessary
2135
+ if (typeof min === 'number') {
2136
+
2137
+ var errorbarLine = RGraph.SVG.create({
2138
+ svg: this.svg,
2139
+ type: 'line',
2140
+ parent: this.svg.all,
2141
+ attr: {
2142
+ x1: x,
2143
+ y1: y,
2144
+ x2: x,
2145
+ y2: y3,
2146
+ stroke: color,
2147
+ 'stroke-width': linewidth
2148
+ }
2149
+ });
2150
+
2151
+ // Draw the cap to the UPPER line
2152
+ var errorbarCap = RGraph.SVG.create({
2153
+ svg: this.svg,
2154
+ type: 'line',
2155
+ parent: this.svg.all,
2156
+ attr: {
2157
+ x1: x - halfCapWidth,
2158
+ y1: y3,
2159
+ x2: x + halfCapWidth,
2160
+ y2: y3,
2161
+ stroke: color,
2162
+ 'stroke-width': linewidth
2163
+ }
2164
+ });
2165
+ }
2166
+ };
2167
+
2168
+
2169
+
2170
+
2171
+
2172
+
2173
+
2174
+
2175
+ //
2176
+ // A trace effect
2177
+ //
2178
+ // @param object Options to give to the effect
2179
+ // @param function A function to call when the effect has completed
2180
+ //
2181
+ this.trace = function ()
2182
+ {
2183
+ var opt = arguments[0] || {},
2184
+ frame = 1,
2185
+ frames = opt.frames || 60,
2186
+ obj = this;
2187
+
2188
+ this.isTrace = true;
2189
+
2190
+ this.draw();
2191
+
2192
+
2193
+
2194
+ // Create the clip area
2195
+ var clippath = RGraph.SVG.create({
2196
+ svg: this.svg,
2197
+ parent: this.svg.defs,
2198
+ type: 'clipPath',
2199
+ attr: {
2200
+ id: 'trace-effect-clip'
2201
+ }
2202
+ });
2203
+
2204
+ var clippathrect = RGraph.SVG.create({
2205
+ svg: this.svg,
2206
+ parent: clippath,
2207
+ type: 'rect',
2208
+ attr: {
2209
+ x: 0,
2210
+ y: 0,
2211
+ width: 0,
2212
+ height: this.height
2213
+ }
2214
+ });
2215
+
2216
+ var iterator = function ()
2217
+ {
2218
+ var width = (frame++) / frames * obj.width;
2219
+
2220
+ clippathrect.setAttribute("width", width);
2221
+
2222
+ if (frame <= frames) {
2223
+ RGraph.SVG.FX.update(iterator);
2224
+ } else {
2225
+
2226
+ // Remove the clippath
2227
+ clippath.parentNode.removeChild(clippath);
2228
+
2229
+ if (opt.callback) {
2230
+ (opt.callback)(obj);
2231
+ }
2232
+ }
2233
+ };
2234
+
2235
+ iterator();
2236
+
2237
+ return this;
2238
+ };
2239
+
2240
+
2241
+
2242
+
2243
+
2244
+
2245
+
2246
+
2247
+ //
2248
+ // A worker function that handles Bar chart specific tooltip substitutions
2249
+ //
2250
+ this.tooltipSubstitutions = function (opt)
2251
+ {
2252
+ var indexes = RGraph.SVG.sequentialIndexToGrouped(opt.index, this.data);
2253
+
2254
+ // Create the values array which contains each datasets value
2255
+ for (var i=0,values=[]; i<this.originalData.length; ++i) {
2256
+ values.push(this.originalData[i][indexes[1]]);
2257
+ }
2258
+
2259
+ return {
2260
+ index: indexes[1],
2261
+ dataset: indexes[0],
2262
+ sequentialIndex: opt.index,
2263
+ value: typeof this.data[indexes[0]] === 'number' ? this.data[indexes[0]] : this.data[indexes[0]][indexes[1]],
2264
+ values: values
2265
+ };
2266
+ };
2267
+
2268
+
2269
+
2270
+
2271
+
2272
+
2273
+
2274
+
2275
+ //
2276
+ // This allows for static tooltip positioning
2277
+ //
2278
+ this.positionTooltipStatic = function (args)
2279
+ {
2280
+ var obj = args.object,
2281
+ e = args.event,
2282
+ tooltip = args.tooltip,
2283
+ index = args.index,
2284
+ svgXY = RGraph.SVG.getSVGXY(obj.svg),
2285
+ coords = this.coords[args.index];
2286
+
2287
+ // Position the tooltip in the X direction
2288
+ args.tooltip.style.left = (
2289
+ svgXY[0] // The X coordinate of the canvas
2290
+ + coords[0] // The X coordinate of the bar on the chart
2291
+ - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width
2292
+ ) + 'px';
2293
+
2294
+ args.tooltip.style.top = (
2295
+ svgXY[1] // The Y coordinate of the canvas
2296
+ + coords[1] // The Y coordinate of the bar on the chart
2297
+ - tooltip.offsetHeight // The height of the tooltip
2298
+ - 15 // An arbitrary amount
2299
+ ) + 'px';
2300
+ };
2301
+
2302
+
2303
+
2304
+
2305
+
2306
+
2307
+
2308
+
2309
+ //
2310
+ // Draws a trendline on the Scatter chart. This is also known
2311
+ // as a "best-fit line"
2312
+ //
2313
+ // @param dataset The index of the dataset to use
2314
+ //
2315
+ this.drawTrendline = function (opt)
2316
+ {
2317
+ var obj = this,
2318
+ color = properties.trendlineColor,
2319
+ linewidth = properties.trendlineLinewidth,
2320
+ margin = properties.trendlineMargin,
2321
+ clip = properties.trendlineClip;
2322
+
2323
+ // Create the clipping region if necessary
2324
+ if (clip) {
2325
+
2326
+ // Create the clip area
2327
+ var clippath = RGraph.SVG.create({
2328
+ svg: this.svg,
2329
+ parent: this.svg.defs,
2330
+ type: 'clipPath',
2331
+ attr: {
2332
+ id: 'trendline-clip'
2333
+ }
2334
+ });
2335
+
2336
+ var clippathrect = RGraph.SVG.create({
2337
+ svg: this.svg,
2338
+ parent: clippath,
2339
+ type: 'rect',
2340
+ attr: {
2341
+ x: properties.marginLeft,
2342
+ y: properties.marginTop,
2343
+ width: this.width - properties.marginLeft - properties.marginRight,
2344
+ height: this.height - properties.marginTop - properties.marginBottom
2345
+ }
2346
+ });
2347
+ }
2348
+
2349
+
2350
+
2351
+ //
2352
+ // Create the pseudo-data array
2353
+ //
2354
+ var data=[];
2355
+
2356
+ // Create the data array from the given data and an
2357
+ // increasing X value
2358
+ for (var i=0; i<this.data.length; ++i) {
2359
+
2360
+ data[i] = [];
2361
+
2362
+ for (var j=0; j<this.data[i].length; ++j) {
2363
+ data[i].push([j, this.data[i][j]]);
2364
+ }
2365
+ }
2366
+
2367
+ // Allow for trendlineColors as well
2368
+ if (RGraph.SVG.isArray(properties.trendlineColors)) {
2369
+ color = properties.trendlineColors;
2370
+ }
2371
+
2372
+
2373
+
2374
+ // handle the options being arrays
2375
+ if (typeof color === 'object' && color[opt.dataset]) {
2376
+ color = color[opt.dataset];
2377
+ } else if (typeof color === 'object') {
2378
+ color = 'gray';
2379
+ }
2380
+
2381
+ if (typeof linewidth === 'object' && typeof linewidth[opt.dataset] === 'number') {
2382
+ linewidth = linewidth[opt.dataset];
2383
+ } else if (typeof linewidth === 'object') {
2384
+ linewidth = 1;
2385
+ }
2386
+
2387
+ if (typeof margin === 'object' && typeof margin[opt.dataset] === 'number') {
2388
+ margin = margin[opt.dataset];
2389
+ } else if (typeof margin === 'object'){
2390
+ margin = 25;
2391
+ }
2392
+
2393
+
2394
+ // Step 1: Calculate the mean values of the X coords and the Y coords
2395
+ for (var i=0,totalX=0,totalY=0; i<this.data[opt.dataset].length; ++i) {
2396
+ totalX += data[opt.dataset][i][0];
2397
+ totalY += data[opt.dataset][i][1];
2398
+ }
2399
+
2400
+ var averageX = totalX / data[opt.dataset].length;
2401
+ var averageY = totalY / data[opt.dataset].length;
2402
+
2403
+ // Step 2: Calculate the slope of the line
2404
+
2405
+ // a: The X/Y values minus the average X/Y value
2406
+ for (var i=0,xCoordMinusAverageX=[],yCoordMinusAverageY=[],valuesMultiplied=[],xCoordMinusAverageSquared=[]; i<this.data[opt.dataset].length; ++i) {
2407
+ xCoordMinusAverageX[i] = data[opt.dataset][i][0] - averageX;
2408
+ yCoordMinusAverageY[i] = data[opt.dataset][i][1] - averageY;
2409
+
2410
+ // b. Multiply the averages
2411
+ valuesMultiplied[i] = xCoordMinusAverageX[i] * yCoordMinusAverageY[i];
2412
+ xCoordMinusAverageSquared[i] = xCoordMinusAverageX[i] * xCoordMinusAverageX[i];
2413
+ }
2414
+
2415
+ var sumOfValuesMultiplied = RGraph.SVG.arraySum(valuesMultiplied);
2416
+ var sumOfXCoordMinusAverageSquared = RGraph.SVG.arraySum(xCoordMinusAverageSquared);
2417
+
2418
+ // Calculate m (???)
2419
+ var m = sumOfValuesMultiplied / sumOfXCoordMinusAverageSquared;
2420
+ var b = averageY - (m * averageX);
2421
+
2422
+ // y = mx + b
2423
+
2424
+ var coords = [
2425
+ [0, m * 0 + b],
2426
+ [data[0].length - 1, m * (data[0].length - 1) + b]
2427
+ ];
2428
+
2429
+ // Convert the X/Y numbers into coordinates
2430
+ coords[0][0] = properties.marginLeft;
2431
+ coords[0][1] = this.getYCoord(coords[0][1], true);
2432
+ coords[1][0] = this.width - properties.marginRight;
2433
+ coords[1][1] = this.getYCoord(coords[1][1], true);
2434
+
2435
+
2436
+
2437
+
2438
+
2439
+
2440
+
2441
+
2442
+
2443
+ //
2444
+ // Draw the line
2445
+ //
2446
+
2447
+ // Set dotted, dash or a custom dash array
2448
+ if ( properties.trendlineDashed === true
2449
+ || (RGraph.SVG.isArray(properties.trendlineDashed) && properties.trendlineDashed[opt.dataset]) ) {
2450
+ var dasharray = [4,4];
2451
+ }
2452
+
2453
+ if ( properties.trendlineDotted === true
2454
+ || (RGraph.SVG.isArray(properties.trendlineDotted) && properties.trendlineDotted[opt.dataset])) {
2455
+ var dasharray = [1,4];
2456
+ }
2457
+
2458
+ // Set a lineDash array. It can be an array of two numbers, or it can be a
2459
+ // multi-dimensional array, each of two numbers. One for each line on the
2460
+ // chart.
2461
+ if (RGraph.SVG.isArray(properties.trendlineDashArray)) {
2462
+ if ( properties.trendlineDashArray.length === 2
2463
+ && typeof properties.trendlineDashArray[0] === 'number'
2464
+ && typeof properties.trendlineDashArray[1] === 'number'
2465
+ ) {
2466
+ var dasharray = properties.trendlineDashArray;
2467
+
2468
+ } else if ( RGraph.SVG.isArray(properties.trendlineDashArray)
2469
+ && RGraph.SVG.isArray(properties.trendlineDashArray[opt.dataset])) {
2470
+ var dasharray = properties.trendlineDashArray[opt.dataset];
2471
+ }
2472
+ }
2473
+
2474
+
2475
+
2476
+
2477
+
2478
+
2479
+
2480
+
2481
+ // Draw the line
2482
+ var line = RGraph.SVG.create({
2483
+ svg: obj.svg,
2484
+ parent: obj.svg.all,
2485
+ type: 'line',
2486
+ attr: {
2487
+ x1: Math.max(coords[0][0], this.coords2[opt.dataset][0][0] - margin),
2488
+ y1: coords[0][1],
2489
+ x2: Math.min(coords[1][0], this.coords2[opt.dataset][this.coords2[opt.dataset].length - 1][0] + margin),
2490
+ y2: coords[1][1],
2491
+
2492
+ fill: 'rgba(0,0,0,0)',
2493
+ stroke: color,
2494
+ 'stroke-width': linewidth,
2495
+ 'stroke-dasharray': dasharray,
2496
+
2497
+ 'class': 'rgraph_line_{1}_trendline_{2}'.format(
2498
+ this.id,
2499
+ opt.dataset
2500
+ ),
2501
+ 'clip-path': clip ? 'url(#trendline-clip)' : ''
2502
+ }
2503
+ });
2504
+
2505
+ // Get a reference to the background gride <path> node
2506
+ var els = this.svg.getElementsByClassName('rgraph_background_grid'),
2507
+ grid = els[0];
2508
+
2509
+ // Remove the trendline from the DOM
2510
+ obj.svg.all.removeChild(line);
2511
+
2512
+ // Now re-add it immedately after the background grid
2513
+ grid.insertAdjacentElement('afterend', line);
2514
+ };
2515
+
2516
+
2517
+
2518
+
2519
+
2520
+
2521
+
2522
+
2523
+ //
2524
+ // This is the code the adds lines across null gaps in the
2525
+ // Line chart
2526
+ //
2527
+ // @param number datasetIdx The index of the dataset
2528
+ // @param array data The dataset
2529
+ //
2530
+ this.nullBridge = function (datasetIdx, data)
2531
+ {
2532
+ var readData = false;
2533
+
2534
+ //
2535
+ // Now add the connecting lines
2536
+ //
2537
+ for (var i=0; i<data.length; i++) {
2538
+
2539
+ var isNull = false,
2540
+ start = null,
2541
+ end = null;
2542
+
2543
+ // This ensures that the first datapoint is not null
2544
+ if (readData === false && RGraph.SVG.isNumber(data[i])) {
2545
+ readData = true;
2546
+ }
2547
+
2548
+ if (RGraph.SVG.isNull(data[i]) && readData) {
2549
+
2550
+ start = i - 1;
2551
+
2552
+ for (var j=(i+1); j<data.length; ++j) {
2553
+
2554
+ if (RGraph.SVG.isNull(data[j])) {
2555
+ continue;
2556
+ } else {
2557
+ end = j;
2558
+ }
2559
+
2560
+ // No idea why this if() condition is necessary but it
2561
+ // prevents an error occurring when the coordinate is
2562
+ // null
2563
+ if (this.coords2[datasetIdx][start][1]) {
2564
+ var path = 'M{1} {2} L{3} {4}'.format(
2565
+ this.coords2[datasetIdx][start][0], this.coords2[datasetIdx][start][1],
2566
+ this.coords2[datasetIdx][end][0], this.coords2[datasetIdx][end][1],
2567
+ );
2568
+ }
2569
+
2570
+ // Create the path and add it to the SVG document
2571
+ var node = RGraph.SVG.create({
2572
+ svg: this.svg,
2573
+ parent: this.svg.all,
2574
+ type: 'path',
2575
+ attr: {
2576
+ d: path,
2577
+ stroke: typeof properties.nullBridgeColors === 'string'
2578
+ ? properties.nullBridgeColors
2579
+ : ((typeof properties.nullBridgeColors === 'object' && !RGraph.SVG.isNull(properties.nullBridgeColors) && properties.nullBridgeColors[datasetIdx]) ? properties.nullBridgeColors[datasetIdx] : properties.colors[datasetIdx]),
2580
+ 'fill': 'transparent',
2581
+ 'stroke-dasharray': properties.nullBridgeDashArray,
2582
+ 'stroke-width': typeof properties.nullBridgeLinewidth === 'number' ? properties.nullBridgeLinewidth : properties.linewidth,
2583
+ 'stroke-linecap': this.getLinecap({index: i}),
2584
+ 'stroke-linejoin': this.getLinejoin({index: i}),
2585
+ 'clip-path': this.isTrace ? 'url(#trace-effect-clip)' : ''
2586
+ }
2587
+ });
2588
+
2589
+
2590
+
2591
+ start = null;
2592
+ end = null;
2593
+
2594
+ break;
2595
+ }
2596
+ }
2597
+ }
2598
+ };
2599
+
2600
+
2601
+
2602
+
2603
+
2604
+
2605
+
2606
+
2607
+ //
2608
+ // Add the dataset tooltip event listener
2609
+ //
2610
+ this.addDatasetTooltip = function ()
2611
+ {
2612
+ if (this.properties.spline) {
2613
+ var coords = this.coordsSpline;
2614
+ } else {
2615
+ var coords = this.coords2;
2616
+ }
2617
+
2618
+ for (let i=0; i<coords.length; ++i) {
2619
+
2620
+
2621
+
2622
+
2623
+
2624
+
2625
+
2626
+ // Create the path that is placed on
2627
+ // top of the line that facilitates
2628
+ // the click
2629
+ //
2630
+ var path = RGraph.SVG.create.pathString(coords[i]);
2631
+
2632
+
2633
+
2634
+
2635
+
2636
+
2637
+
2638
+
2639
+
2640
+ //
2641
+ // If the line chart is filled then create
2642
+ // an extra path that goes back over the
2643
+ // previous line in reverse. When doing
2644
+ // this for dataset 0 then there is no
2645
+ // previous dataset so just go back to the
2646
+ // right using the X axis coordinates
2647
+ //
2648
+ if (
2649
+ (properties.filled && properties.filledAccumulative && i === 0)
2650
+ ||
2651
+ (properties.filled && !properties.filledAccumulative)
2652
+ ) {
2653
+ path += 'L {1} {2}'.format(
2654
+ this.width - properties.marginRight - properties.marginInner,
2655
+ this.height - properties.marginBottom
2656
+ );
2657
+ path += 'L {1} {2}'.format(
2658
+ properties.marginLeft + properties.marginInner,
2659
+ this.height - properties.marginBottom
2660
+ );
2661
+
2662
+
2663
+
2664
+
2665
+ // Chart is filled, but this is not the
2666
+ // first dataset. So go back over the
2667
+ // previous dataset coordinates to get get
2668
+ // to the LHS
2669
+ } else if (properties.filled && properties.filledAccumulative && i > 0) {
2670
+ var previous_dataset = coords[i-1];
2671
+ path += RGraph.SVG.create.pathString(previous_dataset.toReversed(), 'L');
2672
+ }
2673
+
2674
+
2675
+
2676
+
2677
+
2678
+
2679
+
2680
+
2681
+
2682
+
2683
+
2684
+
2685
+
2686
+
2687
+
2688
+ // Create the line the goes over the line as a
2689
+ // cover
2690
+ var node = RGraph.SVG.create({
2691
+ svg: this.svg,
2692
+ type: 'path',
2693
+ parent: this.svg.all,
2694
+ attr: {
2695
+ fill: properties.filled ? '#0000' : 'none',
2696
+ d: path,
2697
+ stroke: '#0000',
2698
+ 'stroke-width': properties.filled ? 0 : Math.max(5, properties.linewidth),
2699
+ 'stroke-linecap':'round'
2700
+ },
2701
+ style: {
2702
+ cursor: 'pointer'
2703
+ }
2704
+ });
2705
+
2706
+ //
2707
+ // Now add the dataset event listener that causes
2708
+ // the dataset toolotip to be shown
2709
+ //
2710
+ var obj = this;
2711
+ (function (dataset)
2712
+ {
2713
+ node.addEventListener(properties.tooltipsEvent, function (e)
2714
+ {
2715
+ if (RGraph.SVG.REG.get('tooltip') &&
2716
+ RGraph.SVG.REG.get('tooltip').__dataset__ === dataset &&
2717
+ RGraph.SVG.REG.get('tooltip').__object__.uid === obj.uid) { // Added on the 27th/6/2019
2718
+ return;
2719
+ }
2720
+
2721
+ // Lose any previous highlighting
2722
+ var previous_highlight = RGraph.SVG.REG.get('tooltip-dataset-highlight');
2723
+ if (previous_highlight) {
2724
+ previous_highlight.setAttribute(obj.properties.filled ? 'fill' : 'stroke', 'transparent');
2725
+ RGraph.SVG.REG.set('tooltip-dataset-highlight', null);
2726
+ }
2727
+
2728
+ //
2729
+ // Calculate a sequential index to give
2730
+ // the tooltip
2731
+ //
2732
+ var seq = 0;
2733
+ for (let i=0; i<dataset; ++i) {
2734
+ seq += obj.coords2[i].length;
2735
+ }
2736
+
2737
+ seq += (obj.coords2[dataset].length / 2);
2738
+
2739
+
2740
+
2741
+
2742
+
2743
+
2744
+ RGraph.SVG.tooltip({
2745
+ object: obj,
2746
+ index: Math.round(obj.coords2[dataset].length / 2),
2747
+ dataset: dataset,
2748
+ sequentialIndex: Math.floor(seq),
2749
+ text: typeof properties.tooltipsDataset === 'string' ? properties.tooltipsDataset : properties.tooltipsDataset[dataset],
2750
+ event: e
2751
+ });
2752
+
2753
+ // Highlight the line
2754
+ //
2755
+ if (properties.filled) {
2756
+ e.target.setAttribute('fill','#fff9');
2757
+ }
2758
+ e.target.setAttribute('stroke','#fff9');
2759
+
2760
+ RGraph.SVG.REG.set('tooltip-dataset-highlight', e.target);
2761
+
2762
+ RGraph.SVG.runOnce('tooltip-dataset-window-mousedown-event-listener-gfyugyuyugfugfyu', function ()
2763
+ {
2764
+ window.addEventListener('mousedown', function (e)
2765
+ {
2766
+ RGraph.SVG.hideTooltip();
2767
+
2768
+ if (RGraph.SVG.REG.get('tooltip-dataset-highlight')) {
2769
+ RGraph.SVG.REG.get('tooltip-dataset-highlight').setAttribute('stroke', 'transparent');
2770
+ RGraph.SVG.REG.get('tooltip-dataset-highlight').setAttribute('fill', properties.filled ? 'transparent' : 'none');
2771
+ }
2772
+ });
2773
+ });
2774
+
2775
+ }, false);
2776
+ })(i);
2777
+ }
2778
+ };
2779
+
2780
+
2781
+
2782
+
2783
+
2784
+
2785
+
2786
+
2787
+ //
2788
+ // Sets the linecap style
2789
+ // Not always very noticeable, but these do have an effect
2790
+ // with thick lines
2791
+ //
2792
+ // butt square round
2793
+ //
2794
+ this.getLinecap = function (opt)
2795
+ {
2796
+ if (typeof properties.linecap === 'object' && typeof properties.linecap[opt.index] === 'string') {
2797
+ return properties.linecap[opt.index];
2798
+
2799
+ } else if ( typeof properties.linecap === 'string' ) {
2800
+ return properties.linecap;
2801
+
2802
+ } else {
2803
+ return 'round';
2804
+ }
2805
+ };
2806
+
2807
+
2808
+
2809
+
2810
+
2811
+
2812
+
2813
+
2814
+ //
2815
+ // Sets the linejoin style
2816
+ //
2817
+ // round miter bevel
2818
+ //
2819
+ this.getLinejoin = function (opt)
2820
+ {
2821
+ if (typeof properties.linejoin === 'object' && typeof properties.linejoin[opt.index] === 'string') {
2822
+ return properties.linejoin[opt.index];
2823
+ } else if ( typeof properties.linejoin === 'string' ) {
2824
+ return properties.linejoin;
2825
+ } else {
2826
+ return 'round';
2827
+ }
2828
+ };
2829
+
2830
+
2831
+
2832
+
2833
+
2834
+
2835
+
2836
+
2837
+
2838
+ //
2839
+ // Set the options that the user has provided
2840
+ //
2841
+ for (i in conf.options) {
2842
+ if (typeof i === 'string') {
2843
+ this.set(i, conf.options[i]);
2844
+ }
2845
+ }
2846
+ }
2847
+
2848
+
2849
+
2850
+ return this;
2851
+
2852
+
2853
+
2854
+
2855
+ // End module pattern
2856
+ })(window, document);