rgraph-rails 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +1 -0
  3. data/README.md +2 -2
  4. data/lib/rgraph-rails/version.rb +1 -1
  5. data/vendor/assets/images/bullet.png +0 -0
  6. data/vendor/assets/images/facebook-large.png +0 -0
  7. data/vendor/assets/images/google-plus-large.png +0 -0
  8. data/vendor/assets/images/logo.png +0 -0
  9. data/vendor/assets/images/rgraph.jpg +0 -0
  10. data/vendor/assets/javascripts/RGraph.bar.js +533 -242
  11. data/vendor/assets/javascripts/RGraph.bipolar.js +152 -169
  12. data/vendor/assets/javascripts/RGraph.common.annotate.js +2 -2
  13. data/vendor/assets/javascripts/RGraph.common.context.js +2 -2
  14. data/vendor/assets/javascripts/RGraph.common.core.js +688 -373
  15. data/vendor/assets/javascripts/RGraph.common.csv.js +2 -2
  16. data/vendor/assets/javascripts/RGraph.common.deprecated.js +2 -2
  17. data/vendor/assets/javascripts/RGraph.common.dynamic.js +188 -193
  18. data/vendor/assets/javascripts/RGraph.common.effects.js +62 -38
  19. data/vendor/assets/javascripts/RGraph.common.key.js +35 -15
  20. data/vendor/assets/javascripts/RGraph.common.resizing.js +38 -21
  21. data/vendor/assets/javascripts/RGraph.common.sheets.js +2 -2
  22. data/vendor/assets/javascripts/RGraph.common.tooltips.js +48 -40
  23. data/vendor/assets/javascripts/RGraph.common.zoom.js +2 -2
  24. data/vendor/assets/javascripts/RGraph.drawing.background.js +33 -49
  25. data/vendor/assets/javascripts/RGraph.drawing.circle.js +27 -30
  26. data/vendor/assets/javascripts/RGraph.drawing.image.js +23 -26
  27. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +47 -40
  28. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +38 -42
  29. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +24 -28
  30. data/vendor/assets/javascripts/RGraph.drawing.poly.js +25 -39
  31. data/vendor/assets/javascripts/RGraph.drawing.rect.js +27 -32
  32. data/vendor/assets/javascripts/RGraph.drawing.text.js +53 -58
  33. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +24 -29
  34. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +45 -51
  35. data/vendor/assets/javascripts/RGraph.fuel.js +11 -9
  36. data/vendor/assets/javascripts/RGraph.funnel.js +40 -43
  37. data/vendor/assets/javascripts/RGraph.gantt.js +34 -34
  38. data/vendor/assets/javascripts/RGraph.gauge.js +64 -55
  39. data/vendor/assets/javascripts/RGraph.hbar.js +194 -137
  40. data/vendor/assets/javascripts/RGraph.hprogress.js +261 -167
  41. data/vendor/assets/javascripts/RGraph.line.js +520 -512
  42. data/vendor/assets/javascripts/RGraph.meter.js +11 -10
  43. data/vendor/assets/javascripts/RGraph.modaldialog.js +11 -2
  44. data/vendor/assets/javascripts/RGraph.odo.js +11 -9
  45. data/vendor/assets/javascripts/RGraph.pie.js +385 -100
  46. data/vendor/assets/javascripts/RGraph.radar.js +36 -29
  47. data/vendor/assets/javascripts/RGraph.rose.js +58 -41
  48. data/vendor/assets/javascripts/RGraph.rscatter.js +40 -36
  49. data/vendor/assets/javascripts/RGraph.scatter.js +441 -499
  50. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1015 -0
  51. data/vendor/assets/javascripts/RGraph.thermometer.js +37 -37
  52. data/vendor/assets/javascripts/RGraph.vprogress.js +285 -157
  53. data/vendor/assets/javascripts/RGraph.waterfall.js +62 -62
  54. data/vendor/assets/stylesheets/website.css +30 -16
  55. metadata +3 -2
@@ -1,4 +1,4 @@
1
- // version: 2016-02-06
1
+ // version: 2016-06-04
2
2
  /**
3
3
  * o--------------------------------------------------------------------------------o
4
4
  * | This file is part of the RGraph package - you can learn more at: |
@@ -7,7 +7,7 @@
7
7
  * | |
8
8
  * | RGraph is dual licensed under the Open Source GPL (General Public License) |
9
9
  * | v2.0 license and a commercial license which means that you're not bound by |
10
- * | the terms of the GPL. The commercial license is just 99 (GBP) and you can |
10
+ * | the terms of the GPL. The commercial license is just 99 GBP and you can |
11
11
  * | read about it here: |
12
12
  * | http://www.rgraph.net/license |
13
13
  * o--------------------------------------------------------------------------------o
@@ -75,7 +75,29 @@
75
75
  }
76
76
  }
77
77
 
78
+ // If necessary convert X/Y values passed as strings to numbers
79
+ for (var i=0,len=this.data.length; i<len; ++i) { // Datasets
80
+ for (var j=0,len2=this.data[i].length; j<len2; ++j) { // Points
78
81
 
82
+ // Handle the conversion of X values
83
+ if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][0] === 'string') {
84
+ if (this.data[i][j][0].match(/^[.0-9]+$/)) {
85
+ this.data[i][j][0] = parseFloat(this.data[i][j][0]);
86
+ } else if (this.data[i][j][0] === '') {
87
+ this.data[i][j][0] = 0;
88
+ }
89
+ }
90
+
91
+ // Handle the conversion of Y values
92
+ if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][1] === 'string') {
93
+ if (this.data[i][j][1].match(/[.0-9]+/)) {
94
+ this.data[i][j][1] = parseFloat(this.data[i][j][1]);
95
+ } else if (this.data[i][j][1] === '') {
96
+ this.data[i][j][1] = 0;
97
+ }
98
+ }
99
+ }
100
+ }
79
101
 
80
102
 
81
103
  this.id = conf.id;
@@ -141,7 +163,10 @@
141
163
  'chart.text.size': 12,
142
164
  'chart.text.angle': 0,
143
165
  'chart.text.color': 'black',
144
- 'chart.text.font': 'Arial',
166
+ 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
167
+ 'chart.text.accessible': true,
168
+ 'chart.text.accessible.overflow': 'visible',
169
+ 'chart.text.accessible.pointerevents': false,
145
170
  'chart.tooltips': [], // Default must be an empty array
146
171
  'chart.tooltips.effect': 'fade',
147
172
  'chart.tooltips.event': 'onmousemove',
@@ -168,9 +193,10 @@
168
193
  'chart.xmax': 0,
169
194
  'chart.ymax': null,
170
195
  'chart.ymin': 0,
171
- 'chart.scale.decimals': null,
196
+ 'chart.scale.decimals': 0,
172
197
  'chart.scale.point': '.',
173
198
  'chart.scale.thousand': ',',
199
+ 'chart.scale.zerostart': true,
174
200
  'chart.title': '',
175
201
  'chart.title.background': null,
176
202
  'chart.title.hpos': null,
@@ -203,6 +229,10 @@
203
229
  'chart.labels.above': false,
204
230
  'chart.labels.above.size': 8,
205
231
  'chart.labels.above.decimals': 0,
232
+ 'chart.labels.offsetx': 0,
233
+ 'chart.labels.offsety': 0,
234
+ 'chart.ylabels.offsetx': 0,
235
+ 'chart.ylabels.offsety': 0,
206
236
  'chart.ylabels': true,
207
237
  'chart.ylabels.count': 5,
208
238
  'chart.ylabels.invert': false,
@@ -287,17 +317,30 @@
287
317
  'chart.events.mousemove': null,
288
318
  'chart.events.click': null,
289
319
  'chart.highlight.stroke': 'rgba(0,0,0,0)',
290
- 'chart.highlight.fill': 'rgba(255,255,255,0.7)'
320
+ 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
321
+ 'chart.clearto': 'rgba(0,0,0,0)',
322
+ 'chart.animation.trace': false,
323
+ 'chart.animation.trace.clip': 1
291
324
  }
292
325
 
293
326
  /**
294
327
  * This allows the data points to be given as dates as well as numbers. Formats supported by RGraph.parseDate() are accepted.
328
+ *
329
+ * ALSO: unrelated but this loop is also used to convert null values to an
330
+ * empty array
295
331
  */
