rgraph-rails 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
  };