296
332
  for (var i=0; i<this.data.length; ++i) {
297
333
  for (var j=0; j<this.data[i].length; ++j) {
298
- if (this.data[i][j] && typeof(this.data[i][j][0]) == 'string') {
334
+
335
+ // Convert null data points to an empty erray
336
+ if ( RGraph.isNull(this.data[i][j]) ) {
337
+ this.data[i][j] = [];
338
+ }
339
+
340
+ // Allow for the X point to be dates
341
+ if (this.data[i][j] && typeof(this.data[i][j][0]) == 'string') {
299
342
  this.data[i][j][0] = RGraph.parseDate(this.data[i][j][0]);
300
- }
343
+ }
301
344
  }
302
345
  }
303
346
 
@@ -343,7 +386,6 @@
343
386
  ca = this.canvas,
344
387
  co = ca.getContext('2d'),
345
388
  prop = this.properties,
346
- pa = RG.Path,
347
389
  pa2 = RG.path2,
348
390
  win = window,
349
391
  doc = document,
@@ -397,10 +439,9 @@
397
439
 
398
440
 
399
441
  // Convert uppercase letters to dot+lower case letter
400
- name = name.replace(/([A-Z])/g, function (str)
401
- {
402
- return '.' + String(RegExp.$1).toLowerCase();
403
- });
442
+ while(name.match(/([A-Z])/)) {
443
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
444
+ }
404
445
 
405
446
 
406
447
 
@@ -501,11 +542,11 @@
501
542
  RG.DrawBackgroundImage(this);
502
543
  }
503
544
 
504
-
545
+
505
546
  /**
506
547
  * Fire the onbeforedraw event
507
548
  */
508
- RG.FireCustomEvent(this, 'onbeforedraw');
549
+ RG.fireCustomEvent(this, 'onbeforedraw');
509
550
 
510
551
 
511
552
  /**
@@ -550,7 +591,6 @@
550
591
  */
551
592
  if (typeof(prop['chart.xmin']) == 'string') prop['chart.xmin'] = RG.parseDate(prop['chart.xmin']);
552
593
  if (typeof(prop['chart.xmax']) == 'string') prop['chart.xmax'] = RG.parseDate(prop['chart.xmax']);
553
-
554
594
 
555
595
  /**
556
596
  * Look for tooltips and populate chart.tooltips
@@ -584,17 +624,17 @@
584
624
 
585
625
 
586
626
  this.scale2 = RG.getScale2(this, {
587
- 'max':this.max,
588
- 'min':this.min,
589
- 'strict':true,
590
- 'scale.thousand':prop['chart.scale.thousand'],
591
- 'scale.point':prop['chart.scale.point'],
592
- 'scale.decimals':prop['chart.scale.decimals'],
593
- 'ylabels.count':prop['chart.ylabels.count'],
594
- 'scale.round':prop['chart.scale.round'],
595
- 'units.pre': prop['chart.units.pre'],
596
- 'units.post': prop['chart.units.post']
597
- });
627
+ 'max':this.max,
628
+ 'min':this.min,
629
+ 'strict':true,
630
+ 'scale.thousand':prop['chart.scale.thousand'],
631
+ 'scale.point':prop['chart.scale.point'],
632
+ 'scale.decimals':prop['chart.scale.decimals'],
633
+ 'ylabels.count':prop['chart.ylabels.count'],
634
+ 'scale.round':prop['chart.scale.round'],
635
+ 'units.pre': prop['chart.units.pre'],
636
+ 'units.post': prop['chart.units.post']
637
+ });
598
638
 
599
639
  this.max = this.scale2.max;
600
640
  this.min = this.scale2.min;
@@ -607,7 +647,7 @@
607
647
 
608
648
  for (i=0,len=this.data.length; i<len; i+=1) {
609
649
  for (j=0,len2=this.data[i].length; j<len2; j+=1) {
610
- if (this.data[i][j][1] != null) {
650
+ if (!RG.isNull(this.data[i][j]) && this.data[i][j][1] != null) {
611
651
  this.max = Math.max(this.max, typeof(this.data[i][j][1]) == 'object' ? RG.array_max(this.data[i][j][1]) : Math.abs(this.data[i][j][1]));
612
652
  }
613
653
  }
@@ -616,16 +656,16 @@
616
656
  this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
617
657
 
618
658
  this.scale2 = RG.getScale2(this, {
619
- 'max':this.max,
620
- 'min':this.min,
621
- 'scale.thousand':prop['chart.scale.thousand'],
622
- 'scale.point':prop['chart.scale.point'],
623
- 'scale.decimals':prop['chart.scale.decimals'],
624
- 'ylabels.count':prop['chart.ylabels.count'],
625
- 'scale.round':prop['chart.scale.round'],
626
- 'units.pre': prop['chart.units.pre'],
627
- 'units.post': prop['chart.units.post']
628
- });
659
+ 'max':this.max,
660
+ 'min':this.min,
661
+ 'scale.thousand':prop['chart.scale.thousand'],
662
+ 'scale.point':prop['chart.scale.point'],
663
+ 'scale.decimals':prop['chart.scale.decimals'],
664
+ 'ylabels.count':prop['chart.ylabels.count'],
665
+ 'scale.round':prop['chart.scale.round'],
666
+ 'units.pre': prop['chart.units.pre'],
667
+ 'units.post': prop['chart.units.post']
668
+ });
629
669
 
630
670
  this.max = this.scale2.max;
631
671
  this.min = this.scale2.min;
@@ -634,7 +674,7 @@
634
674
  this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
635
675
 
636
676
 
637
-
677
+
638
678
  // Progressively Draw the chart
639
679
  RG.background.Draw(this);
640
680
 
@@ -657,28 +697,39 @@
657
697
  }
658
698
 
659
699
  this.DrawLabels();
660
-
661
- i = 0;
662
- for(i=0; i<this.data.length; ++i) {
663
- this.DrawMarks(i);
664
-
665
- // Set the shadow
666
- co.shadowColor = prop['chart.line.shadow.color'];
667
- co.shadowOffsetX = prop['chart.line.shadow.offsetx'];
668
- co.shadowOffsetY = prop['chart.line.shadow.offsety'];
669
- co.shadowBlur = prop['chart.line.shadow.blur'];
670
700
 
671
- this.DrawLine(i);
672
-
673
- // Turn the shadow off
674
- RG.NoShadow(this);
701
+ // Clip the canvas so that the trace2 effect is facilitated
702
+ if (prop['chart.animation.trace']) {
703
+ co.save();
704
+ co.beginPath();
705
+ co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
706
+ co.clip();
675
707
  }
708
+
709
+ for(i=0; i<this.data.length; ++i) {
710
+ this.DrawMarks(i);
711
+
712
+ // Set the shadow
713
+ co.shadowColor = prop['chart.line.shadow.color'];
714
+ co.shadowOffsetX = prop['chart.line.shadow.offsetx'];
715
+ co.shadowOffsetY = prop['chart.line.shadow.offsety'];
716
+ co.shadowBlur = prop['chart.line.shadow.blur'];
676
717
 
677
-
678
- if (prop['chart.line']) {
679
- for (var i=0,len=this.data.length;i<len; i+=1) {
680
- this.DrawMarks(i); // Call this again so the tickmarks appear over the line
718
+ this.DrawLine(i);
719
+
720
+ // Turn the shadow off
721
+ RG.NoShadow(this);
722
+ }
723
+
724
+
725
+ if (prop['chart.line']) {
726
+ for (var i=0,len=this.data.length;i<len; i+=1) {
727
+ this.DrawMarks(i); // Call this again so the tickmarks appear over the line
728
+ }
681
729
  }
730
+
731
+ if (prop['chart.animation.trace']) {
732
+ co.restore();
682
733
  }
683
734
 
684
735
 
@@ -906,7 +957,9 @@
906
957
  inside = prop['chart.ylabels.inside'],
907
958
  context = co,
908
959
  canvas = ca,
909
- boxed = false
960
+ boxed = false,
961
+ offsetx = prop['chart.ylabels.offsetx'],
962
+ offsety = prop['chart.ylabels.offsety']
910
963
 
911
964
  this.halfTextHeight = text_size / 2;
912
965
 
@@ -955,48 +1008,51 @@
955
1008
 
956
1009
  for (var i=0; i<labels.length; ++i) {
957
1010
  var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) );
958
- RG.Text2(this, {'font':font,
959
- 'size':text_size,
960
- 'x':xPos,
961
- 'y':y,
962
- 'text':labels[i],
963
- 'valign':'center',
964
- 'halign':align,
965
- 'bounding':boxed,
966
- 'tag': 'labels.specific'
967
- });
1011
+ RG.Text2(this, {
1012
+ 'font':font,
1013
+ 'size':text_size,
1014
+ 'x':xPos + offsetx,
1015
+ 'y':y + offsety,
1016
+ 'text':labels[i],
1017
+ 'valign':'center',
1018
+ 'halign':align,
1019
+ 'bounding':boxed,
1020
+ 'tag': 'labels.specific'
1021
+ });
968
1022
  }
969
1023
 
970
1024
  var reversed_labels = RG.array_reverse(labels);
971
1025
 
972
1026
  for (var i=0; i<reversed_labels.length; ++i) {
973
1027
  var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) );
974
- RG.Text2(this, {'font':font,
975
- 'size':text_size,
976
- 'x':xPos,
977
- 'y':y,
978
- 'text':reversed_labels[i],
979
- 'valign':'center',
980
- 'halign':align,
981
- 'bounding':boxed,
982
- 'tag': 'labels.specific'
983
- });
1028
+ RG.Text2(this, {
1029
+ 'font':font,
1030
+ 'size':text_size,
1031
+ 'x':xPos + offsetx,
1032
+ 'y':y + offsety,
1033
+ 'text':reversed_labels[i],
1034
+ 'valign':'center',
1035
+ 'halign':align,
1036
+ 'bounding':boxed,
1037
+ 'tag': 'labels.specific'
1038
+ });
984
1039
  }
985
1040
 
986
1041
  /**
987
1042
  * Draw the center label if chart.ymin is specified
988
1043
  */
989
1044
  if (prop['chart.ymin'] != 0) {
990
- RG.Text2(this, {'font':font,
991
- 'size':text_size,
992
- 'x':xPos,
993
- 'y':(this.grapharea / 2) + this.gutterTop,
994
- 'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length - 1],
995
- 'valign':'center',
996
- 'halign':align,
997
- 'bounding':boxed,
998
- 'tag': 'labels.specific'
999
- });
1045
+ RG.Text2(this, {
1046
+ 'font':font,
1047
+ 'size':text_size,
1048
+ 'x':xPos + offsetx,
1049
+ 'y':(this.grapharea / 2) + this.gutterTop + offsety,
1050
+ 'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length - 1],
1051
+ 'valign':'center',
1052
+ 'halign':align,
1053
+ 'bounding':boxed,
1054
+ 'tag': 'labels.specific'
1055
+ });
1000
1056
  }
1001
1057
  }
1002
1058
 
@@ -1014,29 +1070,31 @@
1014
1070
  //value = value.toFixed(prop['chart.scale.decimals']);
1015
1071
 
1016
1072
  if (!invert) {
1017
- RG.Text2(this, {'font':font,
1018
- 'size': text_size,
1019
- 'x': xPos,
1020
- 'y': this.gutterTop + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight),
1021
- 'valign': 'center',
1022
- 'halign':align,
1023
- 'bounding': boxed,
1024
- 'boundingFill': 'white',
1025
- 'text': this.scale2.labels[i],
1026
- 'tag': 'scale'
1027
- });
1073
+ RG.Text2(this, {
1074
+ 'font':font,
1075
+ 'size': text_size,
1076
+ 'x': xPos + offsetx,
1077
+ 'y': this.gutterTop + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
1078
+ 'valign': 'center',
1079
+ 'halign':align,
1080
+ 'bounding': boxed,
1081
+ 'boundingFill': 'white',
1082
+ 'text': this.scale2.labels[i],
1083
+ 'tag': 'scale'
1084
+ });
1028
1085
  } else {
1029
- RG.Text2(this, {'font':font,
1030
- 'size': text_size,
1031
- 'x': xPos,
1032
- 'y': this.gutterTop + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight),
1033
- 'valign': 'center',
1034
- 'halign':align,
1035
- 'bounding': boxed,
1036
- 'boundingFill': 'white',
1037
- 'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
1038
- 'tag': 'scale'
1039
- });
1086
+ RG.Text2(this, {
1087
+ 'font':font,
1088
+ 'size': text_size,
1089
+ 'x': xPos + offsetx,
1090
+ 'y': this.gutterTop + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
1091
+ 'valign': 'center',
1092
+ 'halign':align,
1093
+ 'bounding': boxed,
1094
+ 'boundingFill': 'white',
1095
+ 'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
1096
+ 'tag': 'scale'
1097
+ });
1040
1098
  }
1041
1099
  }
1042
1100
 
@@ -1049,17 +1107,18 @@
1049
1107
  // value = (invert ? value : this.max - (value - this.min)).toFixed(prop['chart.scale.decimals']);
1050
1108
 
1051
1109
  if (!invert) {
1052
- RG.Text2(this, {'font':font,
1053
- 'size': text_size,
1054
- 'x': xPos,
1055
- 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight),
1056
- 'valign': 'center',
1057
- 'halign':align,
1058
- 'bounding': boxed,
1059
- 'boundingFill': 'white',
1060
- 'text': '-' + this.scale2.labels[len - (i+1)],
1061
- 'tag': 'scale'
1062
- });
1110
+ RG.Text2(this, {
1111
+ 'font':font,
1112
+ 'size': text_size,
1113
+ 'x': xPos + offsetx,
1114
+ 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
1115
+ 'valign': 'center',
1116
+ 'halign':align,
1117
+ 'bounding': boxed,
1118
+ 'boundingFill': 'white',
1119
+ 'text': '-' + this.scale2.labels[len - (i+1)],
1120
+ 'tag': 'scale'
1121
+ });
1063
1122
  } else {
1064
1123
 
1065
1124
  // This ensures that the center label isn't drawn twice
@@ -1067,61 +1126,66 @@
1067
1126
  continue;
1068
1127
  }
1069
1128
 
1070
- RG.Text2(this, {'font':font,
1071
- 'size': text_size,
1072
- 'x': xPos,
1073
- 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight),
1074
- 'valign': 'center',
1075
- 'halign':align,
1076
- 'bounding': boxed,
1077
- 'boundingFill': 'white',
1078
- 'text': '-' + this.scale2.labels[i],
1079
- 'tag': 'scale'
1080
- });
1129
+ RG.Text2(this, {
1130
+ 'font':font,
1131
+ 'size': text_size,
1132
+ 'x': xPos + offsetx,
1133
+ 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
1134
+ 'valign': 'center',
1135
+ 'halign':align,
1136
+ 'bounding': boxed,
1137
+ 'boundingFill': 'white',
1138
+ 'text': '-' + this.scale2.labels[i],
1139
+ 'tag': 'scale'
1140
+ });
1081
1141
  }
1082
1142
  }
1083
1143
 
1084
1144
 
1085
1145
 
1086
-
1146
+
1087
1147
  // If ymin is specified draw that
1088
- if (!invert && yMin > 0) {
1089
- RG.Text2(this, {'font':font,
1090
- 'size': text_size,
1091
- 'x': xPos,
1092
- 'y': this.gutterTop + this.halfGraphHeight,
1093
- 'valign': 'center',
1094
- 'halign':align,
1095
- 'bounding': boxed,
1096
- 'boundingFill': 'white',
1097
- 'text': RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1098
- 'tag': 'scale'
1099
- });
1148
+ if (!invert && (yMin > 0 || prop['chart.scale.zerostart'])) {
1149
+ RG.text2(this, {
1150
+ 'font':font,
1151
+ 'size': text_size,
1152
+ 'x': xPos + offsetx,
1153
+ 'y': this.gutterTop + this.halfGraphHeight + offsety,
1154
+ 'valign': 'center',
1155
+ 'halign':align,
1156
+ 'bounding': boxed,
1157
+ 'boundingFill': 'white',
1158
+ 'text': RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1159
+ 'tag': 'scale'
1160
+ });
1100
1161
  }
1101
1162
 
1102
1163
  if (invert) {
1103
- RG.Text2(this, {'font':font,
1104
- 'size': text_size,
1105
- 'x': xPos,
1106
- 'y': this.gutterTop,
1107
- 'valign': 'center',
1108
- 'halign':align,
1109
- 'bounding': boxed,
1110
- 'boundingFill': 'white',
1111
- 'text': RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1112
- 'tag': 'scale'
1113
- });
1114
- RG.Text2(this, {'font':font,
1115
- 'size': text_size,
1116
- 'x': xPos,
1117
- 'y': this.gutterTop + (this.halfGraphHeight * 2),
1118
- 'valign': 'center',
1119
- 'halign':align,
1120
- 'bounding': boxed,
1121
- 'boundingFill': 'white',
1122
- 'text': '-' + RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1123
- 'tag': 'scale'
1124
- });
1164
+ RG.text2(this, {
1165
+ 'font':font,
1166
+ 'size': text_size,
1167
+ 'x': xPos + offsetx,
1168
+ 'y': this.gutterTop + offsety,
1169
+ 'valign': 'center',
1170
+ 'halign':align,
1171
+ 'bounding': boxed,
1172
+ 'boundingFill': 'white',
1173
+ 'text': RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1174
+ 'tag': 'scale'
1175
+ });
1176
+
1177
+ RG.text2(this, {
1178
+ 'font':font,
1179
+ 'size': text_size,
1180
+ 'x': xPos + offsetx,
1181
+ 'y': this.gutterTop + (this.halfGraphHeight * 2) + offsety,
1182
+ 'valign': 'center',
1183
+ 'halign':align,
1184
+ 'bounding': boxed,
1185
+ 'boundingFill': 'white',
1186
+ 'text': '-' + RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1187
+ 'tag': 'scale'
1188
+ });
1125
1189
  }
1126
1190
  }
1127
1191
 
@@ -1162,20 +1226,21 @@
1162
1226
 
1163
1227
  var y = this.gutterTop + (i * (this.grapharea / (len - 1)) );
1164
1228
 
1165
- RG.Text2(this, {'font':font,
1166
- 'size':text_size,
1167
- 'x':xPos,
1168
- 'y':y,
1169
- 'text':labels[i],
1170
- 'halign':align,
1171
- 'valign':'center',
1172
- 'bounding':boxed,
1173
- 'tag': 'scale'
1174
- });
1229
+ RG.Text2(this, {
1230
+ 'font':font,
1231
+ 'size':text_size,
1232
+ 'x':xPos + offsetx,
1233
+ 'y':y + offsety,
1234
+ 'text':labels[i],
1235
+ 'halign':align,
1236
+ 'valign':'center',
1237
+ 'bounding':boxed,
1238
+ 'tag': 'scale'
1239
+ });
1175
1240
  }
1176
1241
 
1177
1242
  /**
1178
- * X axis at the bottom
1243
+ * X axis at the bottom with a scale
1179
1244
  */
1180
1245
  } else {
1181
1246
 
@@ -1189,33 +1254,35 @@
1189
1254
  // value = value.toFixed(prop['chart.scale.decimals']);
1190
1255
  var interval = (ca.height - this.gutterTop - this.gutterBottom) / numYLabels;
1191
1256
 
1192
- RG.Text2(this, {'font':font,
1193
- 'size': text_size,
1194
- 'x': xPos,
1195
- 'y': this.gutterTop + ((i+1) * interval),
1196
- 'valign': 'center',
1197
- 'halign':align,
1198
- 'bounding': boxed,
1199
- 'boundingFill': 'white',
1200
- 'text': this.scale2.labels[i],
1201
- 'tag': 'scale'
1202
- });
1257
+ RG.Text2(this, {
1258
+ 'font':font,
1259
+ 'size': text_size,
1260
+ 'x': xPos + offsetx,
1261
+ 'y': this.gutterTop + ((i+1) * interval) + offsety,
1262
+ 'valign': 'center',
1263
+ 'halign':align,
1264
+ 'bounding': boxed,
1265
+ 'boundingFill': 'white',
1266
+ 'text': this.scale2.labels[i],
1267
+ 'tag': 'scale'
1268
+ });
1203
1269
  }
1204
1270
 
1205
1271
 
1206
1272
  // No X axis being shown and there's no ymin. If ymin IS set its added further down
1207
1273
  if (!prop['chart.xaxis'] && !prop['chart.ymin']) {
1208
- RG.Text2(this, {'font':font,
1209
- 'size': text_size,
1210
- 'x': xPos,
1211
- 'y': this.gutterTop,
1212
- 'valign': 'center',
1213
- 'halign':align,
1214
- 'bounding': boxed,
1215
- 'boundingFill': 'white',
1216
- 'text': RG.number_format(this, (this.min).toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1217
- 'tag': 'scale'
1218
- });
1274
+ RG.Text2(this, {
1275
+ 'font':font,
1276
+ 'size': text_size,
1277
+ 'x': xPos + offsetx,
1278
+ 'y': this.gutterTop + offsety,
1279
+ 'valign': 'center',
1280
+ 'halign':align,
1281
+ 'bounding': boxed,
1282
+ 'boundingFill': 'white',
1283
+ 'text': RG.number_format(this, (this.min).toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1284
+ 'tag': 'scale'
1285
+ });
1219
1286
  }
1220
1287
 
1221
1288
  } else {
@@ -1226,60 +1293,63 @@
1226
1293
  // if (!invert) value += this.min;
1227
1294
  // value = value.toFixed(prop['chart.scale.decimals']);
1228
1295
 
1229
- RG.Text2(this, {'font':font,
1230
- 'size': text_size,
1231
- 'x': xPos,
1232
- 'y': this.gutterTop + this.grapharea - (((i + 1)/this.scale2.labels.length) * this.grapharea),
1233
- 'valign': 'center',
1234
- 'halign':align,
1235
- 'bounding': boxed,
1236
- 'boundingFill': 'white',
1237
- 'text': this.scale2.labels[i],
1238
- 'tag': 'scale'
1239
- });
1296
+ RG.Text2(this, {
1297
+ 'font':font,
1298
+ 'size': text_size,
1299
+ 'x': xPos + offsetx,
1300
+ 'y': this.gutterTop + this.grapharea - (((i + 1)/this.scale2.labels.length) * this.grapharea) + offsety,
1301
+ 'valign': 'center',
1302
+ 'halign':align,
1303
+ 'bounding': boxed,
1304
+ 'boundingFill': 'white',
1305
+ 'text': this.scale2.labels[i],
1306
+ 'tag': 'scale'
1307
+ });
1240
1308
  }
1241
1309
 
1242
1310
  if (!prop['chart.xaxis'] && prop['chart.ymin'] == 0) {
1243
- RG.Text2(this, {'font':font,
1244
- 'size': text_size,
1245
- 'x': xPos,
1246
- 'y': ca.height - this.gutterBottom,
1247
- 'valign': 'center',
1248
- 'halign':align,
1249
- 'bounding': boxed,
1250
- 'boundingFill': 'white',
1251
- 'text': RG.number_format(this, (0).toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1252
- 'tag': 'scale'
1253
- });
1311
+ RG.Text2(this, {
1312
+ 'font':font,
1313
+ 'size': text_size,
1314
+ 'x': xPos + offsetx,
1315
+ 'y': ca.height - this.gutterBottom + offsety,
1316
+ 'valign': 'center',
1317
+ 'halign':align,
1318
+ 'bounding': boxed,
1319
+ 'boundingFill': 'white',
1320
+ 'text': RG.number_format(this, (0).toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1321
+ 'tag': 'scale'
1322
+ });
1254
1323
  }
1255
1324
  }
1256
1325
  }
1257
1326
 
1258
- if (prop['chart.ymin'] && !invert) {
1259
- RG.Text2(this, {'font':font,
1260
- 'size': text_size,
1261
- 'x': xPos,
1262
- 'y': ca.height - this.gutterBottom,
1263
- 'valign': 'center',
1264
- 'halign':align,
1265
- 'bounding': boxed,
1266
- 'boundingFill': 'white',
1267
- 'text': RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1268
- 'tag': 'scale'
1269
- });
1327
+ if ( (prop['chart.ymin'] || prop['chart.scale.zerostart']) && !invert) {
1328
+ RG.text2(this, {
1329
+ 'font':font,
1330
+ 'size': text_size,
1331
+ 'x': xPos + offsetx,
1332
+ 'y': ca.height - this.gutterBottom + offsety,
1333
+ 'valign': 'center',
1334
+ 'halign':align,
1335
+ 'bounding': boxed,
1336
+ 'boundingFill': 'white',
1337
+ 'text': RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1338
+ 'tag': 'scale'
1339
+ });
1270
1340
  } else if (invert) {
1271
- RG.Text2(this, {'font':font,
1272
- 'size': text_size,
1273
- 'x': xPos,
1274
- 'y': this.gutterTop,
1275
- 'valign': 'center',
1276
- 'halign':align,
1277
- 'bounding': boxed,
1278
- 'boundingFill': 'white',
1279
- 'text': RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1280
- 'tag': 'scale'
1281
- });
1282
-
1341
+ RG.text2(this, {
1342
+ 'font':font,
1343
+ 'size': text_size,
1344
+ 'x': xPos + offsetx,
1345
+ 'y': this.gutterTop + offsety,
1346
+ 'valign': 'center',
1347
+ 'halign':align,
1348
+ 'bounding': boxed,
1349
+ 'boundingFill': 'white',
1350
+ 'text': RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
1351
+ 'tag': 'scale'
1352
+ });
1283
1353
  }
1284
1354
  }
1285
1355
  }
@@ -1287,12 +1357,12 @@
1287
1357
 
1288
1358
 
1289
1359
 
1290
-
1360
+
1291
1361
  /**
1292
1362
  * Draw an X scale
1293
1363
  */
1294
1364
  if (prop['chart.xscale']) {
1295
-
1365
+
1296
1366
  var numXLabels = prop['chart.xscale.numlabels'],
1297
1367
  y = ca.height - this.gutterBottom + 5 + (text_size / 2),
1298
1368
  units_pre_x = prop['chart.xscale.units.pre'],
@@ -1301,7 +1371,9 @@
1301
1371
  point = prop['chart.xscale.point'],
1302
1372
  thousand = prop['chart.xscale.thousand'],
1303
1373
  color = prop['chart.labels.color'],
1304
- bold = prop['chart.labels.bold']
1374
+ bold = prop['chart.labels.bold'],
1375
+ offsetx = prop['chart.labels.offsetx'],
1376
+ offsety = prop['chart.labels.offsety']
1305
1377
 
1306
1378
 
1307
1379
  if (!prop['chart.xmax']) {
@@ -1347,8 +1419,8 @@
1347
1419
  'font':font,
1348
1420
  'size': text_size,
1349
1421
  'bold': bold,
1350
- 'x': x,
1351
- 'y': y,
1422
+ 'x': x + offsetx,
1423
+ 'y': y + offsety,
1352
1424
  'valign': 'center',
1353
1425
  'halign':'center',
1354
1426
  'text':text,
@@ -1367,8 +1439,8 @@
1367
1439
  'font':font,
1368
1440
  'size': text_size,
1369
1441
  'bold':bold,
1370
- 'x': this.gutterLeft,
1371
- 'y': y,
1442
+ 'x': this.gutterLeft + offsetx,
1443
+ 'y': y + offsety,
1372
1444
  'valign': 'center',
1373
1445
  'halign':'center',
1374
1446
  'text': text,
@@ -1388,6 +1460,8 @@
1388
1460
  var labels = prop['chart.labels'];
1389
1461
  var color = prop['chart.labels.color'];
1390
1462
  var bold = prop['chart.labels.bold'];
1463
+ var offsetx = prop['chart.labels.offsetx'];
1464
+ var offsety = prop['chart.labels.offsety'];
1391
1465
 
1392
1466
  /**
1393
1467
  * Text angle
@@ -1428,8 +1502,8 @@
1428
1502
  'font':font,
1429
1503
  'size': prop['chart.text.size'],
1430
1504
  'bold': bold,
1431
- 'x': this.getXCoord(labels[i][1]) + offset,
1432
- 'y': yPos,
1505
+ 'x': this.getXCoord(labels[i][1]) + offset + offsetx,
1506
+ 'y': yPos + offsety,
1433
1507
  'valign': valign,
1434
1508
  'halign':angle != 0 ? 'right' : (prop['chart.labels.specific.align'] == 'center' ? 'center' : 'left'),
1435
1509
  'text':String(labels[i][0]),
@@ -1443,19 +1517,19 @@
1443
1517
  */
1444
1518
  co.beginPath();
1445
1519
  co.strokeStyle = '#bbb';
1446
- co.moveTo(Math.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom);
1447
- co.lineTo(Math.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom + 20);
1520
+ co.moveTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom);
1521
+ co.lineTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom + 20);
1448
1522
  co.stroke();
1449
1523
 
1450
1524
  } else {
1451
1525
 
1452
- RG.Text2(this, {
1526
+ RG.text2(this, {
1453
1527
  'color': color,
1454
1528
  'font':font,
1455
1529
  'size': prop['chart.text.size'],
1456
1530
  'bold': bold,
1457
- 'x': xPos + (xInterval / 2),
1458
- 'y': yPos,
1531
+ 'x': xPos + (xInterval / 2) + offsetx,
1532
+ 'y': yPos + offsety,
1459
1533
  'valign': valign,
1460
1534
  'halign':halign,
1461
1535
  'text':String(labels[i]),
@@ -1512,31 +1586,36 @@
1512
1586
  */
1513
1587
  var xmax = prop['chart.xmax'];
1514
1588
  var default_color = prop['chart.defaultcolor'];
1515
-
1589
+
1516
1590
  for (var j=0,len=this.data[i].length; j<len; j+=1) {
1517
1591
  /**
1518
1592
  * This is here because tooltips are optional
1519
1593
  */
1520
- var data_point = this.data[i];
1521
-
1522
- var xCoord = data_point[j][0];
1523
- var yCoord = data_point[j][1];
1524
- var color = data_point[j][2] ? data_point[j][2] : default_color;
1525
- var tooltip = (data_point[j] && data_point[j][3]) ? data_point[j][3] : null;
1594
+ var data_points = this.data[i];
1595
+
1596
+ // Allow for null points
1597
+ if (RG.isNull(data_points[j])) {
1598
+ continue;
1599
+ }
1600
+
1601
+ var xCoord = data_points[j][0];
1602
+ var yCoord = data_points[j][1];
1603
+ var color = data_points[j][2] ? data_points[j][2] : default_color;
1604
+ var tooltip = (data_points[j] && data_points[j][3]) ? data_points[j][3] : null;
1526
1605
 
1527
1606
 
1528
1607
  this.DrawMark(
1529
- i,
1530
- xCoord,
1531
- yCoord,
1532
- xmax,
1533
- this.scale2.max,
1534
- color,
1535
- tooltip,
1536
- this.coords[i],
1537
- data_point,
1538
- j
1539
- );
1608
+ i,
1609
+ xCoord,
1610
+ yCoord,
1611
+ xmax,
1612
+ this.scale2.max,
1613
+ color,
1614
+ tooltip,
1615
+ this.coords[i],
1616
+ data_points,
1617
+ j
1618
+ );
1540
1619
  }
1541
1620
  };
1542
1621
 
@@ -1656,6 +1735,8 @@
1656
1735
 
1657
1736
  if (prop['chart.line.visible']) {
1658
1737
  co.beginPath();
1738
+
1739
+ //TODO Make this color configurable
1659
1740
  co.strokeRect(x - halfBoxWidth, y1, boxWidth, y3 - y1);
1660
1741
 
1661
1742
  // Draw the upper coloured box if a value is specified
@@ -1679,7 +1760,7 @@
1679
1760
  }
1680
1761
 
1681
1762
  co.moveTo(Math.round(x), y0);
1682
- co.lineTo(Math.round(x), y1);
1763
+ co.lineTo(Math.round(x ), y1);
1683
1764
 
1684
1765
  if (prop['chart.boxplot.capped']) {
1685
1766
  co.moveTo(x - halfBoxWidth, Math.round(y4));
@@ -1688,7 +1769,7 @@
1688
1769
 
1689
1770
  co.moveTo(Math.round(x), y4);
1690
1771
  co.lineTo(Math.round(x), y3);
1691
-
1772
+
1692
1773
  co.stroke();
1693
1774
  }
1694
1775
  }
@@ -2059,18 +2140,18 @@
2059
2140
  co.beginPath();
2060
2141
  // Fore ground color
2061
2142
  co.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
2062
- RG.Text2(this, {
2063
- 'font':obj.Get('chart.text.font'),
2064
- 'size':obj.Get('chart.text.size'),
2065
- 'x':text_x,
2066
- 'y':text_y,
2067
- 'text':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
2068
- 'valign':'bottom',
2069
- 'halign':'center',
2070
- 'bounding':true,
2071
- 'boundingFill':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white',
2072
- 'tag': 'labels.ingraph'
2073
- });
2143
+ RG.text2(this, {
2144
+ 'font':obj.Get('chart.text.font'),
2145
+ 'size':obj.Get('chart.text.size'),
2146
+ 'x':text_x,
2147
+ 'y':text_y,
2148
+ 'text':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
2149
+ 'valign':'bottom',
2150
+ 'halign':'center',
2151
+ 'bounding':true,
2152
+ 'bounding.fill':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white',
2153
+ 'tag': 'labels.ingraph'
2154
+ });
2074
2155
  co.fill();
2075
2156
  }
2076
2157
 
@@ -2326,13 +2407,17 @@
2326
2407
  this.highlight =
2327
2408
  this.Highlight = function (shape)
2328
2409
  {
2329
- // Boxplot highlight
2330
- if (shape['height']) {
2331
- RG.Highlight.Rect(this, shape);
2332
-
2333
- // Point highlight
2410
+ if (typeof prop['chart.highlight.style'] === 'function') {
2411
+ (prop['chart.highlight.style'])(shape);
2334
2412
  } else {
2335
- RG.Highlight.Point(this, shape);
2413
+ // Boxplot highlight
2414
+ if (shape['height']) {
2415
+ RG.Highlight.Rect(this, shape);
2416
+
2417
+ // Point highlight
2418
+ } else {
2419
+ RG.Highlight.Point(this, shape);
2420
+ }
2336
2421
  }
2337
2422
  };
2338
2423
 
@@ -2415,77 +2500,43 @@
2415
2500
  */
2416
2501
  this.positionTooltip = function (obj, x, y, tooltip, idx)
2417
2502
  {
2418
- var shape = RG.Registry.Get('chart.tooltip.shape');
2419
- var dataset = shape['dataset'];
2420
- var index = shape['index'];
2421
- var coordX = obj.coords[dataset][index][0]
2422
- var coordY = obj.coords[dataset][index][1]
2423
- var canvasXY = RG.getCanvasXY(obj.canvas);
2424
- var gutterLeft = obj.gutterLeft;
2425
- var gutterTop = obj.gutterTop;
2426
- var width = tooltip.offsetWidth;
2427
- var height = tooltip.offsetHeight;
2428
- tooltip.style.left = 0;
2429
- tooltip.style.top = 0;
2503
+ var shape = RG.Registry.Get('chart.tooltip.shape');
2504
+ var dataset = shape['dataset'];
2505
+ var index = shape['index'];
2506
+ var coordX = obj.coords[dataset][index][0]
2507
+ var coordY = obj.coords[dataset][index][1]
2508
+ var canvasXY = RG.getCanvasXY(obj.canvas);
2509
+ var mouseXY = RG.getMouseXY(window.event);
2510
+ var gutterLeft = obj.gutterLeft;
2511
+ var gutterTop = obj.gutterTop;
2512
+ var width = tooltip.offsetWidth;
2513
+ var height = tooltip.offsetHeight;
2514
+ tooltip.style.left = 0;
2515
+ tooltip.style.top = 0;
2430
2516
 
2431
2517
  // Is the coord a boxplot
2432
- var isBoxplot = typeof(coordY) == 'object' ? true : false;
2518
+ //var isBoxplot = typeof(coordY) == 'object' ? true : false;
2433
2519
 
2434
2520
  // Show any overflow (ie the arrow)
2435
2521
  tooltip.style.overflow = '';
2436
2522
 
2437
- // Create the arrow
2438
- var img = new Image();
2439
- img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
2440
- img.style.position = 'absolute';
2441
- img.id = '__rgraph_tooltip_pointer__';
2442
- img.style.top = (tooltip.offsetHeight - 2) + 'px';
2443
- tooltip.appendChild(img);
2444
2523
 
2445
2524
  // Reposition the tooltip if at the edges:
2446
2525
 
2447
2526
  // LEFT edge //////////////////////////////////////////////////////////////////
2448
-
2449
- if ((canvasXY[0] + (coordX[0] || coordX) - (width / 2)) < 10) {
2450
-
2451
- if (isBoxplot) {
2452
- tooltip.style.left = canvasXY[0] + coordX[0] + ((coordX[1] - coordX[0]) / 2) - (width * 0.1) + 'px';
2453
- tooltip.style.top = canvasXY[1] + coordY[2] - height - 5 + 'px';
2454
- img.style.left = ((width * 0.1) - 8.5) + 'px';
2455
-
2456
- } else {
2457
- tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + 'px';
2458
- tooltip.style.top = canvasXY[1] + coordY - height - 9 + 'px';
2459
- img.style.left = ((width * 0.1) - 8.5) + 'px';
2460
- }
2527
+ if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
2528
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + 'px';
2529
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
2461
2530
 
2462
2531
  // RIGHT edge //////////////////////////////////////////////////////////////////
2463
-
2464
2532
  } else if ((canvasXY[0] + (coordX[0] || coordX) + (width / 2)) > doc.body.offsetWidth) {
2465
- if (isBoxplot) {
2466
- tooltip.style.left = canvasXY[0] + coordX[0] + ((coordX[1] - coordX[0]) / 2) - (width * 0.9) + 'px';
2467
- tooltip.style.top = canvasXY[1] + coordY[2] - height - 5 + 'px';
2468
- img.style.left = ((width * 0.9) - 8.5) + 'px';
2469
-
2470
- } else {
2471
- tooltip.style.left = (canvasXY[0] + coordX - (width * 0.9)) + 'px';
2472
- tooltip.style.top = canvasXY[1] + coordY - height - 9 + 'px';
2473
- img.style.left = ((width * 0.9) - 8.5) + 'px';
2474
- }
2533
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + 'px';
2534
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
2475
2535
 
2476
2536
  // Default positioning - CENTERED //////////////////////////////////////////////////////////////////
2477
-
2478
2537
  } else {
2479
- if (isBoxplot) {
2480
- tooltip.style.left = canvasXY[0] + coordX[0] + ((coordX[1] - coordX[0]) / 2) - (width / 2) + 'px';
2481
- tooltip.style.top = canvasXY[1] + coordY[2] - height - 5 + 'px';
2482
- img.style.left = ((width * 0.5) - 8.5) + 'px';
2483
-
2484
- } else {
2485
- tooltip.style.left = (canvasXY[0] + coordX - (width * 0.5)) + 'px';
2486
- tooltip.style.top = canvasXY[1] + coordY - height - 9 + 'px';
2487
- img.style.left = ((width * 0.5) - 8.5) + 'px';
2488
- }
2538
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + 'px';
2539
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
2489
2540
  }
2490
2541
  };
2491
2542
 
@@ -2624,15 +2675,15 @@
2624
2675
 
2625
2676
  co.beginPath();
2626
2677
  co.fillStyle = RG.RadialGradient(obj,
2627
- obj.coords[0][i][0] + (r / 2.5),
2628
- obj.coords[0][i][1] - (r / 2.5),
2629
- 0,
2630
- obj.coords[0][i][0] + (r / 2.5),
2631
- obj.coords[0][i][1] - (r / 2.5),
2632
- 50,
2633
- 'white',
2634
- obj.data[0][i][2] ? obj.data[0][i][2] : obj.properties['chart.defaultcolor']
2635
- );
2678
+ obj.coords[0][i][0] + (r / 2.5),
2679
+ obj.coords[0][i][1] - (r / 2.5),
2680
+ 0,
2681
+ obj.coords[0][i][0] + (r / 2.5),
2682
+ obj.coords[0][i][1] - (r / 2.5),
2683
+ r,
2684
+ 'white',
2685
+ obj.data[0][i][2] ? obj.data[0][i][2] : obj.properties['chart.defaultcolor']
2686
+ );
2636
2687
  co.arc(obj.coords[0][i][0], obj.coords[0][i][1], r, 0, RG.TWOPI, false);
2637
2688
  co.fill();
2638
2689
  }
@@ -2685,7 +2736,9 @@
2685
2736
  if (typeof(this.data[dataset][i][1][6]) == 'string') this.data[dataset][i][1][6] = this.parseSingleColorForGradient(this.data[dataset][i][1][6]);
2686
2737
  }
2687
2738
 
2688
- this.data[dataset][i][2] = this.parseSingleColorForGradient(this.data[dataset][i][2]);
2739
+ if (!RG.isNull(this.data[dataset][i])) {
2740
+ this.data[dataset][i][2] = this.parseSingleColorForGradient(this.data[dataset][i][2]);
2741
+ }
2689
2742
  }
2690
2743
  }
2691
2744
  }
@@ -2824,153 +2877,42 @@
2824
2877
 
2825
2878
 
2826
2879
  /**
2827
- * Trace
2880
+ * Trace2
2881
+ *
2882
+ * This is a new version of the Trace effect which no longer requires jQuery and is more compatible
2883
+ * with other effects (eg Expand). This new effect is considerably simpler and less code.
2828
2884
  *
2829
- * This effect is for the Scatter chart, uses the jQuery library and slowly
2830
- * uncovers the Line/marks, but you can see the background of the chart.
2885
+ * @param object Options for the effect. Currently only "frames" is available.
2886
+ * @param int A function that is called when the ffect is complete
2831
2887
  */
2832
- this.trace = function ()
2888
+ this.trace =
2889
+ this.trace2 = function ()
2833
2890
  {
2834
- var callback = typeof arguments[1] === 'function' ? arguments[1] : function () {};
2835
- var opt = arguments[0] || [];
2836
- var obj = this;
2891
+ var obj = this,
2892
+ callback = arguments[2],
2893
+ opt = arguments[0] || {},
2894
+ frames = opt.frames || 30,
2895
+ frame = 0,
2896
+ callback = arguments[1] || function () {}
2837
2897
 
2838
- opt['duration'] = opt['duration'] || 1500;
2839
- if (opt['frames']) {
2840
- opt['duration'] = (opt['frames'] / 60) * 1000;
2841
- }
2842
-
2843
- RGraph.clear(ca);
2844
- RGraph.redrawCanvas(ca);
2898
+ obj.Set('animationTrace', true);
2899
+ obj.Set('animationTraceClip', 0);
2845
2900
 
2846
- /**
2847
- * Create the DIV that the second canvas will sit in
2848
- */
2849
- var div = document.createElement('DIV');
2850
- var xy = RG.getCanvasXY(this.canvas);
2851
- div.id = '__rgraph_trace_animation_' + RGraph.random(0, 4351623) + '__';
2852
- div.style.left = xy[0] + 'px';
2853
- div.style.top = xy[1] + 'px';
2854
- div.style.width = prop['chart.gutter.left'];
2855
- div.style.height = ca.height + 'px';
2856
- div.style.position = 'absolute';
2857
- div.style.overflow = 'hidden';
2858
- obj.canvas.parentNode.appendChild(div);
2859
-
2860
-
2861
- /**
2862
- * Make the second canvas
2863
- */
2864
- var id = '__rgraph_scatter_trace_animation_' + RG.random(0, 99999999) + '__';
2865
- var canvas2 = document.createElement('CANVAS');
2866
- canvas2.width = ca.width;
2867
- canvas2.height = ca.height;
2868
- canvas2.style.position = 'absolute';
2869
- canvas2.style.left = 0;
2870
- canvas2.style.top = 0;
2871
- canvas2.setAttribute('data-l', 'false');
2901
+ function iterator ()
2902
+ {
2903
+ RG.clear(obj.canvas);
2872
2904
 
2905
+ RG.redrawCanvas(obj.canvas);
2873
2906
 
2874
- // This stops the clear effect clearing the canvas - which can happen if you have multiple canvas tags on the page all with
2875
- // dynamic effects that do redrawing
2876
- canvas2.noclear = true;
2877
-
2878
- canvas2.id = id;
2879
- div.appendChild(canvas2);
2880
-
2881
- var reposition_canvas2 = function (e)
2882
- {
2883
- var xy = RGraph.getCanvasXY(obj.canvas);
2884
-
2885
- div.style.left = xy[0] + 'px';
2886
- div.style.top = xy[1] + 'px';
2887
- }
2888
- window.addEventListener('resize', reposition_canvas2, false)
2889
-
2890
- /**
2891
- * Make a copy of the original Line object
2892
- */
2893
- var obj2 = new RGraph.Scatter(id, RG.array_clone(this.data));
2894
-
2895
- // Remove the new line from the ObjectRegistry so that it isn't redawn
2896
- RG.ObjectRegistry.Remove(obj2);
2897
-
2898
- for (i in prop) {
2899
- if (typeof i === 'string') {
2900
- obj2.Set(i, prop[i]);
2907
+ if (frame++ < frames) {
2908
+ obj.set('animationTraceClip', frame / frames);
2909
+ RG.Effects.updateCanvas(iterator);
2910
+ } else {
2911
+ callback(obj);
2901
2912
  }
2902
2913
  }
2903
-
2904
-
2905
- obj2.Set('chart.labels', []);
2906
- obj2.Set('chart.background.grid', false);
2907
- obj2.Set('chart.background.barcolor1', 'rgba(0,0,0,0)');
2908
- obj2.Set('chart.background.barcolor2', 'rgba(0,0,0,0)');
2909
- obj2.Set('chart.ylabels', false);
2910
- obj2.Set('chart.noaxes', true);
2911
- obj2.Set('chart.title', '');
2912
- obj2.Set('chart.title.xaxis', '');
2913
- obj2.Set('chart.title.yaxis', '');
2914
- obj2.Set('chart.key', []);
2915
- obj2.Draw();
2916
-
2917
-
2918
- /**
2919
- * This effectively hides the line
2920
- */
2921
- this.set('chart.line.visible', false);
2922
-
2923
-
2924
- RGraph.clear(ca);
2925
- RGraph.redrawCanvas(ca);
2926
2914
 
2927
- /**
2928
- * Place a DIV over the canvas to stop interaction with it
2929
- */
2930
- if (!ca.__rgraph_scatter_trace_cover__) {
2931
- var div2 = document.createElement('DIV');
2932
- div2.id = '__rgraph_trace_animation_' + RGraph.random(0, 4351623) + '__';
2933
- div2.style.left = xy[0] + 'px';
2934
- div2.style.top = xy[1] + 'px';
2935
- div2.style.width = ca.width + 'px';
2936
- div2.style.height = ca.height + 'px';
2937
- div2.style.position = 'absolute';
2938
- div2.style.overflow = 'hidden';
2939
- div2.style.backgroundColor = 'rgba(0,0,0,0)';
2940
- div.div2 = div2;
2941
- ca.__rgraph_scatter_trace_cover__ = div2
2942
- obj.canvas.parentNode.appendChild(div2);
2943
-
2944
- } else {
2945
- div2 = ca.__rgraph_scatter_trace_cover__;
2946
- }
2947
-
2948
- /**
2949
- * Animate the DIV that contains the canvas
2950
- */
2951
- jQuery('#' + div.id).animate({
2952
- width: ca.width + 'px'
2953
- }, opt['duration'], function ()
2954
- {
2955
- // Remove the window resize listener
2956
- window.removeEventListener('resize', reposition_canvas2, false);
2957
-
2958
- div.style.display = 'none';
2959
- div2.style.display = 'none';
2960
-
2961
- //div.removeChild(canvas2);
2962
- obj.Set('chart.line.visible', true);
2963
-
2964
- // Revert the colors back to what they were
2965
- obj.Set('chart.colors', RGraph.array_clone(obj2.Get('chart.colors')));
2966
- obj.Set('chart.key', RG.array_clone(obj2.Get('chart.key')));
2967
-
2968
- RGraph.RedrawCanvas(ca);
2969
-
2970
- ca.__rgraph_trace_cover__ = null;
2971
-
2972
- callback(obj);
2973
- });
2915
+ iterator();
2974
2916
 
2975
2917
  return this;
2976
2918
  };