rgraph-rails 5.00 → 6.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish-geml.yaml +46 -0
  3. data/.gitignore +1 -0
  4. data/README.md +4 -5
  5. data/lib/rgraph-rails/version.rb +1 -1
  6. data/rgraph-rails.gemspec +4 -4
  7. data/vendor/assets/javascripts/RGraph.activity.js +1691 -0
  8. data/vendor/assets/javascripts/RGraph.bar.js +4253 -236
  9. data/vendor/assets/javascripts/RGraph.bipolar.js +3958 -162
  10. data/vendor/assets/javascripts/RGraph.common.annotate.js +414 -35
  11. data/vendor/assets/javascripts/RGraph.common.context.js +635 -30
  12. data/vendor/assets/javascripts/RGraph.common.core.js +10485 -419
  13. data/vendor/assets/javascripts/RGraph.common.csv.js +508 -27
  14. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1693 -90
  15. data/vendor/assets/javascripts/RGraph.common.effects.js +1629 -89
  16. data/vendor/assets/javascripts/RGraph.common.key.js +1003 -53
  17. data/vendor/assets/javascripts/RGraph.common.moment.js +5670 -0
  18. data/vendor/assets/javascripts/RGraph.common.sheets.js +541 -31
  19. data/vendor/assets/javascripts/RGraph.common.sheets.php +351 -0
  20. data/vendor/assets/javascripts/RGraph.common.starburst.js +382 -0
  21. data/vendor/assets/javascripts/RGraph.common.table.js +386 -0
  22. data/vendor/assets/javascripts/RGraph.common.tooltips.js +1433 -32
  23. data/vendor/assets/javascripts/RGraph.drawing.background.js +660 -35
  24. data/vendor/assets/javascripts/RGraph.drawing.circle.js +618 -34
  25. data/vendor/assets/javascripts/RGraph.drawing.image.js +857 -52
  26. data/vendor/assets/javascripts/RGraph.drawing.line.js +712 -0
  27. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +760 -38
  28. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +740 -37
  29. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +573 -36
  30. data/vendor/assets/javascripts/RGraph.drawing.poly.js +667 -36
  31. data/vendor/assets/javascripts/RGraph.drawing.rect.js +638 -34
  32. data/vendor/assets/javascripts/RGraph.drawing.text.js +672 -37
  33. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +653 -52
  34. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +714 -51
  35. data/vendor/assets/javascripts/RGraph.fuel.js +1149 -59
  36. data/vendor/assets/javascripts/RGraph.funnel.js +1277 -56
  37. data/vendor/assets/javascripts/RGraph.gantt.js +1646 -82
  38. data/vendor/assets/javascripts/RGraph.gauge.js +1773 -89
  39. data/vendor/assets/javascripts/RGraph.hbar.js +3869 -159
  40. data/vendor/assets/javascripts/RGraph.horseshoe.js +970 -0
  41. data/vendor/assets/javascripts/RGraph.hprogress.js +1829 -81
  42. data/vendor/assets/javascripts/RGraph.line.js +5293 -244
  43. data/vendor/assets/javascripts/RGraph.meter.js +1570 -77
  44. data/vendor/assets/javascripts/RGraph.modaldialog.js +300 -19
  45. data/vendor/assets/javascripts/RGraph.odo.js +1553 -68
  46. data/vendor/assets/javascripts/RGraph.pie.js +3273 -129
  47. data/vendor/assets/javascripts/RGraph.radar.js +2333 -108
  48. data/vendor/assets/javascripts/RGraph.rose.js +2685 -114
  49. data/vendor/assets/javascripts/RGraph.rscatter.js +1920 -80
  50. data/vendor/assets/javascripts/RGraph.scatter.js +4215 -171
  51. data/vendor/assets/javascripts/RGraph.segmented.js +1006 -0
  52. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1980 -59
  53. data/vendor/assets/javascripts/RGraph.svg.activity.js +1696 -0
  54. data/vendor/assets/javascripts/RGraph.svg.bar.js +2575 -77
  55. data/vendor/assets/javascripts/RGraph.svg.bipolar.js +3533 -106
  56. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +240 -21
  57. data/vendor/assets/javascripts/RGraph.svg.common.core.js +7105 -299
  58. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +408 -28
  59. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1291 -68
  60. data/vendor/assets/javascripts/RGraph.svg.common.key.js +451 -20
  61. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +543 -31
  62. data/vendor/assets/javascripts/RGraph.svg.common.table.js +391 -0
  63. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +1072 -23
  64. data/vendor/assets/javascripts/RGraph.svg.funnel.js +1151 -32
  65. data/vendor/assets/javascripts/RGraph.svg.gauge.js +1429 -34
  66. data/vendor/assets/javascripts/RGraph.svg.hbar.js +2692 -65
  67. data/vendor/assets/javascripts/RGraph.svg.horseshoe.js +969 -0
  68. data/vendor/assets/javascripts/RGraph.svg.line.js +2855 -86
  69. data/vendor/assets/javascripts/RGraph.svg.pie.js +1630 -58
  70. data/vendor/assets/javascripts/RGraph.svg.radar.js +1772 -58
  71. data/vendor/assets/javascripts/RGraph.svg.rose.js +2419 -83
  72. data/vendor/assets/javascripts/RGraph.svg.scatter.js +2280 -65
  73. data/vendor/assets/javascripts/RGraph.svg.segmented.js +930 -0
  74. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +1612 -29
  75. data/vendor/assets/javascripts/RGraph.svg.waterfall.js +1525 -50
  76. data/vendor/assets/javascripts/RGraph.thermometer.js +1411 -64
  77. data/vendor/assets/javascripts/RGraph.vprogress.js +1915 -81
  78. data/vendor/assets/javascripts/RGraph.waterfall.js +1896 -89
  79. data/vendor/assets/javascripts/financial-data.js +1067 -0
  80. metadata +37 -16
  81. data/.travis.yml +0 -11
  82. data/vendor/assets/javascripts/RGraph.common.deprecated.js +0 -35
  83. data/vendor/assets/javascripts/RGraph.common.resizing.js +0 -38
  84. data/vendor/assets/javascripts/RGraph.common.zoom.js +0 -15
  85. data/vendor/assets/javascripts/RGraph.cornergauge.js +0 -71
@@ -1,172 +1,4216 @@
1
+ 'version:2023-09-16 (6.14)';
2
+ //
3
+ // o--------------------------------------------------------------------------------o
4
+ // | This file is part of the RGraph package - you can learn more at: |
5
+ // | |
6
+ // | https://www.rgraph.net |
7
+ // | |
8
+ // | RGraph is licensed under the Open Source MIT license. That means that it's |
9
+ // | totally free to use and there are no restrictions on what you can do with it! |
10
+ // o--------------------------------------------------------------------------------o
1
11
 
2
- RGraph=window.RGraph||{isRGraph:true};RGraph.Scatter=function(conf)
3
- {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var parseConfObjectForOptions=true;this.data=new Array(conf.data.length);this.data=RGraph.arrayClone(conf.data);if(typeof conf.data==='object'&&typeof conf.data[0]==='object'&&(typeof conf.data[0][0]==='number'||typeof conf.data[0][0]==='string')){var tmp=RGraph.arrayClone(conf.data);conf.data=new Array();conf.data[0]=RGraph.arrayClone(tmp);this.data=RGraph.arrayClone(conf.data);}}else{var conf={id:conf};conf.data=arguments[1];this.data=[];if(arguments[1][0]&&arguments[1][0][0]&&typeof arguments[1][0][0]=='object'){for(var i=0;i<arguments[1].length;++i){this.data[i]=RGraph.arrayClone(arguments[1][i]);}}else{for(var i=1;i<arguments.length;++i){this.data[i-1]=RGraph.arrayClone(arguments[i]);}}}
4
- if(!RGraph.isArray(this.data[0][0])){this.data=[this.data];}
5
- for(var i=0,len=this.data.length;i<len;++i){for(var j=0,len2=this.data[i].length;j<len2;++j){if(typeof this.data[i][j]==='object'&&!RGraph.isNull(this.data[i][j])&&typeof this.data[i][j][0]==='string'){if(this.data[i][j][0].match(/^[.0-9]+$/)){this.data[i][j][0]=parseFloat(this.data[i][j][0]);}else if(this.data[i][j][0]===''){this.data[i][j][0]=0;}}
6
- if(typeof this.data[i][j]==='object'&&!RGraph.isNull(this.data[i][j])&&typeof this.data[i][j][1]==='string'){if(this.data[i][j][1].match(/[.0-9]+/)){this.data[i][j][1]=parseFloat(this.data[i][j][1]);}else if(this.data[i][j][1]===''){this.data[i][j][1]=0;}}}}
7
- this.id=conf.id;this.canvas=document.getElementById(this.id);this.canvas.__object__=this;this.context=this.canvas.getContext?this.canvas.getContext('2d'):null;this.max=0;this.coords=[];this.type='scatter';this.isRGraph=true;this.uid=RGraph.CreateUID();this.canvas.uid=this.canvas.uid?this.canvas.uid:RGraph.CreateUID();this.colorsParsed=false;this.coordsText=[];this.original_colors=[];this.firstDraw=true;this.propertyNameAliases={};this.properties={'chart.background.bars.count':null,'chart.background.bars.color1':'rgba(0,0,0,0)','chart.background.bars.color2':'rgba(0,0,0,0)','chart.background.hbars':null,'chart.background.vbars':null,'chart.background.grid':true,'chart.background.grid.linewidth':1,'chart.background.grid.color':'#ddd','chart.background.grid.hsize':20,'chart.background.grid.vsize':20,'chart.background.grid.vlines':true,'chart.background.grid.hlines':true,'chart.background.grid.border':true,'chart.background.grid.autofit':true,'chart.background.grid.autofit.align':true,'chart.background.grid.hlines.count':5,'chart.background.grid.vlines.count':20,'chart.background.image':null,'chart.background.image.stretch':true,'chart.background.image.x':null,'chart.background.image.y':null,'chart.background.image.w':null,'chart.background.image.h':null,'chart.background.image.align':null,'chart.background.color':null,'chart.colors.bubble.graduated':true,'chart.text.color':'black','chart.text.font':'Arial, Verdana, sans-serif','chart.text.size':12,'chart.text.bold':false,'chart.text.italic':false,'chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':false,'chart.tooltips':[],'chart.tooltips.effect':'fade','chart.tooltips.event':'onmousemove','chart.tooltips.hotspot':3,'chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.highlight':true,'chart.tooltips.coords.page':false,'chart.xaxis':true,'chart.xaxis.position':'bottom','chart.xaxis.tickmarks.count':true,'chart.xaxis.tickmarks.last':true,'chart.xaxis.title':'','chart.xaxis.title.bold':null,'chart.xaxis.title.size':null,'chart.xaxis.title.font':null,'chart.xaxis.title.color':null,'chart.xaxis.title.italic':null,'chart.xaxis.title.pos':null,'chart.xaxis.title.x':null,'chart.xaxis.title.y':null,'chart.xaxis.labels.specific.align':'left','chart.xaxis.scale':false,'chart.xaxis.scale.min':0,'chart.xaxis.scale.max':null,'chart.xaxis.scale.units.pre':'','chart.xaxis.scale.units.post':'','chart.xaxis.scale.labels.count':10,'chart.xaxis.scale.formatter':null,'chart.xaxis.scale.decimals':0,'chart.xaxis.scale.thousand':',','chart.xaxis.scale.point':'.','chart.xaxis.labels.angle':0,'chart.xaxis.labels':[],'chart.xaxis.labels.bold':null,'chart.xaxis.labels.italic':null,'chart.xaxis.labels.color':null,'chart.xaxis.labels.font':null,'chart.xaxis.labels.size':null,'chart.xaxis.labels.offsetx':0,'chart.xaxis.labels.offsety':0,'chart.yaxis':true,'chart.yaxis.position':'left','chart.yaxis.labels':true,'chart.yaxis.labels.offsetx':0,'chart.yaxis.labels.offsety':0,'chart.yaxis.labels.count':5,'chart.yaxis.labels.specific':null,'chart.yaxis.labels.inside':false,'chart.yaxis.labels.font':null,'chart.yaxis.labels.size':null,'chart.yaxis.labels.color':null,'chart.yaxis.labels.bold':null,'chart.yaxis.labels.italic':null,'chart.yaxis.tickmarks.count':10,'chart.yaxis.scale.invert':false,'chart.yaxis.scale.units.pre':'','chart.yaxis.scale.units.post':'','chart.yaxis.scale.max':null,'chart.yaxis.scale.min':0,'chart.yaxis.scale.decimals':0,'chart.yaxis.scale.point':'.','chart.yaxis.scale.thousand':',','chart.yaxis.scale.zerostart':true,'chart.yaxis.title':'','chart.yaxis.title.bold':null,'chart.yaxis.title.italic':null,'chart.yaxis.title.size':null,'chart.yaxis.title.font':null,'chart.yaxis.title.color':null,'chart.yaxis.title.pos':null,'chart.yaxis.title.x':null,'chart.yaxis.title.y':null,'chart.tickmarks.style':'cross','chart.tickmarks.style.image.halign':'center','chart.tickmarks.style.image.valign':'center','chart.tickmarks.style.image.offsetx':0,'chart.tickmarks.style.image.offsety':0,'chart.tickmarks.size':5,'chart.margin.left':25,'chart.margin.right':25,'chart.margin.top':25,'chart.margin.bottom':30,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.bold':null,'chart.title.italic':null,'chart.title.font':null,'chart.title.size':null,'chart.title.italic':null,'chart.title.x':null,'chart.title.y':null,'chart.title.halign':null,'chart.title.valign':null,'chart.labels.ingraph':null,'chart.labels.ingraph.font':null,'chart.labels.ingraph.size':null,'chart.labels.ingraph.color':null,'chart.labels.ingraph.bold':null,'chart.labels.ingraph.italic':null,'chart.labels.above':false,'chart.labels.above.size':null,'chart.labels.above.font':null,'chart.labels.above.color':null,'chart.labels.above.bold':null,'chart.labels.above.italic':null,'chart.labels.above.decimals':0,'chart.contextmenu':null,'chart.colors.default':'black','chart.crosshairs':false,'chart.crosshairs.hline':true,'chart.crosshairs.vline':true,'chart.crosshairs.color':'#333','chart.crosshairs.linewidth':1,'chart.crosshairs.coords':false,'chart.crosshairs.coords.fixed':true,'chart.crosshairs.coords.fadeout':false,'chart.crosshairs.coords.labels.x':'X','chart.crosshairs.coords.labels.y':'Y','chart.crosshairs.coords.formatter.x':null,'chart.crosshairs.coords.formatter.y':null,'chart.annotatable':false,'chart.annotatable.color':'black','chart.annotatable.linewidth':1,'chart.line':false,'chart.line.linewidth':1,'chart.line.colors':['green','red','blue','orange','pink','brown','black','gray'],'chart.line.shadow.color':'rgba(0,0,0,0)','chart.line.shadow.blur':2,'chart.line.shadow.offsetx':3,'chart.line.shadow.offsety':3,'chart.line.stepped':false,'chart.line.visible':true,'chart.axes':true,'chart.axes.color':'black','chart.axes.linewidth':1,'chart.key':null,'chart.key.background':'white','chart.key.position':'graph','chart.key.halign':'right','chart.key.shadow':false,'chart.key.shadow.color':'#666','chart.key.shadow.blur':3,'chart.key.shadow.offsetx':2,'chart.key.shadow.offsety':2,'chart.key.position.gutter.boxed':false,'chart.key.position.x':null,'chart.key.position.y':null,'chart.key.interactive':false,'chart.key.interactive.highlight.chart.fill':'rgba(255,0,0,0.9)','chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)','chart.key.color.shape':'square','chart.key.rounded':true,'chart.key.linewidth':1,'chart.key.colors':null,'chart.key.labels.color':null,'chart.key.labels.font':null,'chart.key.labels.size':null,'chart.key.labels.bold':null,'chart.key.labels.italic':null,'chart.key.labels.offsetx':0,'chart.key.labels.offsety':0,'chart.boxplot.width':10,'chart.boxplot.capped':true,'chart.resizable':false,'chart.resizable.handle.background':null,'chart.events.mousemove':null,'chart.events.click':null,'chart.highlight.stroke':'rgba(0,0,0,0)','chart.highlight.fill':'rgba(255,255,255,0.7)','chart.clearto':'rgba(0,0,0,0)','chart.animation.trace':false,'chart.animation.trace.clip':1}
8
- for(var i=0;i<this.data.length;++i){for(var j=0;j<this.data[i].length;++j){if(RGraph.isNull(this.data[i][j])){this.data[i][j]=[];}
9
- if(this.data[i][j]&&typeof(this.data[i][j][0])=='string'){this.data[i][j][0]=RGraph.parseDate(this.data[i][j][0]);}}}
10
- this.data_arr=[];for(var i=0;i<this.data.length;++i){for(var j=0;j<this.data[i].length;++j){this.data_arr.push(this.data[i][j]);}}
11
- for(var i=0;i<this.data_arr.length;++i){this['$'+i]={}}
12
- if(!this.canvas){alert('[SCATTER] No canvas support');return;}
13
- if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
14
- var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
15
- if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
16
- this.set=this.Set=function(name)
17
- {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
18
- if(name.substr(0,6)!='chart.'){name='chart.'+name;}
19
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
20
- prop[name.toLowerCase()]=value;return this;};this.get=this.Get=function(name)
21
- {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
22
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
23
- return prop[name];};this.draw=this.Draw=function()
24
- {if(typeof prop['chart.background.image']==='string'){RG.DrawBackgroundImage(this);}
25
- RG.fireCustomEvent(this,'onbeforedraw');if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
26
- this.coordsText=[];this.marginLeft=prop['chart.margin.left'];this.marginRight=prop['chart.margin.right'];this.marginTop=prop['chart.margin.top'];this.marginBottom=prop['chart.margin.bottom'];this.hasTooltips=false;var overHotspot=false;this.coords=[];if(typeof(prop['chart.xaxis.scale.min'])=='string')prop['chart.xaxis.scale.min']=RG.parseDate(prop['chart.xaxis.scale.min']);if(typeof(prop['chart.xaxis.scale.max'])=='string')prop['chart.xaxis.scale.max']=RG.parseDate(prop['chart.xaxis.scale.max']);this.Set('chart.tooltips',[]);for(var i=0,len=this.data.length;i<len;i+=1){for(var j=0,len2=this.data[i].length;j<len2;j+=1){if(this.data[i][j]&&this.data[i][j][3]){prop['chart.tooltips'].push(this.data[i][j][3]);this.hasTooltips=true;}else{prop['chart.tooltips'].push(null);}}}
27
- this.max=0;if(typeof prop['chart.yaxis.scale.max']==='number'){this.max=prop['chart.yaxis.scale.max'];this.min=prop['chart.yaxis.scale.min']?prop['chart.yaxis.scale.min']:0;this.scale2=RG.getScale2(this,{'scale.max':this.max,'scale.min':this.min,'scale.strict':true,'scale.thousand':prop['chart.yaxis.scale.thousand'],'scale.point':prop['chart.yaxis.scale.point'],'scale.decimals':prop['chart.yaxis.scale.decimals'],'scale.labels.count':prop['chart.yaxis.labels.count'],'scale.round':prop['chart.yaxis.scale.round'],'scale.units.pre':prop['chart.yaxis.scale.units.pre'],'scale.units.post':prop['chart.yaxis.scale.units.post']});this.max=this.scale2.max;this.min=this.scale2.min;var decimals=prop['chart.yaxis.scale.decimals'];}else{var i=0;var j=0;for(i=0,len=this.data.length;i<len;i+=1){for(j=0,len2=this.data[i].length;j<len2;j+=1){if(!RG.isNull(this.data[i][j])&&this.data[i][j][1]!=null){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]));}}}
28
- this.min=prop['chart.yaxis.scale.min']?prop['chart.yaxis.scale.min']:0;this.scale2=RG.getScale2(this,{'scale.max':this.max,'scale.min':this.min,'scale.thousand':prop['chart.yaxis.scale.thousand'],'scale.point':prop['chart.yaxis.scale.point'],'scale.decimals':prop['chart.yaxis.scale.decimals'],'scale.labels.count':prop['chart.yaxis.labels.count'],'scale.round':prop['chart.yaxis.scale.round'],'scale.units.pre':prop['chart.yaxis.scale.units.pre'],'scale.units.post':prop['chart.yaxis.scale.units.post']});this.max=this.scale2.max;this.min=this.scale2.min;}
29
- this.grapharea=ca.height-this.marginTop-this.marginBottom;RG.background.Draw(this);if(prop['chart.background.hbars']&&prop['chart.background.hbars'].length){RG.DrawBars(this);}
30
- if(prop['chart.background.vbars']&&prop['chart.background.vbars'].length){this.DrawVBars();}
31
- if(prop['chart.axes']){this.DrawAxes();}
32
- this.DrawLabels();if(prop['chart.animation.trace']){co.save();co.beginPath();co.rect(0,0,ca.width*prop['chart.animation.trace.clip'],ca.height);co.clip();}
33
- for(i=0;i<this.data.length;++i){this.DrawMarks(i);co.shadowColor=prop['chart.line.shadow.color'];co.shadowOffsetX=prop['chart.line.shadow.offsetx'];co.shadowOffsetY=prop['chart.line.shadow.offsety'];co.shadowBlur=prop['chart.line.shadow.blur'];this.DrawLine(i);RG.NoShadow(this);}
34
- if(prop['chart.line']){for(var i=0,len=this.data.length;i<len;i+=1){this.DrawMarks(i);}}
35
- if(prop['chart.animation.trace']){co.restore();}
36
- if(prop['chart.contextmenu']){RG.ShowContext(this);}
37
- if(prop['chart.key']&&prop['chart.key'].length){RG.DrawKey(this,prop['chart.key'],prop['chart.line.colors']);}
38
- if(prop['chart.labels.above']){this.DrawAboveLabels();}
39
- this.DrawInGraphLabels(this);if(prop['chart.resizable']){RG.AllowResizing(this);}
40
- RG.InstallEventListeners(this);if(this.firstDraw){this.firstDraw=false;RG.fireCustomEvent(this,'onfirstdraw');this.firstDrawFunc();}
41
- RG.FireCustomEvent(this,'ondraw');return this;}
42
- this.drawAxes=this.DrawAxes=function()
43
- {var graphHeight=ca.height-this.marginTop-this.marginBottom;co.beginPath();co.strokeStyle=prop['chart.axes.color'];co.lineWidth=(prop['chart.axes.linewidth']||1)+0.001;if(prop['chart.yaxis']){if(prop['chart.yaxis.position']=='left'){co.moveTo(this.marginLeft,this.marginTop);co.lineTo(this.marginLeft,ca.height-this.marginBottom);}else{co.moveTo(ca.width-this.marginRight,this.marginTop);co.lineTo(ca.width-this.marginRight,ca.height-this.marginBottom);}}
44
- if(prop['chart.xaxis']){if(prop['chart.xaxis.position']=='center'){co.moveTo(this.marginLeft,ma.round(this.marginTop+((ca.height-this.marginTop-this.marginBottom)/2)));co.lineTo(ca.width-this.marginRight,ma.round(this.marginTop+((ca.height-this.marginTop-this.marginBottom)/2)));}else{var y=this.getYCoord(this.scale2.min>0?this.scale2.min:0);if(prop['chart.yaxis.scale.invert']&&this.scale2.min===0){y=ca.height-this.marginBottom;}
45
- co.moveTo(this.marginLeft,y);co.lineTo(ca.width-this.marginRight,y);}}
46
- if(prop['chart.yaxis']){var numyticks=prop['chart.yaxis.tickmarks.count'];for(i=0;i<numyticks;++i){var y=((ca.height-this.marginTop-this.marginBottom)/numyticks)*i;y=y+this.marginTop;if(prop['chart.xaxis.position']=='center'&&i==(numyticks/2)){continue;}
47
- if(prop['chart.yaxis.position']=='left'){co.moveTo(this.marginLeft,ma.round(y));co.lineTo(this.marginLeft-3,ma.round(y));}else{co.moveTo(ca.width-this.marginRight+3,Math.round(y));co.lineTo(ca.width-this.marginRight,Math.round(y));}}
48
- if(prop['chart.yaxis.tickmarks.count']>0){if(prop['chart.xaxis.position']==='center'&&prop['chart.yaxis.position']=='left'){co.moveTo(this.marginLeft,ma.round(ca.height-this.marginBottom));co.lineTo(this.marginLeft-3,ma.round(ca.height-this.marginBottom));}else if(prop['chart.xaxis.position']==='center'&&prop['chart.yaxis.position']=='right'){co.moveTo(ca.width-this.marginRight+3,ma.round(ca.height-this.marginBottom));co.lineTo(ca.width-this.marginRight,ma.round(ca.height-this.marginBottom));}}
49
- if(!prop['chart.xaxis']&&prop['chart.yaxis.position']==='left'){co.moveTo(this.marginLeft,ma.round(ca.height-this.marginBottom));co.lineTo(this.marginLeft-3,ma.round(ca.height-this.marginBottom));}else if(!prop['chart.xaxis']&&prop['chart.yaxis.position']==='right'){co.moveTo(ca.width-this.marginRight,ma.round(ca.height-this.marginBottom));co.lineTo(ca.width-this.marginRight+3,ma.round(ca.height-this.marginBottom));}
50
- if(prop['chart.xaxis.position']==='bottom'&&prop['chart.yaxis.tickmarks.count']>0&&prop['chart.yaxis.scale.min']<0){if(prop['chart.yaxis.position']=='left'){co.moveTo(this.marginLeft,ma.round(this.getYCoord(prop['chart.yaxis.scale.min'])));co.lineTo(this.marginLeft-3,ma.round(this.getYCoord(prop['chart.yaxis.scale.min'])));}else{co.moveTo(ca.width-this.marginRight+3,ma.round(this.getYCoord(prop['chart.yaxis.scale.min'])));co.lineTo(ca.width-this.marginRight,ma.round(this.getYCoord(prop['chart.yaxis.scale.min'])));}}}
51
- if(prop['chart.xaxis.tickmarks.count']>0&&prop['chart.xaxis']){var x=0,y=this.getYCoord(prop['chart.yaxis.scale.invert']?this.scale2.max:(this.scale2.max>0&&this.scale2.min>0?this.scale2.min:0))-3,size=3;if(prop['chart.yaxis.scale.min']===0&&prop['chart.xaxis.position']==='bottom'){y+=3;}
52
- if(this.scale2.max>0&&this.scale2.min>0){y+=3;}
53
- this.xTickGap=(prop['chart.xaxis.labels']&&prop['chart.xaxis.labels'].length)?((ca.width-this.marginLeft-this.marginRight)/prop['chart.xaxis.labels'].length):(ca.width-this.marginLeft-this.marginRight)/10;if(typeof(prop['chart.xaxis.tickmarks.count'])=='number'){this.xTickGap=(ca.width-this.marginLeft-this.marginRight)/prop['chart.xaxis.tickmarks.count'];}
54
- for(x=(this.marginLeft+(prop['chart.yaxis.position']=='left'&&prop['chart.yaxis']?this.xTickGap:0));x<=(ca.width-this.marginRight-(prop['chart.yaxis.position']=='left'||!prop['chart.yaxis']?-1:1));x+=this.xTickGap){if(prop['chart.yaxis.position']=='left'&&!prop['chart.xaxis.tickmarks.last']&&x==(ca.width-this.marginRight)){continue;}else if(prop['chart.yaxis.position']=='right'&&!prop['chart.xaxis.tickmarks.last']&&x==this.marginLeft){continue;}
55
- co.moveTo(ma.round(x),y);co.lineTo(ma.round(x),y+(prop['chart.xaxis.position']==='center'||prop['chart.yaxis.scale.min']<0?size*2:size));}}
56
- co.stroke();co.lineWidth=1;};this.drawLabels=this.DrawLabels=function()
57
- {co.fillStyle=prop['chart.text.color'];var font=prop['chart.text.font'],xMin=prop['chart.xaxis.scale.min'],xMax=prop['chart.xaxis.scale.max'],yMax=this.scale2.max,yMin=prop['chart.yaxis.scale.min']?prop['chart.yaxis.scale.min']:0,text_size=prop['chart.text.size'],units_pre=prop['chart.yaxis.scale.units.pre'],units_post=prop['chart.yaxis.scale.units.post'],numYLabels=prop['chart.yaxis.labels.count'],invert=prop['chart.yaxis.scale.invert'],inside=prop['chart.yaxis.labels.inside'],context=co,canvas=ca,boxed=false,offsetx=prop['chart.yaxis.labels.offsetx'],offsety=prop['chart.yaxis.labels.offsety']
58
- this.halfTextHeight=text_size/2;this.halfGraphHeight=(ca.height-this.marginTop-this.marginBottom)/2;if(prop['chart.yaxis.labels']){var xPos=prop['chart.yaxis.position']=='left'?this.marginLeft-5:ca.width-this.marginRight+5;var align=prop['chart.yaxis.position']=='right'?'left':'right';if(inside){if(prop['chart.yaxis.position']=='left'){xPos=prop['chart.margin.left']+5;align='left';boxed=true;}else{xPos=ca.width-prop['chart.margin.right']-5;align='right';boxed=true;}}
59
- var textConf=RG.getTextConf({object:this,prefix:'chart.yaxis.labels'});if(prop['chart.xaxis.position']==='center'){if(typeof(prop['chart.yaxis.labels.specific'])=='object'&&prop['chart.yaxis.labels.specific']!=null&&prop['chart.yaxis.labels.specific'].length){var labels=prop['chart.yaxis.labels.specific'];if(prop['chart.yaxis.scale.min']>0){labels=[];for(var i=0;i<(prop['chart.yaxis.labels.specific'].length-1);++i){labels.push(prop['chart.yaxis.labels.specific'][i]);}}
60
- for(var i=0;i<labels.length;++i){var y=this.marginTop+(i*(this.grapharea/(labels.length*2)));RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:y+offsety,text:labels[i],valign:'center',halign:align,bounding:boxed,tag:'labels.specific'});}
61
- var reversed_labels=RG.array_reverse(labels);for(var i=0;i<reversed_labels.length;++i){var y=this.marginTop+(this.grapharea/2)+((i+1)*(this.grapharea/(labels.length*2)));RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:y+offsety,text:reversed_labels[i],valign:'center',halign:align,bounding:boxed,tag:'labels.specific'});}
62
- if(prop['chart.yaxis.scale.min']!=0){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:(this.grapharea/2)+this.marginTop+offsety,text:prop['chart.yaxis.labels.specific'][prop['chart.yaxis.labels.specific'].length-1],valign:'center',halign:align,bounding:boxed,tag:'labels.specific'});}}
63
- if(!prop['chart.yaxis.labels.specific']&&typeof numYLabels=='number'){for(var i=0,len=this.scale2.labels.length;i<len;i+=1){if(!invert){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+this.halfGraphHeight-(((i+1)/numYLabels)*this.halfGraphHeight)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:this.scale2.labels[i],tag:'scale'});}else{RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+this.halfGraphHeight-((i/numYLabels)*this.halfGraphHeight)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:this.scale2.labels[this.scale2.labels.length-(i+1)],tag:'scale'});}}
64
- for(var i=0,len=this.scale2.labels.length;i<len;i+=1){if(!invert){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+this.halfGraphHeight+this.halfGraphHeight-((i/numYLabels)*this.halfGraphHeight)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:'-'+this.scale2.labels[len-(i+1)],tag:'scale'});}else{if(i==(len-1)&&invert){continue;}
65
- RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+this.halfGraphHeight+this.halfGraphHeight-(((i+1)/numYLabels)*this.halfGraphHeight)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:'-'+this.scale2.labels[i],tag:'scale'});}}
66
- if(!invert&&(yMin>0||prop['chart.yaxis.scale.zerostart'])){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+this.halfGraphHeight+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat({object:this,number:yMin.toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post}),tag:'scale'});}
67
- if(invert){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat({object:this,number:yMin.toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post}),tag:'scale'});RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+(this.halfGraphHeight*2)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:'-'+RG.numberFormat({object:this,number:yMin.toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post}),tag:'scale'});}}}else{var xPos=prop['chart.yaxis.position']=='left'?this.marginLeft-5:ca.width-this.marginRight+5;var align=prop['chart.yaxis.position']=='right'?'left':'right';if(inside){if(prop['chart.yaxis.position']=='left'){xPos=prop['chart.margin.left']+5;align='left';boxed=true;}else{xPos=ca.width-this.marginRight-5;align='right';boxed=true;}}
68
- if(typeof prop['chart.yaxis.labels.specific']==='object'&&prop['chart.yaxis.labels.specific']){var labels=prop['chart.yaxis.labels.specific'];if(prop['chart.yaxis.scale.min']>9999){labels=[];for(var i=0;i<(prop['chart.yaxis.labels.specific'].length-1);++i){labels.push(prop['chart.yaxis.labels.specific'][i]);}}
69
- for(var i=0,len=labels.length;i<len;i+=1){var y=this.marginTop+(i*(this.grapharea/(len-1)));RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:y+offsety,text:labels[i],halign:align,valign:'center',bounding:boxed,tag:'scale'});}}else{if(typeof(numYLabels)=='number'){if(invert){for(var i=0;i<numYLabels;++i){var interval=(ca.height-this.marginTop-this.marginBottom)/numYLabels;RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+((i+1)*interval)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:this.scale2.labels[i],tag:'scale'});}
70
- if(!prop['chart.xaxis']&&!prop['chart.yaxis.scale.min']){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat({object:this,number:this.min.toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post}),tag:'scale'});}}else{for(var i=0,len=this.scale2.labels.length;i<len;i+=1){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+this.grapharea-(((i+1)/this.scale2.labels.length)*this.grapharea)+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:this.scale2.labels[i],tag:'scale'});}
71
- if(!prop['chart.xaxis']&&prop['chart.yaxis.scale.min']===0){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:ca.height-this.marginBottom+offsety,valign:'center',halign:align,boundin:boxed,boundingFill:'white',text:RG.numberFormat({object:this,number:(0).toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post,point:prop['chart.yaxis.scale.point'],thousand:prop['chart.yaxis.scale.thousand']}),tag:'scale'});}}}
72
- if((prop['chart.yaxis.scale.min']||prop['chart.yaxis.scale.zerostart'])&&!invert){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:ca.height-this.marginBottom+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat({object:this,number:prop['chart.yaxis.scale.min'].toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post,point:prop['chart.yaxis.scale.point'],thousand:prop['chart.yaxis.scale.thousand']}),tag:'scale'});}else if(invert){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offsetx,y:this.marginTop+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat({object:this,number:prop['chart.yaxis.scale.min'].toFixed(this.scale2.min===0?0:prop['chart.yaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post,point:prop['chart.yaxis.scale.point'],thousand:prop['chart.yaxis.scale.thousand']}),tag:'scale'});}}}}
73
- if(prop['chart.xaxis.scale']){var textConf=RG.getTextConf({object:this,prefix:'chart.xaxis.labels'});var numXLabels=prop['chart.xaxis.scale.labels.count'],y=ca.height-this.marginBottom+5+(text_size/2),units_pre_x=prop['chart.xaxis.scale.units.pre'],units_post_x=prop['chart.xaxis.scale.units.post'],decimals=prop['chart.xaxis.scale.decimals'],point=prop['chart.xaxis.scale.point'],thousand=prop['chart.xaxis.scale.thousand'],color=prop['chart.xaxis.labels.color'],bold=prop['chart.xaxis.labels.bold'],offsetx=prop['chart.xaxis.labels.offsetx'],offsety=prop['chart.xaxis.labels.offsety'],angle=prop['chart.xaxis.labels.angle'];if(angle>0){angle*=-1;valign='center';halign='right';y+=5;}else{valign='center';halign='center';}
74
- if(!prop['chart.xaxis.scale.max']){var xmax=0;var xmin=prop['chart.xaxis.scale.min'];for(var ds=0,len=this.data.length;ds<len;ds+=1){for(var point=0,len2=this.data[ds].length;point<len2;point+=1){xmax=Math.max(xmax,this.data[ds][point][0]);}}}else{xmax=prop['chart.xaxis.scale.max'];xmin=prop['chart.xaxis.scale.min']}
75
- this.xscale2=RG.getScale2(this,{'scale.max':xmax,'scale.min':xmin,'scale.decimals':decimals,'scale.point':point,'scale.thousand':thousand,'scale.units.pre':units_pre_x,'scale.units.post':units_post_x,'scale.labels.count':numXLabels,'scale.strict':true});this.Set('chart.xaxis.scale.max',this.xscale2.max);var interval=(ca.width-this.marginLeft-this.marginRight)/this.xscale2.labels.length;for(var i=0,len=this.xscale2.labels.length;i<len;i+=1){var num=((prop['chart.xaxis.scale.max']-prop['chart.xaxis.scale.min'])*((i+1)/numXLabels))+(xmin||0),x=this.marginLeft+((i+1)*interval),text=typeof prop['chart.xaxis.scale.formatter']==='function'?String(prop['chart.xaxis.scale.formatter'](this,num)):this.xscale2.labels[i];RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x+offsetx,y:y+offsety,valign:valign,halign:halign,text:text,angle:angle,tag:'xscale'});}
76
- var text=typeof prop['chart.xaxis.scale.formatter']==='function'?String(prop['chart.xaxis.scale.formatter'](this,prop['chart.xaxis.scale.min'])):String(prop['chart.xaxis.scale.min']);RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+offsetx,y:y+offsety,valign:'center',halign:'center',text:text,tag:'xscale',angle:angle});}else{var textConf=RG.getTextConf({object:this,prefix:'chart.xaxis.labels'});var graphArea=ca.width-this.marginLeft-this.marginRight;var xInterval=graphArea/prop['chart.xaxis.labels'].length;var xPos=this.marginLeft;var yPos=(ca.height-this.marginBottom)+3;var labels=prop['chart.xaxis.labels'];var color=prop['chart.xaxis.labels.color'];var bold=prop['chart.xaxis.labels.bold'];var offsetx=prop['chart.xaxis.labels.offsetx'];var offsety=prop['chart.xaxis.labels.offsety'];var angle=0;var valign='top';var halign='center';if(prop['chart.xaxis.labels.angle']>0){angle=-1*prop['chart.xaxis.labels.angle'];valign='center';halign='right';yPos+=10;}
77
- for(i=0;i<labels.length;++i){if(typeof(labels[i])=='object'){if(prop['chart.xaxis.labels.specific.align']=='center'){var rightEdge=0;if(labels[i+1]&&labels[i+1][1]){rightEdge=labels[i+1][1];}else{rightEdge=prop['chart.xaxis.scale.max'];}
78
- var offset=(this.getXCoord(rightEdge)-this.getXCoord(labels[i][1]))/2;}else{var offset=5;}
79
- var xPos=this.getXCoord(labels[i][1]);if(RG.isNull(xPos)){continue;}
80
- RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+offset+offsetx,y:yPos+offsety,valign:valign,halign:angle!=0?'right':(prop['chart.xaxis.labels.specific.align']=='center'?'center':'left'),text:String(labels[i][0]),angle:angle,marker:false,tag:'labels.specific'});co.beginPath();co.strokeStyle='#bbb';co.moveTo(ma.round(this.marginLeft+(graphArea*((labels[i][1]-xMin)/(prop['chart.xaxis.scale.max']-xMin)))),ca.height-this.marginBottom);co.lineTo(ma.round(this.marginLeft+(graphArea*((labels[i][1]-xMin)/(prop['chart.xaxis.scale.max']-xMin)))),ca.height-this.marginBottom+20);co.stroke();}else{RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:xPos+(xInterval/2)+offsetx,y:yPos+offsety,valign:valign,halign:halign,text:String(labels[i]),angle:angle,tag:'labels'});}
81
- xPos+=xInterval;}
82
- if(typeof(labels[0])=='object'){co.beginPath();co.strokeStyle='#bbb';co.moveTo(this.marginLeft+graphArea,ca.height-this.marginBottom);co.lineTo(this.marginLeft+graphArea,ca.height-this.marginBottom+20);co.stroke();}}};this.drawMarks=this.DrawMarks=function(i)
83
- {this.coords[i]=[];var xmax=prop['chart.xaxis.scale.max'];var default_color=prop['chart.colors.default'];for(var j=0,len=this.data[i].length;j<len;j+=1){var data_points=this.data[i];if(RG.isNull(data_points[j])){continue;}
84
- var xCoord=data_points[j][0];var yCoord=data_points[j][1];var color=data_points[j][2]?data_points[j][2]:default_color;var tooltip=(data_points[j]&&data_points[j][3])?data_points[j][3]:null;this.DrawMark(i,xCoord,yCoord,xmax,this.scale2.max,color,tooltip,this.coords[i],data_points,j);}};this.drawMark=this.DrawMark=function(data_set_index,x,y,xMax,yMax,color,tooltip,coords,data,data_index)
85
- {var tickmarks=prop['chart.tickmarks.style'],tickSize=prop['chart.tickmarks.size'],xMin=prop['chart.xaxis.scale.min'],x=((x-xMin)/(xMax-xMin))*(ca.width-this.marginLeft-this.marginRight),originalX=x,originalY=y;if(tickmarks&&typeof(tickmarks)=='object'){tickmarks=tickmarks[data_set_index];}
86
- if(typeof(tickSize)=='object'){var tickSize=tickSize[data_set_index];var halfTickSize=tickSize/2;}else{var halfTickSize=tickSize/2;}
87
- if(y&&typeof y==='object'&&typeof y[0]==='number'&&typeof y[1]==='number'&&typeof y[2]==='number'&&typeof y[3]==='number'&&typeof y[4]==='number'){this.Set('chart.boxplot',true);var y0=this.getYCoord(y[0]),y1=this.getYCoord(y[1]),y2=this.getYCoord(y[2]),y3=this.getYCoord(y[3]),y4=this.getYCoord(y[4]),col1=y[5],col2=y[6],boxWidth=typeof y[7]=='number'?y[7]:prop['chart.boxplot.width'];}else{var yCoord=this.getYCoord(y);}
88
- x+=this.marginLeft;co.beginPath();co.strokeStyle=color;if(prop['chart.boxplot']){boxWidth=(boxWidth/prop['chart.xaxis.scale.max'])*(ca.width-this.marginLeft-this.marginRight);var halfBoxWidth=boxWidth/2;if(prop['chart.line.visible']){co.beginPath();if(typeof y[8]==='string'){co.strokeStyle=y[8];}
89
- co.strokeRect(x-halfBoxWidth,y1,boxWidth,y3-y1);if(col1){co.fillStyle=col1;co.fillRect(x-halfBoxWidth,y1,boxWidth,y2-y1);}
90
- if(col2){co.fillStyle=col2;co.fillRect(x-halfBoxWidth,y2,boxWidth,y3-y2);}
91
- co.stroke();co.beginPath();if(prop['chart.boxplot.capped']){co.moveTo(x-halfBoxWidth,ma.round(y0));co.lineTo(x+halfBoxWidth,ma.round(y0));}
92
- co.moveTo(ma.round(x),y0);co.lineTo(ma.round(x),y1);if(prop['chart.boxplot.capped']){co.moveTo(x-halfBoxWidth,ma.round(y4));co.lineTo(x+halfBoxWidth,ma.round(y4));}
93
- co.moveTo(ma.round(x),y4);co.lineTo(ma.round(x),y3);co.stroke();}}
94
- if(prop['chart.line.visible']&&typeof(y)=='number'&&!y0&&!y1&&!y2&&!y3&&!y4){if(tickmarks=='circle'){co.arc(x,yCoord,halfTickSize,0,6.28,0);co.fillStyle=color;co.fill();}else if(tickmarks=='plus'){co.moveTo(x,yCoord-halfTickSize);co.lineTo(x,yCoord+halfTickSize);co.moveTo(x-halfTickSize,yCoord);co.lineTo(x+halfTickSize,yCoord);co.stroke();}else if(tickmarks=='square'){co.strokeStyle=color;co.fillStyle=color;co.fillRect(x-halfTickSize,yCoord-halfTickSize,tickSize,tickSize);}else if(tickmarks=='cross'){co.moveTo(x-halfTickSize,yCoord-halfTickSize);co.lineTo(x+halfTickSize,yCoord+halfTickSize);co.moveTo(x+halfTickSize,yCoord-halfTickSize);co.lineTo(x-halfTickSize,yCoord+halfTickSize);co.stroke();}else if(tickmarks=='diamond'){co.fillStyle=co.strokeStyle;co.moveTo(x,yCoord-halfTickSize);co.lineTo(x+halfTickSize,yCoord);co.lineTo(x,yCoord+halfTickSize);co.lineTo(x-halfTickSize,yCoord);co.lineTo(x,yCoord-halfTickSize);co.fill();co.stroke();}else if(typeof(tickmarks)=='function'){var graphWidth=ca.width-this.marginLeft-this.marginRight,graphheight=ca.height-this.marginTop-this.marginBottom,xVal=((x-this.marginLeft)/graphWidth)*xMax,yVal=((graphheight-(yCoord-this.marginTop))/graphheight)*yMax;tickmarks(this,data,x,yCoord,xVal,yVal,xMax,yMax,color,data_set_index,data_index)}else if(typeof tickmarks==='string'&&(tickmarks.substr(0,6)==='image:'||tickmarks.substr(0,5)==='data:'||tickmarks.substr(0,1)==='/'||tickmarks.substr(0,3)==='../'||tickmarks.substr(0,7)==='images/')){var img=new Image();if(tickmarks.substr(0,6)==='image:'){img.src=tickmarks.substr(6);}else{img.src=tickmarks;}
95
- img.onload=function()
96
- {if(prop['chart.tickmarks.style.image.halign']==='center')x-=(this.width/2);if(prop['chart.tickmarks.style.image.halign']==='right')x-=this.width;if(prop['chart.tickmarks.style.image.valign']==='center')yCoord-=(this.height/2);if(prop['chart.tickmarks.style.image.valign']==='bottom')yCoord-=this.height;x+=prop['chart.tickmarks.style.image.offsetx'];yCoord+=prop['chart.tickmarks.style.image.offsety'];co.drawImage(this,x,yCoord);}}else if(tickmarks===null){}else{alert('[SCATTER] ('+this.id+') Unknown tickmark style: '+tickmarks);}}
97
- if(prop['chart.boxplot']&&typeof y0==='number'&&typeof y1==='number'&&typeof y2==='number'&&typeof y3==='number'&&typeof y4==='number'){x=[x-halfBoxWidth,x+halfBoxWidth];yCoord=[y0,y1,y2,y3,y4];}
98
- coords.push([x,yCoord,tooltip]);};this.drawLine=this.DrawLine=function(i)
99
- {if(typeof(prop['chart.line.visible'])=='boolean'&&prop['chart.line.visible']==false){return;}
100
- if(prop['chart.line']&&this.coords[i].length>=2){if(prop['chart.line.dash']&&typeof co.setLineDash==='function'){co.setLineDash(prop['chart.line.dash']);}
101
- co.lineCap='round';co.lineJoin='round';co.lineWidth=this.getLineWidth(i);co.strokeStyle=prop['chart.line.colors'][i];co.beginPath();var prevY=null;var currY=null;for(var j=0,len=this.coords[i].length;j<len;j+=1){var xPos=this.coords[i][j][0];var yPos=this.coords[i][j][1];if(j>0)prevY=this.coords[i][j-1][1];currY=yPos;if(j==0||RG.is_null(prevY)||RG.is_null(currY)){co.moveTo(xPos,yPos);}else{var stepped=prop['chart.line.stepped'];if((typeof stepped=='boolean'&&stepped)||(typeof stepped=='object'&&stepped[i])){co.lineTo(this.coords[i][j][0],this.coords[i][j-1][1]);}
102
- co.lineTo(xPos,yPos);}}
103
- co.stroke();if(prop['chart.line.dash']&&typeof co.setLineDash==='function'){co.setLineDash([1,0]);}}
104
- co.lineWidth=1;};this.getLineWidth=this.GetLineWidth=function(i)
105
- {var linewidth=prop['chart.line.linewidth'];if(typeof linewidth=='number'){return linewidth;}else if(typeof linewidth=='object'){if(linewidth[i]){return linewidth[i];}else{return linewidth[0];}
106
- alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');}};this.drawVBars=this.DrawVBars=function()
107
- {var vbars=prop['chart.background.vbars'];var graphWidth=ca.width-this.marginLeft-this.marginRight;if(vbars){var xmax=prop['chart.xaxis.scale.max'];var xmin=prop['chart.xaxis.scale.min'];for(var i=0,len=vbars.length;i<len;i+=1){var key=i;var value=vbars[key];if(typeof value[0]=='string')value[0]=RG.parseDate(value[0]);if(typeof value[1]=='string')value[1]=RG.parseDate(value[1])-value[0];var x=(((value[0]-xmin)/(xmax-xmin))*graphWidth)+this.marginLeft;var width=(value[1]/(xmax-xmin))*graphWidth;co.fillStyle=value[2];co.fillRect(x,this.marginTop,width,(ca.height-this.marginTop-this.marginBottom));}}};this.drawInGraphLabels=this.DrawInGraphLabels=function(obj)
108
- {var labels=obj.get('chart.labels.ingraph');var labels_processed=[];if(!labels){return;}
109
- var fgcolor='black';var bgcolor='white';var direction=1;var textConf=RG.getTextConf({object:this,prefix:'chart.labels.ingraph'});for(var i=0,len=labels.length;i<len;i+=1){if(typeof(labels[i])=='number'){for(var j=0;j<labels[i];++j){labels_processed.push(null);}}else if(typeof(labels[i])=='string'||typeof(labels[i])=='object'){labels_processed.push(labels[i]);}else{labels_processed.push('');}}
110
- RG.NoShadow(obj);if(labels_processed&&labels_processed.length>0){var i=0;for(var set=0;set<obj.coords.length;++set){for(var point=0;point<obj.coords[set].length;++point){if(labels_processed[i]){var x=obj.coords[set][point][0];var y=obj.coords[set][point][1];var length=typeof(labels_processed[i][4])=='number'?labels_processed[i][4]:25;var text_x=x;var text_y=y-5-length;co.moveTo(x,y-5);co.lineTo(x,y-5-length);co.stroke();co.beginPath();co.moveTo(x,y-5);co.lineTo(x-3,y-10);co.lineTo(x+3,y-10);co.closePath();co.beginPath();co.fillStyle=(typeof(labels_processed[i])=='object'&&typeof(labels_processed[i][1])=='string')?labels_processed[i][1]:'black';RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:text_x,y:text_y,text:(typeof(labels_processed[i])=='object'&&typeof(labels_processed[i][0])=='string')?labels_processed[i][0]:labels_processed[i],valign:'bottom',halign:'center',bounding:true,boundingFill:(typeof(labels_processed[i])=='object'&&typeof(labels_processed[i][2])=='string')?labels_processed[i][2]:'white',tag:'labels.ingraph'});co.fill();}
111
- i++;}}}};this.getShape=this.getPoint=function(e)
112
- {var mouseXY=RG.getMouseXY(e);var mouseX=mouseXY[0];var mouseY=mouseXY[1];var overHotspot=false;var offset=prop['chart.tooltips.hotspot'];for(var set=0,len=this.coords.length;set<len;++set){for(var i=0,len2=this.coords[set].length;i<len2;++i){var x=this.coords[set][i][0];var y=this.coords[set][i][1];var tooltip=this.data[set][i][3];if(typeof(y)=='number'){if(mouseX<=(x+offset)&&mouseX>=(x-offset)&&mouseY<=(y+offset)&&mouseY>=(y-offset)){var tooltip=RG.parseTooltipText(this.data[set][i][3],0);var index_adjusted=i;for(var ds=(set-1);ds>=0;--ds){index_adjusted+=this.data[ds].length;}
113
- return{0:this,1:x,2:y,3:set,4:i,5:this.data[set][i][3],'object':this,'x':x,'y':y,'dataset':set,'index':i,'tooltip':tooltip,'index_adjusted':index_adjusted};}}else if(RG.is_null(y)){}else{var mark=this.data[set][i];var width=prop['chart.boxplot.width'];if(typeof(mark[1][7])=='number'){width=mark[1][7];}
114
- if(typeof(x)=='object'&&mouseX>x[0]&&mouseX<x[1]&&mouseY<y[1]&&mouseY>y[3]){var tooltip=RG.parseTooltipText(this.data[set][i][3],0);return{0:this,1:x[0],2:x[1]-x[0],3:y[1],4:y[3]-y[1],5:set,6:i,7:this.data[set][i][3],'object':this,'x':x[0],'y':y[1],'width':x[1]-x[0],'height':y[3]-y[1],'dataset':set,'index':i,'tooltip':tooltip};}}}}};this.drawAboveLabels=this.DrawAboveLabels=function()
115
- {var size=prop['chart.labels.above.size'];var font=prop['chart.text.font'];var units_pre=prop['chart.yaxis.scale.units.pre'];var units_post=prop['chart.yaxis.scale.units.post'];var textConf=RG.getTextConf({object:this,prefix:'chart.labels.above'});for(var set=0,len=this.coords.length;set<len;++set){for(var point=0,len2=this.coords[set].length;point<len2;++point){var x_val=this.data[set][point][0];var y_val=this.data[set][point][1];if(!RG.is_null(y_val)){if(RG.is_array(y_val)){var max=0;for(var i=0;i<y_val;++i){max=Math.max(max,y_val[i]);}
116
- y_val=max;}
117
- var x_pos=this.coords[set][point][0];var y_pos=this.coords[set][point][1];var xvalueFormatter=prop['chart.labels.above.formatter.x'];var yvalueFormatter=prop['chart.labels.above.formatter.y'];RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x_pos,y:y_pos-5-size,text:(typeof xvalueFormatter==='function'?xvalueFormatter(this,x_val):x_val.toFixed(prop['chart.labels.above.decimals']))+', '+
118
- (typeof yvalueFormatter==='function'?yvalueFormatter(this,y_val):y_val.toFixed(prop['chart.labels.above.decimals'])),valign:'center',halign:'center',bounding:true,boundingFill:'rgba(255, 255, 255, 0.7)',boundingStroke:'rgba(0,0,0,0.1)',tag:'labels.above'});}}}};this.getYValue=this.getValue=function(arg)
119
- {if(arg.length==2){var mouseX=arg[0];var mouseY=arg[1];}else{var mouseCoords=RG.getMouseXY(arg);var mouseX=mouseCoords[0];var mouseY=mouseCoords[1];}
120
- var obj=this;if(mouseY<this.marginTop||mouseY>(ca.height-this.marginBottom)||mouseX<this.marginLeft||mouseX>(ca.width-this.marginRight)){return null;}
121
- if(prop['chart.xaxis.position']=='center'){var value=(((this.grapharea/2)-(mouseY-this.marginTop))/this.grapharea)*(this.max-this.min)
122
- value*=2;if(value>=0){value+=this.min
123
- if(prop['chart.yaxis.scale.invert']){value-=this.min;value=this.max-value;}}else{value-=this.min;if(prop['chart.yaxis.scale.invert']){value+=this.min;value=this.max+value;value*=-1;}}}else{var value=((this.grapharea-(mouseY-this.marginTop))/this.grapharea)*(this.max-this.min)
124
- value+=this.min;if(prop['chart.yaxis.scale.invert']){value-=this.min;value=this.max-value;}}
125
- return value;};this.getXValue=function(arg)
126
- {if(arg.length==2){var mouseX=arg[0];var mouseY=arg[1];}else{var mouseXY=RG.getMouseXY(arg);var mouseX=mouseXY[0];var mouseY=mouseXY[1];}
127
- var obj=this;if(mouseY<this.marginTop||mouseY>(ca.height-this.marginBottom)||mouseX<this.marginLeft||mouseX>(ca.width-this.marginRight)){return null;}
128
- var width=(ca.width-this.marginLeft-this.marginRight);var value=((mouseX-this.marginLeft)/width)*(prop['chart.xaxis.scale.max']-prop['chart.xaxis.scale.min'])
129
- value+=prop['chart.xaxis.scale.min'];return value;};this.highlight=this.Highlight=function(shape)
130
- {if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else{if(shape['height']){RG.Highlight.Rect(this,shape);}else{RG.Highlight.Point(this,shape);}}};this.getObjectByXY=function(e)
131
- {var mouseXY=RG.getMouseXY(e);if(mouseXY[0]>(this.marginLeft-3)&&mouseXY[0]<(ca.width-this.marginRight+3)&&mouseXY[1]>(this.marginTop-3)&&mouseXY[1]<((ca.height-this.marginBottom)+3)){return this;}};this.getXCoord=function(value)
132
- {if(typeof value!='number'&&typeof value!='string'){return null;}
133
- if(typeof value==='string'){value=RG.parseDate(value);}
134
- var xmin=prop['chart.xaxis.scale.min'];var xmax=prop['chart.xaxis.scale.max'];var x;if(value<xmin)return null;if(value>xmax)return null;var gutterRight=this.marginRight;var gutterLeft=this.marginLeft;if(prop['chart.yaxis.position']=='right'){x=((value-xmin)/(xmax-xmin))*(ca.width-gutterLeft-gutterRight);x=(ca.width-gutterRight-x);}else{x=((value-xmin)/(xmax-xmin))*(ca.width-gutterLeft-gutterRight);x=x+gutterLeft;}
135
- return x;};this.getYCoord=this.getYCoordFromValue=function(value)
136
- {if(typeof(value)!='number'){return null;}
137
- var invert=prop['chart.yaxis.scale.invert'];var xaxispos=prop['chart.xaxis.position'];var graphHeight=ca.height-this.marginTop-this.marginBottom;var halfGraphHeight=graphHeight/2;var ymax=this.max;var ymin=prop['chart.yaxis.scale.min'];var coord=0;if(value>ymax||(prop['chart.xaxis.position']=='bottom'&&value<ymin)||(prop['chart.xaxis.position']=='center'&&((value>0&&value<ymin)||(value<0&&value>(-1*ymin))))){return null;}
138
- if(xaxispos=='center'){coord=((Math.abs(value)-ymin)/(ymax-ymin))*halfGraphHeight;if(invert){coord=halfGraphHeight-coord;}
139
- if(value<0){coord+=this.marginTop;coord+=halfGraphHeight;}else{coord=halfGraphHeight-coord;coord+=this.marginTop;}}else{coord=((value-ymin)/(ymax-ymin))*graphHeight;if(invert){coord=graphHeight-coord;}
140
- coord=graphHeight-coord;coord=this.marginTop+coord;}
141
- return coord;};RG.Scatter.Bubble=function(scatter,min,max,width,data)
142
- {this.scatter=scatter;this.min=min;this.max=max;this.width=width;this.data=data;this.coords=[];this.type='scatter.bubble'
143
- this.set=this.Set=function(name,value)
144
- {this.scatter.set(name,value);return this;};this.get=this.Get=function(name)
145
- {this.scatter.get(name);};this.draw=this.Draw=function()
146
- {var bubble_min=this.min,bubble_max=this.max,bubble_data=this.data,bubble_max_width=this.width;var obj_bubble=this,obj_scatter=this.scatter;this.scatter.ondraw=function(obj)
147
- {for(var i=0;i<obj.coords[0].length;++i){bubble_data[i]=ma.max(bubble_data[i],bubble_min);bubble_data[i]=ma.min(bubble_data[i],bubble_max);var r=((bubble_data[i]-bubble_min)/(bubble_max-bubble_min))*bubble_max_width,color=obj_scatter.data[0][i][2]?obj_scatter.data[0][i][2]:obj_scatter.properties['chart.colors.default'];co.beginPath();co.fillStyle=RG.radialGradient(obj,obj_scatter.coords[0][i][0]+(r/2.5),obj_scatter.coords[0][i][1]-(r/2.5),0,obj_scatter.coords[0][i][0]+(r/2.5),obj_scatter.coords[0][i][1]-(r/2.5),r,prop['chart.colors.bubble.graduated']?'white':color,color);co.arc(obj_scatter.coords[0][i][0],obj_scatter.coords[0][i][1],r,0,RG.TWOPI,false);co.fill();obj_bubble.coords[i]=[obj_scatter.coords[0][i][0],obj_scatter.coords[0][i][1],r,co.fillStyle];}}
148
- this.scatter.Draw();return this;};};this.parseColors=function()
149
- {if(this.original_colors.length===0){this.original_colors['data']=RG.array_clone(this.data);this.original_colors['chart.background.vbars']=RG.array_clone(prop['chart.background.vbars']);this.original_colors['chart.background.hbars']=RG.array_clone(prop['chart.background.hbars']);this.original_colors['chart.line.colors']=RG.array_clone(prop['chart.line.colors']);this.original_colors['chart.colors.default']=RG.array_clone(prop['chart.colors.default']);this.original_colors['chart.crosshairs.color']=RG.array_clone(prop['chart.crosshairs.color']);this.original_colors['chart.highlight.stroke']=RG.array_clone(prop['chart.highlight.stroke']);this.original_colors['chart.highlight.fill']=RG.array_clone(prop['chart.highlight.fill']);this.original_colors['chart.background.bars.color1']=RG.array_clone(prop['chart.background.bars.color1']);this.original_colors['chart.background.bars.color2']=RG.array_clone(prop['chart.background.bars.color2']);this.original_colors['chart.background.grid.color']=RG.array_clone(prop['chart.background.grid.color']);this.original_colors['chart.background.color']=RG.array_clone(prop['chart.background.color']);this.original_colors['chart.axes.color']=RG.array_clone(prop['chart.axes.color']);}
150
- var data=this.data;if(data){for(var dataset=0;dataset<data.length;++dataset){for(var i=0;i<this.data[dataset].length;++i){if(this.data[dataset][i]&&typeof(this.data[dataset][i][1])=='object'&&this.data[dataset][i][1]){if(typeof(this.data[dataset][i][1][5])=='string')this.data[dataset][i][1][5]=this.parseSingleColorForGradient(this.data[dataset][i][1][5]);if(typeof(this.data[dataset][i][1][6])=='string')this.data[dataset][i][1][6]=this.parseSingleColorForGradient(this.data[dataset][i][1][6]);}
151
- if(!RG.isNull(this.data[dataset][i])){this.data[dataset][i][2]=this.parseSingleColorForGradient(this.data[dataset][i][2]);}}}}
152
- var hbars=prop['chart.background.hbars'];if(hbars){for(i=0;i<hbars.length;++i){hbars[i][2]=this.parseSingleColorForGradient(hbars[i][2]);}}
153
- var vbars=prop['chart.background.vbars'];if(vbars){for(i=0;i<vbars.length;++i){vbars[i][2]=this.parseSingleColorForGradient(vbars[i][2]);}}
154
- var colors=prop['chart.line.colors'];if(colors){for(i=0;i<colors.length;++i){colors[i]=this.parseSingleColorForGradient(colors[i]);}}
155
- prop['chart.colors.default']=this.parseSingleColorForGradient(prop['chart.colors.default']);prop['chart.crosshairs.color']=this.parseSingleColorForGradient(prop['chart.crosshairs.color']);prop['chart.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.highlight.stroke']);prop['chart.highlight.fill']=this.parseSingleColorForGradient(prop['chart.highlight.fill']);prop['chart.background.bars.color1']=this.parseSingleColorForGradient(prop['chart.background.bars.color1']);prop['chart.background.bars.color2']=this.parseSingleColorForGradient(prop['chart.background.bars.color2']);prop['chart.background.grid.color']=this.parseSingleColorForGradient(prop['chart.background.grid.color']);prop['chart.background.color']=this.parseSingleColorForGradient(prop['chart.background.color']);prop['chart.axes.color']=this.parseSingleColorForGradient(prop['chart.axes.color']);};this.reset=function()
156
- {};this.parseSingleColorForGradient=function(color)
157
- {if(!color||typeof(color)!='string'){return color;}
158
- if(color.match(/^gradient\((.*)\)$/i)){if(color.match(/^gradient\(({.*})\)$/i)){return RGraph.parseJSONGradient({object:this,def:RegExp.$1});}
159
- if(color.match(/^gradient\(({.*})\)$/i)){return RGraph.parseJSONGradient({object:this,def:RegExp.$1});}
160
- var parts=RegExp.$1.split(':');var grad=co.createLinearGradient(0,ca.height-prop['chart.margin.bottom'],0,prop['chart.margin.top']);var diff=1/(parts.length-1);grad.addColorStop(0,RG.trim(parts[0]));for(var j=1;j<parts.length;++j){grad.addColorStop(j*diff,RG.trim(parts[j]));}}
161
- return grad?grad:color;};this.interactiveKeyHighlight=function(index)
162
- {if(this.coords&&this.coords[index]&&this.coords[index].length){this.coords[index].forEach(function(value,idx,arr)
163
- {co.beginPath();co.fillStyle=prop['chart.key.interactive.highlight.chart.fill'];co.arc(value[0],value[1],prop['chart.tickmarks.size']+3,0,RG.TWOPI,false);co.fill();});}};this.on=function(type,func)
164
- {if(type.substr(0,2)!=='on'){type='on'+type;}
165
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
166
- return this;};this.firstDrawFunc=function()
167
- {};this.trace=this.trace2=function()
168
- {var obj=this,callback=arguments[2],opt=arguments[0]||{},frames=opt.frames||30,frame=0,callback=arguments[1]||function(){}
169
- obj.Set('animationTrace',true);obj.Set('animationTraceClip',0);function iterator()
170
- {RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(frame++<frames){obj.set('animationTraceClip',frame/frames);RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
171
- iterator();return this;};this.resetColorsToOriginalValues=function()
172
- {for(var i=0,len=this.original_colors['data'].length;i<len;++i){for(var j=0,len2=this.original_colors['data'][i].length;j<len2;++j){this.data[i][j][2]=RG.array_clone(this.original_colors['data'][i][j][2]);if(typeof this.data[i][j][1]==='object'){this.data[i][j][1][5]=RG.array_clone(this.original_colors['data'][i][j][1][5]);this.data[i][j][1][6]=RG.array_clone(this.original_colors['data'][i][j][1][6]);}}}};RG.register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};
12
+ RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
13
+
14
+
15
+
16
+
17
+ //
18
+ // The scatter graph constructor
19
+ //
20
+ RGraph.Scatter = function (conf)
21
+ {
22
+ this.data = new Array(conf.data.length);
23
+
24
+ // Store the data set(s)
25
+ this.data = RGraph.arrayClone(conf.data);
26
+
27
+
28
+ // Convert objects to arrays
29
+ for (let i=0; i<this.data.length; ++i) {
30
+
31
+ // Single dataset
32
+ if ( RGraph.isObject(conf.data[i])
33
+ && (RGraph.isNumber(this.data[i].x) || RGraph.isString(this.data[i].x))
34
+ && (RGraph.isNumber(this.data[i].y) || RGraph.isString(this.data[i].y))) {
35
+
36
+ conf.data[i] = [
37
+ conf.data[i].x,
38
+ conf.data[i].y,
39
+ conf.data[i].color || undefined,
40
+ typeof conf.data[i].tooltip === 'string' ? conf.data[i].tooltip : undefined
41
+ ];
42
+
43
+ // Multiple datasets
44
+ } else if (RGraph.isArray(conf.data[i])) {
45
+ for (let j=0; j<conf.data[i].length; ++j) {
46
+ if (RGraph.isObject(conf.data[i][j])) {
47
+ conf.data[i][j] = [
48
+ conf.data[i][j].x,
49
+ conf.data[i][j].y,
50
+ conf.data[i][j].color || undefined,
51
+ typeof conf.data[i][j].tooltip === 'string' ? conf.data[i][j].tooltip : undefined
52
+ ];
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+
59
+
60
+ // Account for just one dataset being given
61
+ if (typeof conf.data === 'object' && typeof conf.data[0] === 'object' && (typeof conf.data[0][0] === 'number' || typeof conf.data[0][0] === 'string')) {
62
+ var tmp = RGraph.arrayClone(conf.data);
63
+ conf.data = new Array();
64
+ conf.data[0] = RGraph.arrayClone(tmp);
65
+ }
66
+
67
+ this.data = RGraph.arrayClone(conf.data);
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+ // If necessary convert X/Y values passed as strings
77
+ // to numbers
78
+ for (var i=0,len=this.data.length; i<len; ++i) { // Datasets
79
+ for (var j=0,len2=this.data[i].length; j<len2; ++j) { // Points
80
+
81
+ // Handle the conversion of X values
82
+ if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][0] === 'string') {
83
+ if (this.data[i][j][0].match(/^[.0-9]+$/)) {
84
+ this.data[i][j][0] = parseFloat(this.data[i][j][0]);
85
+ } else if (this.data[i][j][0] === '') {
86
+ this.data[i][j][0] = 0;
87
+ }
88
+ }
89
+
90
+ // Handle the conversion of Y values
91
+ if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][1] === 'string') {
92
+ if (this.data[i][j][1].match(/[.0-9]+/)) {
93
+ this.data[i][j][1] = parseFloat(this.data[i][j][1]);
94
+ } else if (this.data[i][j][1] === '') {
95
+ this.data[i][j][1] = 0;
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ // Store a copy of the data that won't be touched
102
+ this.unmodified_data = RGraph.arrayClone(this.data);
103
+
104
+ this.id = conf.id;
105
+ this.canvas = document.getElementById(this.id);
106
+ this.canvas.__object__ = this;
107
+ this.context = this.canvas.getContext ? this.canvas.getContext('2d') : null;
108
+ this.max = 0;
109
+ this.coords = [];
110
+ this.type = 'scatter';
111
+ this.isRGraph = true;
112
+ this.isrgraph = true;
113
+ this.rgraph = true;
114
+ this.uid = RGraph.createUID();
115
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID();
116
+ this.colorsParsed = false;
117
+ this.coordsText = [];
118
+ this.coordsBubble = [];
119
+ this.coords_trendline = [];
120
+ this.original_colors = [];
121
+ this.firstDraw = true; // After the first draw this will be false
122
+ this.stopAnimationRequested = false;// Used to control the animations
123
+
124
+
125
+
126
+
127
+ // Various config properties
128
+ this.properties = {
129
+ backgroundBarsCount: null,
130
+ backgroundBarsColor1: 'rgba(0,0,0,0)',
131
+ backgroundBarsColor2: 'rgba(0,0,0,0)',
132
+ backgroundHbars: null,
133
+ backgroundVbars: null,
134
+ backgroundGrid: true,
135
+ backgroundGridLinewidth: 1,
136
+ backgroundGridColor: '#ddd',
137
+ backgroundGridHsize: 20,
138
+ backgroundGridVsize: 20,
139
+ backgroundGridVlines: true,
140
+ backgroundGridHlines: true,
141
+ backgroundGridBorder: true,
142
+ backgroundGridAutofit: true,
143
+ backgroundGridAutofitAlign: true,
144
+ backgroundGridHlinesCount: 5,
145
+ backgroundGridVlinesCount: 20,
146
+ backgroundImage: null,
147
+ backgroundImageStretch: true,
148
+ backgroundImageX: null,
149
+ backgroundImageY: null,
150
+ backgroundImageW: null,
151
+ backgroundImageH: null,
152
+ backgroundImageAlign: null,
153
+ backgroundColor: null,
154
+
155
+ colors: [], // This is used internally for the tooltip key
156
+ colorsBubbleGraduated: true,
157
+ colorsBubbleStroke: null,
158
+
159
+ textColor: 'black',
160
+ textFont: 'Arial, Verdana, sans-serif',
161
+ textSize: 12,
162
+ textBold: false,
163
+ textItalic: false,
164
+ textAccessible: false,
165
+ textAccessibleOverflow: 'visible',
166
+ textAccessiblePointerevents:false,
167
+ text: null,
168
+
169
+ tooltips: [], // Default must be an empty array
170
+ tooltipsEffect: 'slide',
171
+ tooltipsEvent: 'onmousemove',
172
+ tooltipsHotspot: 3,
173
+ tooltipsCssClass: 'RGraph_tooltip',
174
+ tooltipsCss: null,
175
+ tooltipsHighlight: true,
176
+ tooltipsCoordsPage: false,
177
+ tooltipsFormattedThousand: ',',
178
+ tooltipsFormattedPoint: '.',
179
+ tooltipsFormattedDecimals: 0,
180
+ tooltipsFormattedUnitsPre: '',
181
+ tooltipsFormattedUnitsPost: '',
182
+ tooltipsFormattedKeyColors: null,
183
+ tooltipsFormattedKeyColorsShape: 'square',
184
+ tooltipsFormattedKeyLabels: [],
185
+ tooltipsFormattedListType: 'ul',
186
+ tooltipsFormattedListItems: null,
187
+ tooltipsFormattedTableHeaders: null,
188
+ tooltipsFormattedTableData: null,
189
+ tooltipsPointer: true,
190
+ tooltipsPointerOffsetx: 0,
191
+ tooltipsPointerOffsety: 0,
192
+ tooltipsPositionStatic: true,
193
+ tooltipsHotspotIgnore: null,
194
+
195
+
196
+ xaxis: true,
197
+ xaxisLinewidth: 1,
198
+ xaxisColor: 'black',
199
+ xaxisTickmarks: true,
200
+ xaxisTickmarksLength: 3,
201
+ xaxisTickmarksLastLeft: null,
202
+ xaxisTickmarksLastRight: null,
203
+ xaxisTickmarksCount: null,
204
+ xaxisLabels: null,
205
+ xaxisLabelsFormattedDecimals: 0,
206
+ xaxisLabelsFormattedPoint: '.',
207
+ xaxisLabelsFormattedThousand: ',',
208
+ xaxisLabelsFormattedUnitsPre: '',
209
+ xaxisLabelsFormattedUnitsPost: '',
210
+ xaxisLabelsCount: null,
211
+ xaxisLabelsSize: null,
212
+ xaxisLabelsFont: null,
213
+ xaxisLabelsItalic: null,
214
+ xaxisLabelsBold: null,
215
+ xaxisLabelsColor: null,
216
+ xaxisLabelsOffsetx: 0,
217
+ xaxisLabelsOffsety: 0,
218
+ xaxisLabelsHalign: null,
219
+ xaxisLabelsValign: null,
220
+ xaxisLabelsPosition: 'section',
221
+ xaxisLabelsSpecificAlign: 'left',
222
+ xaxisPosition: 'bottom',
223
+ xaxisLabelsAngle: 0,
224
+ xaxisTitle: '',
225
+ xaxisTitleBold: null,
226
+ xaxisTitleSize: null,
227
+ xaxisTitleFont: null,
228
+ xaxisTitleColor: null,
229
+ xaxisTitleItalic: null,
230
+ xaxisTitlePos: null,
231
+ xaxisTitleOffsetx: 0,
232
+ xaxisTitleOffsety: 0,
233
+ xaxisTitleX: null,
234
+ xaxisTitleY: null,
235
+ xaxisTitleHalign: 'center',
236
+ xaxisTitleValign: 'top',
237
+ xaxisScale: false,
238
+ xaxisScaleMin: 0,
239
+ xaxisScaleMax: null,
240
+ xaxisScaleUnitsPre: '',
241
+ xaxisScaleUnitsPost: '',
242
+ xaxisScaleLabelsCount: 10,
243
+ xaxisScaleFormatter: null,
244
+ xaxisScaleDecimals: 0,
245
+ xaxisScaleThousand: ',',
246
+ xaxisScalePoint: '.',
247
+
248
+ yaxis: true,
249
+ yaxisLinewidth: 1,
250
+ yaxisColor: 'black',
251
+ yaxisTickmarks: true,
252
+ yaxisTickmarksCount: null,
253
+ yaxisTickmarksLastTop: null,
254
+ yaxisTickmarksLastBottom: null,
255
+ yaxisTickmarksLength: 3,
256
+ yaxisScale: true,
257
+ yaxisScaleMin: 0,
258
+ yaxisScaleMax: null,
259
+ yaxisScaleUnitsPre: '',
260
+ yaxisScaleUnitsPost: '',
261
+ yaxisScaleDecimals: 0,
262
+ yaxisScalePoint: '.',
263
+ yaxisScaleThousand: ',',
264
+ yaxisScaleRound: false,
265
+ yaxisScaleInvert: false,
266
+ yaxisScaleFormatter: null,
267
+ yaxisLabelsSpecific: null,
268
+ yaxisLabelsCount: 5,
269
+ yaxisLabelsOffsetx: 0,
270
+ yaxisLabelsOffsety: 0,
271
+ yaxisLabelsHalign: null,
272
+ yaxisLabelsValign: null,
273
+ yaxisLabelsFont: null,
274
+ yaxisLabelsSize: null,
275
+ yaxisLabelsColor: null,
276
+ yaxisLabelsBold: null,
277
+ yaxisLabelsItalic: null,
278
+ yaxisLabelsPosition: 'edge',
279
+ yaxisPosition: 'left',
280
+ yaxisTitle: '',
281
+ yaxisTitleBold: null,
282
+ yaxisTitleSize: null,
283
+ yaxisTitleFont: null,
284
+ yaxisTitleColor: null,
285
+ yaxisTitleItalic: null,
286
+ yaxisTitlePos: null,
287
+ yaxisTitleX: null,
288
+ yaxisTitleY: null,
289
+ yaxisTitleOffsetx: 0,
290
+ yaxisTitleOffsety: 0,
291
+ yaxisTitleHalign: null,
292
+ yaxisTitleValign: null,
293
+ yaxisTitleAccessible: null,
294
+
295
+ tickmarksStyle: 'cross',
296
+ tickmarksStyleImageHalign: 'center',
297
+ tickmarksStyleImageValign: 'center',
298
+ tickmarksStyleImageOffsetx: 0,
299
+ tickmarksStyleImageOffsety: 0,
300
+ tickmarksSize: 5,
301
+
302
+ marginLeft: 35,
303
+ marginRight: 35,
304
+ marginTop: 35,
305
+ marginBottom: 35,
306
+
307
+ title: '',
308
+ titleBold: null,
309
+ titleItalic: null,
310
+ titleFont: null,
311
+ titleSize: null,
312
+ titleItalic: null,
313
+ titleX: null,
314
+ titleY: null,
315
+ titleHalign: null,
316
+ titleValign: null,
317
+ titleOffsetx: 0,
318
+ titleOffsety: 0,
319
+ titleSubtitle: '',
320
+ titleSubtitleSize: null,
321
+ titleSubtitleColor: '#aaa',
322
+ titleSubtitleFont: null,
323
+ titleSubtitleBold: null,
324
+ titleSubtitleItalic: null,
325
+ titleSubtitleOffsetx: 0,
326
+ titleSubtitleOffsety: 0,
327
+
328
+ labelsIngraph: null,
329
+ labelsIngraphFont: null,
330
+ labelsIngraphSize: null,
331
+ labelsIngraphColor: null,
332
+ labelsIngraphBold: null,
333
+ labelsIngraphItalic: null,
334
+ labelsIngraphOffsetx: 0,
335
+ labelsIngraphOffsety: 0,
336
+ labelsAbove: false,
337
+ labelsAboveSize: null,
338
+ labelsAboveFont: null,
339
+ labelsAboveColor: null,
340
+ labelsAboveBold: null,
341
+ labelsAboveItalic: null,
342
+ labelsAboveDecimals: 0,
343
+ labelsAboveOffsetx: 0,
344
+ labelsAboveOffsety: 0,
345
+
346
+ contextmenu: null,
347
+
348
+ colorsDefault: 'black',
349
+
350
+ crosshairs: false,
351
+ crosshairsHline: true,
352
+ crosshairsVline: true,
353
+ crosshairsColor: '#333',
354
+ crosshairsLinewidth: 1,
355
+ crosshairsCoords: false,
356
+ crosshairsCoordsFixed: true,
357
+ crosshairsCoordsLabelsX: 'X',
358
+ crosshairsCoordsLabelsY: 'Y',
359
+ crosshairsCoordsFormatterX: null,
360
+ crosshairsCoordsFormatterY: null,
361
+
362
+ annotatable: false,
363
+ annotatableColor: 'black',
364
+ annotatableLinewidth: 1,
365
+
366
+ lasso: false,
367
+ lassoFill: '#0064',
368
+ lassoStroke: '#006',
369
+ lassoLinewidth: 1,
370
+ lassoHighlightLinewidth: 1,
371
+ lassoHighlightStroke: 'transparent',
372
+ lassoHighlightFill: 'red',
373
+ lassoCallback: null,
374
+ lassoClearCallback: null,
375
+ lassoPersist: false,
376
+ lassoPersistLocal: true,
377
+ lassoPersistLoad: null,
378
+ lassoPersistSave: null,
379
+
380
+ line: false,
381
+ lineLinewidth: 1,
382
+ lineColors: ['green', 'red','blue','orange','pink','brown','black','gray'],
383
+ lineShadowColor: 'rgba(0,0,0,0)',
384
+ lineShadowBlur: 2,
385
+ lineShadowOffsetx: 3,
386
+ lineShadowOffsety: 3,
387
+ lineStepped: false,
388
+ lineVisible: true,
389
+
390
+ key: null,
391
+ keyBackground: 'white',
392
+ keyPosition: 'graph',
393
+ keyHalign: 'right',
394
+ keyShadow: false,
395
+ keyShadowColor: '#666',
396
+ keyShadowBlur: 3,
397
+ keyShadowOffsetx: 2,
398
+ keyShadowOffsety: 2,
399
+ keyPositionGutterBoxed: false,
400
+ keyPositionX: null,
401
+ keyPositionY: null,
402
+ keyInteractive: false,
403
+ keyInteractiveHighlightChartFill: 'rgba(255,0,0,0.9)',
404
+ keyInteractiveHighlightLabel: 'rgba(255,0,0,0.2)',
405
+ keyColorShape: 'square',
406
+ keyRounded: true,
407
+ keyLinewidth: 1,
408
+ keyColors: null,
409
+ keyLabelsColor: null,
410
+ keyLabelsFont: null,
411
+ keyLabelsSize: null,
412
+ keyLabelsBold: null,
413
+ keyLabelsItalic: null,
414
+ keyLabelsOffsetx: 0,
415
+ keyLabelsOffsety: 0,
416
+ keyFormattedDecimals: 0,
417
+ keyFormattedPoint: '.',
418
+ keyFormattedThousand: ',',
419
+ keyFormattedUnitsPre: '',
420
+ keyFormattedUnitsPost: '',
421
+ keyFormattedValueSpecific: null,
422
+ keyFormattedItemsCount: null,
423
+
424
+ boxplotWidth: 10,
425
+ boxplotCapped: true,
426
+
427
+ trendline: false,
428
+ trendlineColors: ['gray'],
429
+ trendlineLinewidth: 1,
430
+ trendlineMargin: 15,
431
+ trendlineDashed: true,
432
+ trendlineDotted: false,
433
+ trendlineDashArray: null,
434
+ trendlineClipping: null,
435
+
436
+ highlightStroke: 'rgba(0,0,0,0)',
437
+ highlightFill: 'rgba(255,255,255,0.7)',
438
+
439
+ bubbleMin: 0,
440
+ bubbleMax: null,
441
+ bubbleWidth: null,
442
+ bubbleData: null,
443
+ bubbleLinewidth: 1,
444
+ bubbleShadow: false,
445
+ bubbleShadowColor: '#aaa',
446
+ bubbleShadowOffsetx: 2,
447
+ bubbleShadowOffsety: 2,
448
+ bubbleShadowBlur: 3,
449
+
450
+ marimekkoLinewidth: 10,
451
+ marimekkoColors: ['#faa', '#afa', '#aaf', '#ffa', '#faf', '#aff'],
452
+ marimekkoColorsSequential: false,
453
+ marimekkoColorsStroke: 'white',
454
+ marimekkoLabels: null,
455
+ marimekkoLabelsColor: null,
456
+ marimekkoLabelsSize: null,
457
+ marimekkoLabelsFont: null,
458
+ marimekkoLabelsBold: null,
459
+ marimekkoLabelsItalic: null,
460
+ marimekkoLabelsOffsetx: 0,
461
+ marimekkoLabelsOffsety: 0,
462
+ marimekkoLabelsFormattedDecimals: 0,
463
+ marimekkoLabelsFormattedPoint: '.',
464
+ marimekkoLabelsFormattedThousand: ',',
465
+ marimekkoLabelsFormattedUnitsPre: '',
466
+ marimekkoLabelsFormattedUnitsPost:'',
467
+ marimekkoLabelsIngraph: false,
468
+ marimekkoLabelsIngraphColor: null,
469
+ marimekkoLabelsIngraphSize: 10,
470
+ marimekkoLabelsIngraphFont: null,
471
+ marimekkoLabelsIngraphBold: null,
472
+ marimekkoLabelsIngraphItalic: null,
473
+ marimekkoLabelsIngraphUnitsPre: '',
474
+ marimekkoLabelsIngraphUnitsPost: '',
475
+ marimekkoLabelsIngraphPoint: '.',
476
+ marimekkoLabelsIngraphThousand: ',',
477
+ marimekkoLabelsIngraphDecimals: 0,
478
+ marimekkoLabelsIngraphOffsetx: 0,
479
+ marimekkoLabelsIngraphOffsety: 0,
480
+ marimekkoLabelsIngraphBackgroundFill: '#fffa',
481
+ marimekkoLabelsIngraphBackgroundStroke: 'transparent',
482
+ marimekkoLabelsIngraphSpecific: null,
483
+
484
+ adjustable: false,
485
+ adjustableOnly: null,
486
+
487
+ clearto: 'rgba(0,0,0,0)',
488
+
489
+ outofbounds: false,
490
+
491
+ animationTrace: false,
492
+ animationTraceClip: 1,
493
+
494
+ horizontalLines: null
495
+ }
496
+
497
+ //
498
+ // This allows the data points to be given as dates as well as numbers. Formats supported by RGraph.parseDate() are accepted.
499
+ //
500
+ // ALSO: unrelated but this loop is also used to convert null values to an
501
+ // empty array
502
+ //
503
+ for (var i=0; i<this.data.length; ++i) {
504
+ for (var j=0; j<this.data[i].length; ++j) {
505
+
506
+ // Convert null data points to an empty erray
507
+ if ( RGraph.isNull(this.data[i][j]) ) {
508
+ this.data[i][j] = [];
509
+ }
510
+
511
+ // Allow for the X point to be dates
512
+ if (this.data[i][j] && typeof this.data[i][j][0] == 'string') {
513
+ this.data[i][j][0] = RGraph.parseDate(this.data[i][j][0]);
514
+ }
515
+ }
516
+ }
517
+
518
+
519
+ //
520
+ // Now make the data_arr array - all the data as one big
521
+ // array
522
+ //
523
+ this.data_arr = [];
524
+
525
+ for (var i=0; i<this.data.length; ++i) {
526
+ for (var j=0; j<this.data[i].length; ++j) {
527
+ this.data_arr.push(this.data[i][j]);
528
+ }
529
+ }
530
+
531
+ // Create the $ objects so that they can be used
532
+ for (var i=0; i<this.data_arr.length; ++i) {
533
+ this['$' + i] = {}
534
+ }
535
+
536
+
537
+ // Check for support
538
+ if (!this.canvas) {
539
+ alert('[SCATTER] No canvas support');
540
+ return;
541
+ }
542
+
543
+
544
+
545
+ // Easy access to properties and the path function
546
+ var properties = this.properties;
547
+ this.path = RGraph.pathObjectFunction;
548
+
549
+
550
+ //
551
+ // "Decorate" the object with the generic effects if the effects library has been included
552
+ //
553
+ if (RGraph.Effects && typeof RGraph.Effects.decorate === 'function') {
554
+ RGraph.Effects.decorate(this);
555
+ }
556
+
557
+
558
+
559
+ // Add the responsive method. This method resides in the common file.
560
+ this.responsive = RGraph.responsive;
561
+
562
+
563
+
564
+
565
+
566
+
567
+
568
+
569
+ //
570
+ // A simple setter
571
+ //
572
+ // @param string name The name of the property to set
573
+ // @param string value The value of the property
574
+ //
575
+ this.set = function (name)
576
+ {
577
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
578
+
579
+ // the number of arguments is only one and it's an
580
+ // object - parse it for configuration data and return.
581
+ if (arguments.length === 1 && typeof arguments[0] === 'object') {
582
+ for (i in arguments[0]) {
583
+ if (typeof i === 'string') {
584
+ this.set(i, arguments[0][i]);
585
+ }
586
+ }
587
+
588
+ return this;
589
+ }
590
+
591
+ properties[name] = value;
592
+
593
+
594
+
595
+ // If a single tooltip has been given add it to each datapiece
596
+ if (name === 'tooltips' && typeof value === 'string') {
597
+ this.populateTooltips();
598
+ }
599
+
600
+ return this;
601
+ };
602
+
603
+
604
+
605
+
606
+
607
+
608
+
609
+
610
+ //
611
+ // A simple getter
612
+ //
613
+ // @param string name The name of the property to set
614
+ //
615
+ this.get = function (name)
616
+ {
617
+ return properties[name];
618
+ };
619
+
620
+
621
+
622
+
623
+
624
+
625
+
626
+
627
+ //
628
+ // The function you call to draw the Scatter chart
629
+ //
630
+ this.draw = function ()
631
+ {
632
+ // MUST be the first thing done!
633
+ if (typeof properties.backgroundImage === 'string') {
634
+ RGraph.drawBackgroundImage(this);
635
+ }
636
+
637
+
638
+ //
639
+ // Fire the onbeforedraw event
640
+ //
641
+ RGraph.fireCustomEvent(this, 'onbeforedraw');
642
+
643
+
644
+
645
+ // Translate half a pixel for antialiasing purposes - but only if it hasn't been
646
+ // done already
647
+ //
648
+ // MUST be the first thing done!
649
+ //
650
+ if (!this.canvas.__rgraph_aa_translated__) {
651
+ this.context.translate(0.5,0.5);
652
+
653
+ this.canvas.__rgraph_aa_translated__ = true;
654
+ }
655
+
656
+ //
657
+ // Parse the colors. This allows for simple gradient syntax
658
+ //
659
+ if (!this.colorsParsed) {
660
+ this.parseColors();
661
+
662
+ // Don't want to do this again
663
+ this.colorsParsed = true;
664
+ }
665
+
666
+
667
+
668
+
669
+ //
670
+ // Stop this growing uncontrollably
671
+ //
672
+ this.coordsText = [];
673
+
674
+
675
+ //
676
+ // Populate the labels string/array if its a string
677
+ //
678
+ if (properties.xaxisLabels && properties.xaxisLabels.length) {
679
+ //
680
+ // If the labels option is a string then turn it
681
+ // into an array.
682
+ //
683
+ if (typeof properties.xaxisLabels === 'string') {
684
+ properties.xaxisLabels = RGraph.arrayPad({
685
+ array: [],
686
+ length: properties.xaxisLabelsCount,
687
+ value: properties.xaxisLabels
688
+ });
689
+ }
690
+
691
+ for (var i=0; i<properties.xaxisLabels.length; ++i) {
692
+
693
+ if (typeof properties.xaxisLabels[i] === 'object' && properties.xaxisLabels[i].length === 2) {
694
+ var label = properties.xaxisLabels[i][0];
695
+ } else {
696
+ var label = properties.xaxisLabels[i];
697
+ }
698
+
699
+ label = RGraph.labelSubstitution({
700
+ object: this,
701
+ text: label,
702
+ index: i,
703
+ value: this.data[0][i],
704
+ decimals: properties.xaxisLabelsFormattedDecimals || 0,
705
+ unitsPre: properties.xaxisLabelsFormattedUnitsPre || '',
706
+ unitsPost: properties.xaxisLabelsFormattedUnitsPost || '',
707
+ thousand: properties.xaxisLabelsFormattedThousand || ',',
708
+ point: properties.xaxisLabelsFormattedPoint || '.'
709
+ });
710
+
711
+ if (typeof properties.xaxisLabels[i] === 'object' && properties.xaxisLabels[i].length === 2) {
712
+ properties.xaxisLabels[i][0] = label;
713
+ } else {
714
+ properties.xaxisLabels[i] = label;
715
+ }
716
+ }
717
+ }
718
+
719
+
720
+ //
721
+ // Make the margins easy ro access
722
+ //
723
+ this.marginLeft = properties.marginLeft;
724
+ this.marginRight = properties.marginRight;
725
+ this.marginTop = properties.marginTop;
726
+ this.marginBottom = properties.marginBottom;
727
+
728
+ // Go through all the data points and see if a tooltip has been given
729
+ this.hasTooltips = false;
730
+ var overHotspot = false;
731
+
732
+ // Reset the coords array
733
+ this.coords = [];
734
+
735
+ //
736
+ // This facilitates the xmax, xmin and X values being dates
737
+ //
738
+ if (typeof properties.xaxisScaleMin == 'string') properties.xaxisScaleMin = RGraph.parseDate(properties.xaxisScaleMin);
739
+ if (typeof properties.xaxisScaleMax == 'string') properties.xaxisScaleMax = RGraph.parseDate(properties.xaxisScaleMax);
740
+
741
+ //
742
+ // Look for tooltips and populate the tooltips
743
+ //
744
+ // NB 26/01/2011 Updated so that the tooltips property is ALWAYS populated
745
+ //
746
+ //if (!RGraph.ISOLD) {
747
+ this.set('tooltips', []);
748
+ for (var i=0,len=this.data.length; i<len; i+=1) {
749
+ for (var j =0,len2=this.data[i].length;j<len2; j+=1) {
750
+
751
+ if (this.data[i][j] && this.data[i][j][3]) {
752
+ properties.tooltips.push(this.data[i][j][3]);
753
+ this.hasTooltips = true;
754
+ } else {
755
+ properties.tooltips.push(null);
756
+ }
757
+ }
758
+ }
759
+ //}
760
+
761
+ // Reset the maximum value
762
+ this.max = 0;
763
+
764
+ // Work out the maximum Y value
765
+ //if (properties.ymax && properties.ymax > 0) {
766
+ if (typeof properties.yaxisScaleMax === 'number') {
767
+
768
+ this.max = properties.yaxisScaleMax;
769
+ this.min = properties.yaxisScaleMin ? properties.yaxisScaleMin : 0;
770
+
771
+
772
+ this.scale2 = RGraph.getScale({object: this, options: {
773
+ 'scale.max': this.max,
774
+ 'scale.min': this.min,
775
+ 'scale.strict': true,
776
+ 'scale.thousand': properties.yaxisScaleThousand,
777
+ 'scale.point': properties.yaxisScalePoint,
778
+ 'scale.decimals': properties.yaxisScaleDecimals,
779
+ 'scale.labels.count': properties.yaxisLabelsCount,
780
+ 'scale.round': properties.yaxisScaleRound,
781
+ 'scale.units.pre': properties.yaxisScaleUnitsPre,
782
+ 'scale.units.post': properties.yaxisScaleUnitsPost
783
+ }});
784
+
785
+ this.max = this.scale2.max;
786
+ this.min = this.scale2.min;
787
+ var decimals = properties.yaxisScaleDecimals;
788
+
789
+ } else {
790
+
791
+ var i = 0;
792
+ var j = 0;
793
+
794
+ for (i=0,len=this.data.length; i<len; i+=1) {
795
+ for (j=0,len2=this.data[i].length; j<len2; j+=1) {
796
+ if (!RGraph.isNull(this.data[i][j]) && this.data[i][j][1] != null) {
797
+ this.max = Math.max(this.max, typeof this.data[i][j][1] == 'object' ? RGraph.arrayMax(this.data[i][j][1]) : Math.abs(this.data[i][j][1]));
798
+ }
799
+ }
800
+ }
801
+
802
+ this.min = properties.yaxisScaleMin ? properties.yaxisScaleMin : 0;
803
+
804
+ this.scale2 = RGraph.getScale({object: this, options: {
805
+ 'scale.max': this.max,
806
+ 'scale.min': this.min,
807
+ 'scale.thousand': properties.yaxisScaleThousand,
808
+ 'scale.point': properties.yaxisScalePoint,
809
+ 'scale.decimals': properties.yaxisScaleDecimals,
810
+ 'scale.labels.count': properties.yaxisLabelsCount,
811
+ 'scale.round': properties.yaxisScaleRound,
812
+ 'scale.units.pre': properties.yaxisScaleUnitsPre,
813
+ 'scale.units.post': properties.yaxisScaleUnitsPost
814
+ }});
815
+
816
+ this.max = this.scale2.max;
817
+ this.min = this.scale2.min;
818
+ }
819
+
820
+ this.grapharea = this.canvas.height - this.marginTop - this.marginBottom;
821
+
822
+
823
+
824
+ // Progressively Draw the chart
825
+ RGraph.Background.draw(this);
826
+
827
+ //
828
+ // Draw any horizontal bars that have been specified
829
+ //
830
+ if (properties.backgroundHbars && properties.backgroundHbars.length) {
831
+ RGraph.drawBars(this);
832
+ }
833
+
834
+ //
835
+ // Draw any vertical bars that have been specified
836
+ //
837
+ if (properties.backgroundVbars && properties.backgroundVbars.length) {
838
+ this.drawVBars();
839
+ }
840
+
841
+ //
842
+ // Draw an X scale
843
+ //
844
+ if (!properties.xaxisScaleMax) {
845
+
846
+ var xmax = 0;
847
+ var xmin = properties.xaxisScaleMin;
848
+
849
+ for (var ds=0,len=this.data.length; ds<len; ds+=1) {
850
+ for (var point=0,len2=this.data[ds].length; point<len2; point+=1) {
851
+ xmax = Math.max(xmax, this.data[ds][point][0]);
852
+ }
853
+ }
854
+ } else {
855
+ xmax = properties.xaxisScaleMax;
856
+ xmin = properties.xaxisScaleMin
857
+ }
858
+
859
+ if (properties.xaxisScale) {
860
+ this.xscale2 = RGraph.getScale({object: this, options: {
861
+ 'scale.max': xmax,
862
+ 'scale.min': xmin,
863
+ 'scale.decimals': properties.xaxisScaleDecimals,
864
+ 'scale.point': properties.xaxisScalePoint,
865
+ 'scale.thousand': properties.xaxisScaleThousand,
866
+ 'scale.units.pre': properties.xaxisScaleUnitsPre,
867
+ 'scale.units.post': properties.xaxisScaleUnitsPost,
868
+ 'scale.labels.count': properties.xaxisLabelsCount,
869
+ 'scale.strict': true
870
+ }});
871
+
872
+ this.set('xaxisScaleMax', this.xscale2.max);
873
+ }
874
+
875
+ this.drawAxes();
876
+
877
+
878
+
879
+
880
+
881
+
882
+
883
+ this.drawLabels();
884
+
885
+ // Clip the canvas so that the trace2 effect is facilitated
886
+ if (properties.animationTrace) {
887
+ this.context.save();
888
+ this.context.beginPath();
889
+ this.context.rect(0, 0, this.canvas.width * properties.animationTraceClip, this.canvas.height);
890
+ this.context.clip();
891
+ }
892
+
893
+ for(i=0; i<this.data.length; ++i) {
894
+
895
+ this.drawMarks(i);
896
+
897
+ // Draw bubbles
898
+ if (RGraph.isArray(properties.bubbleData)) {
899
+ this.drawBubble(i);
900
+ }
901
+
902
+ // Set the shadow
903
+ this.context.shadowColor = properties.lineShadowColor;
904
+ this.context.shadowOffsetX = properties.lineShadowOffsetx;
905
+ this.context.shadowOffsetY = properties.lineShadowOffsety;
906
+ this.context.shadowBlur = properties.lineShadowBlur;
907
+
908
+ this.drawLine(i);
909
+
910
+ // Turn the shadow off
911
+ RGraph.noShadow(this);
912
+ }
913
+
914
+
915
+ if (properties.line) {
916
+ for (var i=0,len=this.data.length;i<len; i+=1) {
917
+ this.drawMarks(i); // Call this again so the tickmarks appear over the line
918
+ }
919
+ }
920
+
921
+ if (properties.animationTrace) {
922
+ this.context.restore();
923
+ }
924
+
925
+
926
+ //
927
+ // Draw a trendline if requested
928
+ //
929
+ if (properties.trendline) {
930
+ for (var i=0; i<this.data.length; ++i) {
931
+ if (properties.trendline === true || (typeof properties.trendline === 'object' && properties.trendline[i] === true) ) {
932
+ this.drawTrendline(i);
933
+ }
934
+ }
935
+ }
936
+
937
+
938
+
939
+ //
940
+ // Setup the context menu if required
941
+ //
942
+ if (properties.contextmenu) {
943
+ RGraph.showContext(this);
944
+ }
945
+
946
+
947
+
948
+ //
949
+ // Draw the key if necessary
950
+ //
951
+
952
+ if (properties.key && properties.key.length) {
953
+ RGraph.drawKey(
954
+ this,
955
+ properties.key,
956
+ this.isMarimekko ? properties.marimekkoColors : properties.lineColors
957
+ );
958
+ }
959
+
960
+
961
+ //
962
+ // Draw " above" labels if enabled
963
+ //
964
+ if (properties.labelsAbove) {
965
+ this.drawAboveLabels();
966
+ }
967
+
968
+ //
969
+ // Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js
970
+ //
971
+ this.drawInGraphLabels(this);
972
+
973
+
974
+
975
+
976
+ //
977
+ // Add custom text thats specified
978
+ //
979
+ RGraph.addCustomText(this);
980
+
981
+
982
+
983
+
984
+
985
+
986
+
987
+ // Draw any custom lines that have been defined
988
+ RGraph.drawHorizontalLines(this);
989
+
990
+
991
+
992
+
993
+
994
+
995
+
996
+ // Draw any custom lines that have been defined
997
+ this.installLasso();
998
+
999
+
1000
+ //
1001
+ // Draw a Marimekko chart
1002
+ //
1003
+ if (this.isMarimekko) {
1004
+ this.drawMarimekko();
1005
+ }
1006
+
1007
+
1008
+
1009
+
1010
+
1011
+
1012
+
1013
+ //
1014
+ // This installs the event listeners
1015
+ //
1016
+ RGraph.installEventListeners(this);
1017
+
1018
+
1019
+ //
1020
+ // Fire the onfirstdraw event
1021
+ //
1022
+ if (this.firstDraw) {
1023
+ this.firstDraw = false;
1024
+ RGraph.fireCustomEvent(this, 'onfirstdraw');
1025
+ this.firstDrawFunc();
1026
+ }
1027
+
1028
+
1029
+
1030
+ //
1031
+ // Fire the RGraph draw event
1032
+ //
1033
+ RGraph.fireCustomEvent(this, 'ondraw');
1034
+
1035
+
1036
+
1037
+
1038
+
1039
+
1040
+
1041
+
1042
+
1043
+ //
1044
+ // Install any inline responsive configuration. This
1045
+ // should be last in the draw function - even after
1046
+ // the draw events.
1047
+ //
1048
+ RGraph.installInlineResponsive(this);
1049
+
1050
+
1051
+
1052
+
1053
+
1054
+
1055
+
1056
+
1057
+ return this;
1058
+ }
1059
+
1060
+
1061
+
1062
+
1063
+
1064
+
1065
+
1066
+
1067
+ //
1068
+ // Draws the axes of the scatter graph
1069
+ //
1070
+ this.drawAxes = function ()
1071
+ {
1072
+ this.context.lineCap = 'square';
1073
+
1074
+ // Draw the X axis
1075
+ RGraph.drawXAxis(this);
1076
+
1077
+ // Draw the Y axis
1078
+ RGraph.drawYAxis(this);
1079
+
1080
+ // Reset the linewidth back to one
1081
+ //
1082
+ this.context.lineWidth = 1;
1083
+ };
1084
+
1085
+
1086
+
1087
+
1088
+
1089
+
1090
+
1091
+
1092
+ //
1093
+ // Draws the labels on the scatter graph
1094
+ //
1095
+ this.drawLabels = function ()
1096
+ {
1097
+ // Nothing to do here because the labels are drawn in the RGraph.drawXAxis() and
1098
+ // RGrapth.drawYAxis functions
1099
+ };
1100
+
1101
+
1102
+
1103
+
1104
+
1105
+
1106
+
1107
+
1108
+ //
1109
+ // Draws the actual scatter graph marks
1110
+ //
1111
+ // @param i integer The dataset index
1112
+ //
1113
+ this.drawMarks = function (i)
1114
+ {
1115
+ //
1116
+ // Reset the coords array
1117
+ //
1118
+ this.coords[i] = [];
1119
+
1120
+ //
1121
+ // Plot the values
1122
+ //
1123
+ var xmax = properties.xaxisScaleMax;
1124
+ var default_color = properties.colorsDefault;
1125
+
1126
+ for (var j=0,len=this.data[i].length; j<len; j+=1) {
1127
+ //
1128
+ // This is here because tooltips are optional
1129
+ //
1130
+ var data_points = this.data[i];
1131
+
1132
+ // Allow for null points
1133
+ if (RGraph.isNull(data_points[j])) {
1134
+ continue;
1135
+ }
1136
+
1137
+ var xCoord = data_points[j][0];
1138
+ var yCoord = data_points[j][1];
1139
+ var color = data_points[j][2] ? data_points[j][2] : default_color;
1140
+ var tooltip = (data_points[j] && data_points[j][3]) ? data_points[j][3] : null;
1141
+
1142
+
1143
+ this.drawMark(
1144
+ i,
1145
+ xCoord,
1146
+ yCoord,
1147
+ xmax,
1148
+ this.scale2.max,
1149
+ color,
1150
+ tooltip,
1151
+ this.coords[i],
1152
+ data_points,
1153
+ j
1154
+ );
1155
+ }
1156
+ };
1157
+
1158
+
1159
+
1160
+
1161
+
1162
+
1163
+
1164
+
1165
+ //
1166
+ // Draws a single scatter mark
1167
+ //
1168
+ this.drawMark = function (data_set_index, x, y, xMax, yMax, color, tooltip, coords, data, data_index)
1169
+ {
1170
+ var tickmarks = properties.tickmarksStyle,
1171
+ tickSize = properties.tickmarksSize,
1172
+ xMin = properties.xaxisScaleMin,
1173
+ x = ((x - xMin) / (xMax - xMin)) * (this.canvas.width - this.marginLeft - this.marginRight),
1174
+ originalX = x,
1175
+ originalY = y;
1176
+
1177
+ //
1178
+ // This allows the tickmarks property to be an array
1179
+ //
1180
+ if (tickmarks && typeof tickmarks == 'object') {
1181
+ tickmarks = tickmarks[data_set_index];
1182
+ }
1183
+
1184
+
1185
+ //
1186
+ // This allows the ticksize property to be an array
1187
+ //
1188
+ if (typeof tickSize == 'object') {
1189
+ var tickSize = tickSize[data_set_index];
1190
+ var halfTickSize = tickSize / 2;
1191
+ } else {
1192
+ var halfTickSize = tickSize / 2;
1193
+ }
1194
+
1195
+
1196
+ //
1197
+ // This bit is for boxplots only
1198
+ //
1199
+ if ( y
1200
+ && typeof y === 'object'
1201
+ && typeof y[0] === 'number'
1202
+ && typeof y[1] === 'number'
1203
+ && typeof y[2] === 'number'
1204
+ && typeof y[3] === 'number'
1205
+ && typeof y[4] === 'number'
1206
+ ) {
1207
+
1208
+ this.set('boxplot', true);
1209
+
1210
+
1211
+ var y0 = this.getYCoord(y[0], properties.outofbounds),
1212
+ y1 = this.getYCoord(y[1], properties.outofbounds),
1213
+ y2 = this.getYCoord(y[2], properties.outofbounds),
1214
+ y3 = this.getYCoord(y[3], properties.outofbounds),
1215
+ y4 = this.getYCoord(y[4], properties.outofbounds),
1216
+ col1 = y[5],
1217
+ col2 = y[6],
1218
+ boxWidth = typeof y[7] == 'number' ? y[7] : properties.boxplotWidth;
1219
+
1220
+
1221
+ } else {
1222
+
1223
+ //
1224
+ // The new way of getting the Y coord. This function (should) handle everything
1225
+ //
1226
+ var yCoord = this.getYCoord(y, properties.outofbounds);
1227
+ }
1228
+
1229
+ //
1230
+ // Account for the X axis being at the centre
1231
+ //
1232
+ // This is so that points are on the graph, and not the gutter - which helps
1233
+ x += this.marginLeft;
1234
+
1235
+
1236
+
1237
+
1238
+ this.context.beginPath();
1239
+
1240
+ // Color
1241
+ this.context.strokeStyle = color;
1242
+
1243
+
1244
+
1245
+ //
1246
+ // Boxplots
1247
+ //
1248
+ if (properties.boxplot) {
1249
+
1250
+ // boxWidth is a scale value, so convert it to a pixel vlue
1251
+ boxWidth = (boxWidth / properties.xaxisScaleMax) * (this.canvas.width - this.marginLeft - this.marginRight);
1252
+
1253
+ var halfBoxWidth = boxWidth / 2;
1254
+
1255
+ if (properties.lineVisible) {
1256
+ this.context.beginPath();
1257
+
1258
+ // Set the outline color of the box
1259
+
1260
+ if (typeof y[8] === 'string') {
1261
+ this.context.strokeStyle = y[8];
1262
+ }
1263
+ this.context.strokeRect(x - halfBoxWidth, y1, boxWidth, y3 - y1);
1264
+
1265
+ // Draw the upper coloured box if a value is specified
1266
+ if (col1) {
1267
+ this.context.fillStyle = col1;
1268
+ this.context.fillRect(x - halfBoxWidth, y1, boxWidth, y2 - y1);
1269
+ }
1270
+
1271
+ // Draw the lower coloured box if a value is specified
1272
+ if (col2) {
1273
+ this.context.fillStyle = col2;
1274
+ this.context.fillRect(x - halfBoxWidth, y2, boxWidth, y3 - y2);
1275
+ }
1276
+ this.context.stroke();
1277
+
1278
+ // Now draw the whiskers
1279
+ this.context.beginPath();
1280
+ if (properties.boxplotCapped) {
1281
+ this.context.moveTo(x - halfBoxWidth, Math.round(y0));
1282
+ this.context.lineTo(x + halfBoxWidth, Math.round(y0));
1283
+ }
1284
+
1285
+ this.context.moveTo(Math.round(x), y0);
1286
+ this.context.lineTo(Math.round(x ), y1);
1287
+
1288
+ if (properties.boxplotCapped) {
1289
+ this.context.moveTo(x - halfBoxWidth, Math.round(y4));
1290
+ this.context.lineTo(x + halfBoxWidth, Math.round(y4));
1291
+ }
1292
+
1293
+ this.context.moveTo(Math.round(x), y4);
1294
+ this.context.lineTo(Math.round(x), y3);
1295
+
1296
+ this.context.stroke();
1297
+ }
1298
+ }
1299
+
1300
+
1301
+ //
1302
+ // Draw the tickmark, but not for boxplots
1303
+ //
1304
+ if (properties.lineVisible && typeof y == 'number' && !y0 && !y1 && !y2 && !y3 && !y4) {
1305
+
1306
+ if (tickmarks == 'circle') {
1307
+ this.context.arc(x, yCoord, halfTickSize, 0, 6.28, 0);
1308
+ this.context.fillStyle = color;
1309
+ this.context.fill();
1310
+
1311
+ } else if (tickmarks == 'plus') {
1312
+
1313
+ this.context.moveTo(x, yCoord - halfTickSize);
1314
+ this.context.lineTo(x, yCoord + halfTickSize);
1315
+ this.context.moveTo(x - halfTickSize, yCoord);
1316
+ this.context.lineTo(x + halfTickSize, yCoord);
1317
+ this.context.stroke();
1318
+
1319
+ } else if (tickmarks == 'square') {
1320
+ this.context.strokeStyle = color;
1321
+ this.context.fillStyle = color;
1322
+ this.context.fillRect(
1323
+ x - halfTickSize,
1324
+ yCoord - halfTickSize,
1325
+ tickSize,
1326
+ tickSize
1327
+ );
1328
+
1329
+ } else if (tickmarks == 'cross') {
1330
+
1331
+ this.context.moveTo(x - halfTickSize, yCoord - halfTickSize);
1332
+ this.context.lineTo(x + halfTickSize, yCoord + halfTickSize);
1333
+ this.context.moveTo(x + halfTickSize, yCoord - halfTickSize);
1334
+ this.context.lineTo(x - halfTickSize, yCoord + halfTickSize);
1335
+
1336
+ this.context.stroke();
1337
+
1338
+ //
1339
+ // Diamond shape tickmarks
1340
+ //
1341
+ } else if (tickmarks == 'diamond') {
1342
+ this.context.fillStyle = this.context.strokeStyle;
1343
+
1344
+ this.context.moveTo(x, yCoord - halfTickSize);
1345
+ this.context.lineTo(x + halfTickSize, yCoord);
1346
+ this.context.lineTo(x, yCoord + halfTickSize);
1347
+ this.context.lineTo(x - halfTickSize, yCoord);
1348
+ this.context.lineTo(x, yCoord - halfTickSize);
1349
+
1350
+ this.context.fill();
1351
+ this.context.stroke();
1352
+
1353
+ //
1354
+ // Custom tickmark style
1355
+ //
1356
+ } else if (typeof tickmarks == 'function') {
1357
+
1358
+ var graphWidth = this.canvas.width - this.marginLeft - this.marginRight,
1359
+ graphheight = this.canvas.height - this.marginTop - this.marginBottom,
1360
+ xVal = ((x - this.marginLeft) / graphWidth) * xMax,
1361
+ yVal = ((graphheight - (yCoord - this.marginTop)) / graphheight) * yMax;
1362
+
1363
+ tickmarks(this, data, x, yCoord, xVal, yVal, xMax, yMax, color, data_set_index, data_index)
1364
+
1365
+
1366
+
1367
+
1368
+
1369
+
1370
+
1371
+
1372
+
1373
+
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+ //
1382
+ // Image based tickmark
1383
+ //
1384
+ // lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index
1385
+ } else if (
1386
+ typeof tickmarks === 'string' &&
1387
+ (
1388
+ tickmarks.substr(0, 6) === 'image:' ||
1389
+ tickmarks.substr(0, 5) === 'data:' ||
1390
+ tickmarks.substr(0, 1) === '/' ||
1391
+ tickmarks.substr(0, 3) === '../' ||
1392
+ tickmarks.substr(0, 7) === 'images/'
1393
+ )
1394
+ ) {
1395
+
1396
+ var img = new Image();
1397
+
1398
+ if (tickmarks.substr(0, 6) === 'image:') {
1399
+ img.src = tickmarks.substr(6);
1400
+ } else {
1401
+ img.src = tickmarks;
1402
+ }
1403
+
1404
+ var obj = this;
1405
+ img.onload = function ()
1406
+ {
1407
+ if (properties.tickmarksStyleImageHalign === 'center') x -= (this.width / 2);
1408
+ if (properties.tickmarksStyleImageHalign === 'right') x -= this.width;
1409
+
1410
+ if (properties.tickmarksStyleImageValign === 'center') yCoord -= (this.height / 2);
1411
+ if (properties.tickmarksStyleImageValign === 'bottom') yCoord -= this.height;
1412
+
1413
+ x += properties.tickmarksStyleImageOffsetx;
1414
+ yCoord += properties.tickmarksStyleImageOffsety;
1415
+
1416
+ obj.context.drawImage(this, x, yCoord);
1417
+ }
1418
+
1419
+
1420
+
1421
+
1422
+
1423
+ //
1424
+ // No tickmarks
1425
+ //
1426
+ } else if (tickmarks === null) {
1427
+
1428
+ //
1429
+ // Unknown tickmark type
1430
+ //
1431
+ } else {
1432
+ alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks );
1433
+ }
1434
+ }
1435
+
1436
+ //
1437
+ // Add the tickmark to the coords array
1438
+ //
1439
+
1440
+ if ( properties.boxplot
1441
+ && typeof y0 === 'number'
1442
+ && typeof y1 === 'number'
1443
+ && typeof y2 === 'number'
1444
+ && typeof y3 === 'number'
1445
+ && typeof y4 === 'number') {
1446
+
1447
+ x = [x - halfBoxWidth, x + halfBoxWidth];
1448
+ yCoord = [y0, y1, y2, y3, y4];
1449
+ }
1450
+
1451
+ coords.push([x, yCoord, tooltip]);
1452
+ };
1453
+
1454
+
1455
+
1456
+
1457
+
1458
+
1459
+
1460
+
1461
+ //
1462
+ // Draws an optional line connecting the tick marks.
1463
+ //
1464
+ // @param i The index of the dataset to use
1465
+ //
1466
+ this.drawLine = function (i)
1467
+ {
1468
+ if (typeof properties.lineVisible == 'boolean' && properties.lineVisible == false) {
1469
+ return;
1470
+ }
1471
+
1472
+ if (properties.line && this.coords[i].length >= 2) {
1473
+
1474
+ if (properties.lineDash && typeof this.context.setLineDash === 'function') {
1475
+ this.context.setLineDash(properties.lineDash);
1476
+ }
1477
+
1478
+ this.context.lineCap = 'round';
1479
+ this.context.lineJoin = 'round';
1480
+ this.context.lineWidth = this.getLineWidth(i);// i is the index of the set of coordinates
1481
+ this.context.strokeStyle = properties.lineColors[i];
1482
+
1483
+ this.context.beginPath();
1484
+
1485
+ var prevY = null;
1486
+ var currY = null;
1487
+
1488
+ for (var j=0,len=this.coords[i].length; j<len; j+=1) {
1489
+
1490
+
1491
+ var xPos = this.coords[i][j][0];
1492
+ var yPos = this.coords[i][j][1];
1493
+
1494
+ if (j > 0) prevY = this.coords[i][j - 1][1];
1495
+ currY = yPos;
1496
+
1497
+ if (j == 0 || RGraph.isNull(prevY) || RGraph.isNull(currY)) {
1498
+ this.context.moveTo(xPos, yPos);
1499
+ } else {
1500
+
1501
+ // Stepped?
1502
+ var stepped = properties.lineStepped;
1503
+
1504
+ if ( (typeof stepped == 'boolean' && stepped)
1505
+ || (typeof stepped == 'object' && stepped[i])
1506
+ ) {
1507
+ this.context.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]);
1508
+ }
1509
+
1510
+ this.context.lineTo(xPos, yPos);
1511
+ }
1512
+ }
1513
+ this.context.stroke();
1514
+
1515
+ //
1516
+ // Set the linedash back to the default
1517
+ //
1518
+ if (properties.lineDash && typeof this.context.setLineDash === 'function') {
1519
+ this.context.setLineDash([1,0]);
1520
+ }
1521
+ }
1522
+
1523
+ //
1524
+ // Set the linewidth back to 1
1525
+ //
1526
+ this.context.lineWidth = 1;
1527
+ };
1528
+
1529
+
1530
+
1531
+
1532
+
1533
+
1534
+
1535
+
1536
+ //
1537
+ // Returns the linewidth
1538
+ //
1539
+ // @param number i The index of the "line" (/set of coordinates)
1540
+ //
1541
+ this.getLineWidth = function (i)
1542
+ {
1543
+ var linewidth = properties.lineLinewidth;
1544
+
1545
+ if (typeof linewidth == 'number') {
1546
+ return linewidth;
1547
+
1548
+ } else if (typeof linewidth == 'object') {
1549
+ if (linewidth[i]) {
1550
+ return linewidth[i];
1551
+ } else {
1552
+ return linewidth[0];
1553
+ }
1554
+
1555
+ alert('[SCATTER] Error! The linewidth property should be a single number or an array of one or more numbers');
1556
+ }
1557
+ };
1558
+
1559
+
1560
+
1561
+
1562
+
1563
+
1564
+
1565
+
1566
+ //
1567
+ // Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function
1568
+ // is not common
1569
+ //
1570
+ this.drawVBars = function ()
1571
+ {
1572
+ var vbars = properties.backgroundVbars;
1573
+ var graphWidth = this.canvas.width - this.marginLeft - this.marginRight;
1574
+
1575
+ if (vbars) {
1576
+
1577
+ var xmax = properties.xaxisScaleMax;
1578
+ var xmin = properties.xaxisScaleMin;
1579
+
1580
+ for (var i=0,len=vbars.length; i<len; i+=1) {
1581
+
1582
+ var key = i;
1583
+ var value = vbars[key];
1584
+
1585
+ //
1586
+ // Accomodate date/time values
1587
+ //
1588
+ if (typeof value[0] == 'string') value[0] = RGraph.parseDate(value[0]);
1589
+ if (typeof value[1] == 'string') value[1] = RGraph.parseDate(value[1]) - value[0];
1590
+
1591
+ var x = (( (value[0] - xmin) / (xmax - xmin) ) * graphWidth) + this.marginLeft;
1592
+ var width = (value[1] / (xmax - xmin) ) * graphWidth;
1593
+
1594
+ this.context.fillStyle = value[2];
1595
+ this.context.fillRect(x, this.marginTop, width, (this.canvas.height - this.marginTop - this.marginBottom));
1596
+ }
1597
+ }
1598
+ };
1599
+
1600
+
1601
+
1602
+
1603
+
1604
+
1605
+
1606
+
1607
+ //
1608
+ // Draws in-graph labels.
1609
+ //
1610
+ // @param object obj The graph object
1611
+ //
1612
+ this.drawInGraphLabels = function (obj)
1613
+ {
1614
+ var labels = obj.get('labelsIngraph');
1615
+ var labels_processed = [];
1616
+
1617
+ if (!labels) {
1618
+ return;
1619
+ }
1620
+
1621
+ // Defaults
1622
+ var fgcolor = 'black';
1623
+ var bgcolor = 'white';
1624
+ var direction = 1;
1625
+
1626
+
1627
+ var textConf = RGraph.getTextConf({
1628
+ object: this,
1629
+ prefix: 'labelsIngraph'
1630
+ });
1631
+
1632
+ //
1633
+ // Preprocess the labels array. Numbers are expanded
1634
+ //
1635
+ for (var i=0,len=labels.length; i<len; i+=1) {
1636
+ if (typeof labels[i] == 'number') {
1637
+ for (var j=0; j<labels[i]; ++j) {
1638
+ labels_processed.push(null);
1639
+ }
1640
+ } else if (typeof labels[i] == 'string' || typeof labels[i] == 'object') {
1641
+ labels_processed.push(labels[i]);
1642
+
1643
+ } else {
1644
+ labels_processed.push('');
1645
+ }
1646
+ }
1647
+
1648
+ //
1649
+ // Turn off any shadow
1650
+ //
1651
+ RGraph.noShadow(obj);
1652
+
1653
+ if (labels_processed && labels_processed.length > 0) {
1654
+
1655
+ var i=0;
1656
+
1657
+ for (var set=0; set<obj.coords.length; ++set) {
1658
+ for (var point = 0; point<obj.coords[set].length; ++point) {
1659
+ if (labels_processed[i]) {
1660
+ var x = obj.coords[set][point][0] + properties.labelsIngraphOffsetx;
1661
+ var y = obj.coords[set][point][1] + properties.labelsIngraphOffsety;
1662
+ var length = typeof labels_processed[i][4] == 'number' ? labels_processed[i][4] : 25;
1663
+
1664
+ var text_x = x;
1665
+ var text_y = y - 5 - length;
1666
+
1667
+ this.context.moveTo(x, y - 5);
1668
+ this.context.lineTo(x, y - 5 - length);
1669
+
1670
+ this.context.stroke();
1671
+ this.context.beginPath();
1672
+
1673
+ // This draws the arrow
1674
+ this.context.moveTo(x, y - 5);
1675
+ this.context.lineTo(x - 3, y - 10);
1676
+ this.context.lineTo(x + 3, y - 10);
1677
+ this.context.closePath();
1678
+
1679
+ this.context.beginPath();
1680
+ // Fore ground color
1681
+ this.context.fillStyle = (typeof labels_processed[i] == 'object' && typeof labels_processed[i][1] == 'string') ? labels_processed[i][1] : 'black';
1682
+
1683
+ RGraph.text({
1684
+
1685
+ object: this,
1686
+
1687
+ font: textConf.font,
1688
+ size: textConf.size,
1689
+ color: textConf.color,
1690
+ bold: textConf.bold,
1691
+ italic: textConf.italic,
1692
+
1693
+ x: text_x,
1694
+ y: text_y,
1695
+ text: (typeof labels_processed[i] == 'object' && typeof labels_processed[i][0] == 'string') ? labels_processed[i][0] : labels_processed[i],
1696
+ valign: 'bottom',
1697
+ halign: 'center',
1698
+ bounding: true,
1699
+ boundingFill: (typeof labels_processed[i] == 'object' && typeof labels_processed[i][2] == 'string') ? labels_processed[i][2] : 'white',
1700
+ tag: 'labels.ingraph'
1701
+ });
1702
+ this.context.fill();
1703
+ }
1704
+
1705
+ i++;
1706
+ }
1707
+ }
1708
+ }
1709
+ };
1710
+
1711
+
1712
+
1713
+
1714
+
1715
+
1716
+
1717
+
1718
+ //
1719
+ // This function makes it much easier to get the (if any) point that is currently being hovered over.
1720
+ //
1721
+ // @param object e The event object
1722
+ //
1723
+ this.getShape = function (e)
1724
+ {
1725
+ var mouseXY = RGraph.getMouseXY(e);
1726
+ var mouseX = mouseXY[0];
1727
+ var mouseY = mouseXY[1];
1728
+ var overHotspot = false;
1729
+ var offset = properties.tooltipsHotspot; // This is how far the hotspot extends
1730
+
1731
+ if (this.isMarimekko) {
1732
+
1733
+ for (var i=0,seq=0; i<this.coordsMarimekko.length; ++i) {
1734
+ for (var j=0; j<this.coordsMarimekko[i].length; ++j) {
1735
+
1736
+ let coords = this.coordsMarimekko[i][j];
1737
+
1738
+ if ( mouseX > coords[0] && mouseX < (coords[0] + coords[2])
1739
+ && mouseY > coords[1] && mouseY < (coords[1] + coords[3])
1740
+ ) {
1741
+ // Determine the tooltip
1742
+ var tooltip = null;
1743
+ if (RGraph.isString(this.get('marimekkoTooltips'))) {
1744
+ tooltip = this.get('marimekkoTooltips');
1745
+ } else if ( RGraph.isArray(this.get('marimekkoTooltips')) && RGraph.isString(this.get('marimekkoTooltips')[seq])) {
1746
+ tooltip = this.get('marimekkoTooltips')[seq];
1747
+ }
1748
+
1749
+ if (RGraph.parseTooltipText) {
1750
+ tooltip = RGraph.parseTooltipText(tooltip, seq);
1751
+ }
1752
+
1753
+ // Return the shape array
1754
+ return {
1755
+ object: this,
1756
+ x: coords[0],
1757
+ y: coords[1],
1758
+ width: coords[2],
1759
+ height: coords[3],
1760
+ tooltip: tooltip,
1761
+ dataset: i,
1762
+ index: j,
1763
+ sequentialIndex: seq
1764
+ };
1765
+ }
1766
+
1767
+ ++seq;
1768
+
1769
+ }
1770
+ }
1771
+
1772
+ } else {
1773
+ for (var set=0,len=this.coords.length; set<len; ++set) {
1774
+ for (var i=0,len2=this.coords[set].length; i<len2; ++i) {
1775
+
1776
+ var x = this.coords[set][i][0];
1777
+ var y = this.coords[set][i][1];
1778
+ var tooltip = this.data[set][i][3];
1779
+
1780
+ if (typeof y == 'number') {
1781
+ if (mouseX <= (x + offset) &&
1782
+ mouseX >= (x - offset) &&
1783
+ mouseY <= (y + offset) &&
1784
+ mouseY >= (y - offset)) {
1785
+
1786
+
1787
+
1788
+ if (RGraph.parseTooltipText) {
1789
+ var tooltip = RGraph.parseTooltipText(this.data[set][i][3], 0);
1790
+ }
1791
+
1792
+ var sequentialIndex = i;
1793
+
1794
+ for (var ds=(set-1); ds >=0; --ds) {
1795
+ sequentialIndex += this.data[ds].length;
1796
+ }
1797
+
1798
+
1799
+
1800
+
1801
+
1802
+ // Should the point be ignored?
1803
+ if (RGraph.tooltipsHotspotIgnore(this, sequentialIndex)) {
1804
+ return;
1805
+ }
1806
+
1807
+
1808
+
1809
+
1810
+ return {
1811
+ object: this,
1812
+ x: x,
1813
+ y: y,
1814
+ tooltip: typeof tooltip === 'string' ? tooltip : null,
1815
+ dataset: set,
1816
+ index: i,
1817
+ sequentialIndex: sequentialIndex
1818
+ };
1819
+ }
1820
+
1821
+
1822
+
1823
+
1824
+ } else if (RGraph.isNull(y)) {
1825
+ // Nothing to see here
1826
+
1827
+
1828
+
1829
+
1830
+
1831
+ // Boxplots
1832
+ } else {
1833
+
1834
+ var mark = this.data[set][i];
1835
+
1836
+ //
1837
+ // Determine the width
1838
+ //
1839
+ var width = properties.boxplotWidth;
1840
+
1841
+ if (typeof mark[1][7] === 'number') {
1842
+ width = mark[1][7];
1843
+ }
1844
+
1845
+ if ( typeof x === 'object'
1846
+ && mouseX > x[0]
1847
+ && mouseX < x[1]
1848
+ && mouseY > y[1]
1849
+ && mouseY < y[3]
1850
+ ) {
1851
+
1852
+ var tooltip = RGraph.parseTooltipText(this.data[set][i][3], 0);
1853
+
1854
+ // Determine the sequential index
1855
+ var sequentialIndex = i;
1856
+ for (var ds=(set-1); ds >=0; --ds) {
1857
+ sequentialIndex += this.data[ds].length;
1858
+ }
1859
+
1860
+ return {
1861
+ object: this,
1862
+ x: x[0],
1863
+ y: y[3],
1864
+ width: Math.abs(x[1] - x[0]),
1865
+ height: Math.abs(y[1] - y[3]),
1866
+ dataset: set,
1867
+ index: i,
1868
+ sequentialIndex: sequentialIndex,
1869
+ tooltip: tooltip
1870
+ };
1871
+ }
1872
+ }
1873
+ }
1874
+ }
1875
+ }
1876
+ };
1877
+
1878
+
1879
+
1880
+
1881
+
1882
+
1883
+
1884
+
1885
+ //
1886
+ // Draws the above line labels
1887
+ //
1888
+ this.drawAboveLabels = function ()
1889
+ {
1890
+ var size = properties.labelsAboveSize;
1891
+ var font = properties.textFont;
1892
+ var units_pre = properties.yaxisScaleUnitsPre;
1893
+ var units_post = properties.yaxisScaleUnitsPost;
1894
+
1895
+ var textConf = RGraph.getTextConf({
1896
+ object: this,
1897
+ prefix: 'labelsAbove'
1898
+ });
1899
+
1900
+ for (var set=0,len=this.coords.length; set<len; ++set) {
1901
+ for (var point=0,len2=this.coords[set].length; point<len2; ++point) {
1902
+
1903
+ var x_val = this.data[set][point][0];
1904
+ var y_val = this.data[set][point][1];
1905
+
1906
+ if (!RGraph.isNull(y_val)) {
1907
+
1908
+ // Use the top most value from a box plot
1909
+ if (RGraph.isArray(y_val)) {
1910
+ var max = 0;
1911
+ for (var i=0; i<y_val; ++i) {
1912
+ max = Math.max(max, y_val[i]);
1913
+ }
1914
+
1915
+ y_val = max;
1916
+ }
1917
+
1918
+ var x_pos = this.coords[set][point][0];
1919
+ var y_pos = this.coords[set][point][1];
1920
+
1921
+
1922
+ var xvalueFormatter = properties.labelsAboveFormatterX;
1923
+ var yvalueFormatter = properties.labelsAboveFormatterY;
1924
+
1925
+ RGraph.text({
1926
+
1927
+ object: this,
1928
+
1929
+ font: textConf.font,
1930
+ size: textConf.size,
1931
+ color: textConf.color,
1932
+ bold: textConf.bold,
1933
+ italic: textConf.italic,
1934
+
1935
+ x: x_pos + properties.labelsAboveOffsetx,
1936
+ y: y_pos - 5 - size + properties.labelsAboveOffsety,
1937
+
1938
+ text:
1939
+ (typeof xvalueFormatter === 'function' ? xvalueFormatter(this, x_val) : x_val.toFixed(properties.labelsAboveDecimals)) +
1940
+ ', ' +
1941
+ (typeof yvalueFormatter === 'function' ? yvalueFormatter(this, y_val) : y_val.toFixed(properties.labelsAboveDecimals)),
1942
+
1943
+ valign: 'bottom',
1944
+ halign: 'center',
1945
+ bounding: true,
1946
+ boundingFill: 'rgba(255, 255, 255, 0.7)',
1947
+ boundingStroke: 'rgba(0,0,0,0.1)',
1948
+ tag: 'labels.above'
1949
+ });
1950
+ }
1951
+ }
1952
+ }
1953
+ };
1954
+
1955
+
1956
+
1957
+
1958
+
1959
+
1960
+
1961
+
1962
+ //
1963
+ // When you click on the chart, this method can return the Y value at that point. It works for any point on the
1964
+ // chart (that is inside the gutters) - not just points within the Bars.
1965
+ //
1966
+ // @param object e The event object
1967
+ //
1968
+ this.getYValue = function (arg)
1969
+ {
1970
+ if (arg.length == 2) {
1971
+ var mouseX = arg[0];
1972
+ var mouseY = arg[1];
1973
+ } else {
1974
+ var mouseCoords = RGraph.getMouseXY(arg);
1975
+ var mouseX = mouseCoords[0];
1976
+ var mouseY = mouseCoords[1];
1977
+ }
1978
+
1979
+ var obj = this;
1980
+
1981
+ if ( mouseY < this.marginTop
1982
+ || mouseY > (this.canvas.height - this.marginBottom)
1983
+ //|| mouseX < this.marginLeft
1984
+ //|| mouseX > (this.canvas.width - this.marginRight)
1985
+ ) {
1986
+ return null;
1987
+ }
1988
+
1989
+ if (properties.xaxisPosition == 'center') {
1990
+ var value = (((this.grapharea / 2) - (mouseY - this.marginTop)) / this.grapharea) * (this.max - this.min)
1991
+ value *= 2;
1992
+
1993
+
1994
+ // Account for each side of the X axis
1995
+ if (value >= 0) {
1996
+ value += this.min
1997
+
1998
+ if (properties.yaxisScaleInvert) {
1999
+ value -= this.min;
2000
+ value = this.max - value;
2001
+ }
2002
+
2003
+ } else {
2004
+
2005
+ value -= this.min;
2006
+ if (properties.yaxisScaleInvert) {
2007
+ value += this.min;
2008
+ value = this.max + value;
2009
+ value *= -1;
2010
+ }
2011
+ }
2012
+
2013
+ } else {
2014
+
2015
+ var value = ((this.grapharea - (mouseY - this.marginTop)) / this.grapharea) * (this.max - this.min)
2016
+ value += this.min;
2017
+
2018
+ if (properties.yaxisScaleInvert) {
2019
+ value -= this.min;
2020
+ value = this.max - value;
2021
+ }
2022
+ }
2023
+
2024
+ return value;
2025
+ };
2026
+
2027
+
2028
+
2029
+
2030
+
2031
+
2032
+
2033
+
2034
+ //
2035
+ // When you click on the chart, this method can return the X value at that point.
2036
+ //
2037
+ // @param mixed arg This can either be an event object or the X coordinate
2038
+ // @param number If specifying the X coord as the first arg then this should be the Y coord
2039
+ //
2040
+ this.getXValue = function (arg)
2041
+ {
2042
+ if (arg.length == 2) {
2043
+ var mouseX = arg[0];
2044
+ var mouseY = arg[1];
2045
+ } else {
2046
+ var mouseXY = RGraph.getMouseXY(arg);
2047
+ var mouseX = mouseXY[0];
2048
+ var mouseY = mouseXY[1];
2049
+ }
2050
+ var obj = this;
2051
+
2052
+ if (//|| mouseY < this.marginTop
2053
+ //|| mouseY > (this.canvas.height - this.marginBottom)
2054
+ mouseX < this.marginLeft
2055
+ || mouseX > (this.canvas.width - this.marginRight)
2056
+ ) {
2057
+ return null;
2058
+ }
2059
+
2060
+ var width = (this.canvas.width - this.marginLeft - this.marginRight);
2061
+ var value = ((mouseX - this.marginLeft) / width) * (properties.xaxisScaleMax - properties.xaxisScaleMin)
2062
+ value += properties.xaxisScaleMin;
2063
+
2064
+ return value;
2065
+ };
2066
+
2067
+
2068
+
2069
+
2070
+
2071
+
2072
+
2073
+
2074
+ //
2075
+ // Each object type has its own Highlight() function which highlights the appropriate shape
2076
+ //
2077
+ // @param object shape The shape to highlight
2078
+ //
2079
+ this.highlight = function (shape)
2080
+ {
2081
+ if (typeof properties.highlightStyle === 'function') {
2082
+ (properties.highlightStyle)(shape);
2083
+
2084
+ // Inverted highlight style
2085
+ } else if (properties.highlightStyle === 'invert') {
2086
+
2087
+ var tickmarksSize = properties.tickmarksSize;
2088
+
2089
+ // Clip to the graph area
2090
+ this.path(
2091
+ 'sa b r % % % % cl',
2092
+ properties.marginLeft - tickmarksSize, properties.marginTop - tickmarksSize,
2093
+ this.canvas.width - properties.marginLeft - properties.marginRight + tickmarksSize + tickmarksSize,
2094
+ this.canvas.height - properties.marginTop - properties.marginBottom + tickmarksSize + tickmarksSize
2095
+ );
2096
+
2097
+ this.path(
2098
+ 'b m % % a % % 25 4.71 4.72 true l % % l % % l % % l % % l % % c f %',
2099
+ shape.x, properties.marginTop,
2100
+ shape.x, shape.y,
2101
+ shape.x, 0,
2102
+ this.canvas.width, 0,
2103
+ this.canvas.width, this.canvas.height,
2104
+ 0, this.canvas.height,
2105
+ 0, 0,
2106
+ properties.highlightFill
2107
+ );
2108
+
2109
+ // Draw a border around the circular cutout
2110
+ this.path(
2111
+ 'b a % % 25 0 6.29 false s % rs',
2112
+ shape.x, shape.y,
2113
+ properties.highlightStroke
2114
+ );
2115
+ } else {
2116
+ // Boxplot highlight
2117
+ if (shape.height) {
2118
+ RGraph.Highlight.rect(this, shape);
2119
+
2120
+ // Point highlight
2121
+ } else {
2122
+ RGraph.Highlight.point(this, shape);
2123
+ }
2124
+ }
2125
+ };
2126
+
2127
+
2128
+
2129
+
2130
+
2131
+
2132
+
2133
+
2134
+ //
2135
+ // The getObjectByXY() worker method. Don't call this call:
2136
+ //
2137
+ // RGraph.ObjectRegistry.getObjectByXY(e)
2138
+ //
2139
+ // @param object e The event object
2140
+ //
2141
+ this.getObjectByXY = function (e)
2142
+ {
2143
+ var mouseXY = RGraph.getMouseXY(e);
2144
+
2145
+ if (
2146
+ mouseXY[0] > (this.marginLeft - 3)
2147
+ && mouseXY[0] < (this.canvas.width - this.marginRight + 3)
2148
+ && mouseXY[1] > (this.marginTop - 3)
2149
+ && mouseXY[1] < ((this.canvas.height - this.marginBottom) + 3)
2150
+ ) {
2151
+
2152
+ return this;
2153
+ }
2154
+ };
2155
+
2156
+
2157
+
2158
+
2159
+
2160
+
2161
+
2162
+
2163
+ //
2164
+ // This function can be used when the canvas is clicked on (or similar - depending on the event)
2165
+ // to retrieve the relevant X coordinate for a particular value.
2166
+ //
2167
+ // @param int value The value to get the X coordinate for
2168
+ //
2169
+ this.getXCoord = function (value)
2170
+ {
2171
+ if (typeof value != 'number' && typeof value != 'string') {
2172
+ return null;
2173
+ }
2174
+
2175
+ // Allow for date strings to be passed
2176
+ if (typeof value === 'string') {
2177
+ value = RGraph.parseDate(value);
2178
+ }
2179
+
2180
+ var xmin = properties.xaxisScaleMin;
2181
+ var xmax = properties.xaxisScaleMax;
2182
+ var x;
2183
+
2184
+ if (value < xmin) return null;
2185
+ if (value > xmax) return null;
2186
+
2187
+ if (properties.yaxisPosition == 'right') {
2188
+ x = ((value - xmin) / (xmax - xmin)) * (this.canvas.width - this.marginLeft - this.marginRight);
2189
+ x = (this.canvas.width - this.marginRight - x);
2190
+ } else {
2191
+ x = ((value - xmin) / (xmax - xmin)) * (this.canvas.width - this.marginLeft - this.marginRight);
2192
+ x = x + this.marginLeft;
2193
+ }
2194
+
2195
+ return x;
2196
+ };
2197
+
2198
+
2199
+
2200
+
2201
+
2202
+
2203
+
2204
+
2205
+ //
2206
+ // Returns the applicable Y COORDINATE when given a Y value
2207
+ //
2208
+ // @param int value The value to use
2209
+ // @return int The appropriate Y coordinate
2210
+ //
2211
+ this.getYCoord = function (value)
2212
+ {
2213
+ var outofbounds = arguments[1];
2214
+
2215
+ if (typeof value != 'number') {
2216
+
2217
+ return null;
2218
+ }
2219
+
2220
+ var invert = properties.yaxisScaleInvert;
2221
+ var xaxispos = properties.xaxisPosition;
2222
+ var graphHeight = this.canvas.height - this.marginTop - this.marginBottom;
2223
+ var halfGraphHeight = graphHeight / 2;
2224
+ var ymax = this.max;
2225
+ var ymin = properties.yaxisScaleMin;
2226
+ var coord = 0;
2227
+
2228
+ if ( (value > ymax && !outofbounds)
2229
+ || (properties.xaxisPosition === 'bottom' && value < ymin && !outofbounds)
2230
+ || (properties.xaxisPosition === 'center' && ((value > 0 && value < ymin) || (value < 0 && value > (-1 * ymin))))
2231
+ ) {
2232
+ return null;
2233
+ }
2234
+
2235
+ //
2236
+ // This calculates scale values if the X axis is in the center
2237
+ //
2238
+ if (xaxispos == 'center') {
2239
+
2240
+ coord = ((Math.abs(value) - ymin) / (ymax - ymin)) * halfGraphHeight;
2241
+
2242
+ if (invert) {
2243
+ coord = halfGraphHeight - coord;
2244
+ }
2245
+
2246
+ if (value < 0) {
2247
+ coord += this.marginTop;
2248
+ coord += halfGraphHeight;
2249
+ } else {
2250
+ coord = halfGraphHeight - coord;
2251
+ coord += this.marginTop;
2252
+ }
2253
+
2254
+ //
2255
+ // And this calculates scale values when the X axis is at the bottom
2256
+ //
2257
+ } else {
2258
+
2259
+ coord = ((value - ymin) / (ymax - ymin)) * graphHeight;
2260
+
2261
+ if (invert) {
2262
+ coord = graphHeight - coord;
2263
+ }
2264
+
2265
+ // Invert the coordinate because the Y scale starts at the top
2266
+ coord = graphHeight - coord;
2267
+
2268
+ // And add on the top gutter
2269
+ coord = this.marginTop + coord;
2270
+ }
2271
+
2272
+ return coord;
2273
+ };
2274
+
2275
+
2276
+
2277
+
2278
+
2279
+
2280
+
2281
+
2282
+ //
2283
+ // Draws a bubble chart
2284
+ //
2285
+ // @param dataset int The dataset index
2286
+ //
2287
+ this.drawBubble = function (dataset)
2288
+ {
2289
+ var data = RGraph.isArray(properties.bubbleData) && RGraph.isArray(properties.bubbleData[dataset]) ? properties.bubbleData[dataset] : properties.bubbleData;
2290
+ var min = RGraph.isArray(properties.bubbleMin) ? properties.bubbleMin[dataset] : properties.bubbleMin;
2291
+ var max = RGraph.isArray(properties.bubbleMax) ? properties.bubbleMax[dataset] : properties.bubbleMax;
2292
+ var width = RGraph.isArray(properties.bubbleWidth) ? properties.bubbleWidth[dataset] : properties.bubbleWidth;
2293
+
2294
+ // Initialise the coordinates array
2295
+ this.coordsBubble[dataset] = [];
2296
+
2297
+ // Loop through all the points (first dataset)
2298
+ for (var i=0; i<this.coords[dataset].length; ++i) {
2299
+
2300
+ data[i] = Math.max(data[i], min);
2301
+ data[i] = Math.min(data[i], max);
2302
+
2303
+ var radius = (((data[i] - min) / (max - min) ) * width) / 2,
2304
+ color = this.data[dataset][i][2] ? this.data[dataset][i][2] : properties.colorsDefault;
2305
+
2306
+
2307
+
2308
+
2309
+
2310
+
2311
+
2312
+
2313
+
2314
+
2315
+
2316
+
2317
+ // Set a shadow for the bubbles if requested
2318
+ if (this.properties.bubbleShadow) {
2319
+ RGraph.setShadow({
2320
+ object: this,
2321
+ prefix:'bubbleShadow'
2322
+ });
2323
+ }
2324
+
2325
+ this.context.beginPath();
2326
+ this.context.fillStyle = RGraph.radialGradient({
2327
+ object: this,
2328
+ x1: this.coords[dataset][i][0] + (radius / 2.5),
2329
+ y1: this.coords[dataset][i][1] - (radius / 2.5),
2330
+ r1: 0,
2331
+ x2: this.coords[dataset][i][0] + (radius / 2.5),
2332
+ y2: this.coords[dataset][i][1] - (radius / 2.5),
2333
+ r2: radius,
2334
+ colors: [
2335
+ properties.colorsBubbleGraduated ? 'white' : color,
2336
+ color
2337
+ ]
2338
+ });
2339
+
2340
+ // Draw the bubble
2341
+ this.context.arc(
2342
+ this.coords[dataset][i][0],
2343
+ this.coords[dataset][i][1],
2344
+ radius,
2345
+ 0,
2346
+ RGraph.TWOPI,
2347
+ false
2348
+ );
2349
+
2350
+ if (this.properties.colorsBubbleStroke) {
2351
+ this.context.lineWidth = this.properties.bubbleLinewidth;
2352
+ this.context.strokeStyle = this.properties.colorsBubbleStroke;
2353
+ this.context.stroke();
2354
+ }
2355
+
2356
+ this.context.fill();
2357
+
2358
+ // Clear the shadow
2359
+ if (this.properties.bubbleShadow) {
2360
+ RGraph.noShadow(this);
2361
+ }
2362
+
2363
+ this.coordsBubble[dataset][i] = [
2364
+ this.coords[dataset][i][0],
2365
+ this.coords[dataset][i][1],
2366
+ radius,
2367
+ this.context.fillStyle
2368
+ ];
2369
+ }
2370
+ };
2371
+
2372
+
2373
+
2374
+
2375
+
2376
+
2377
+
2378
+
2379
+ //
2380
+ // This allows for easy specification of gradients
2381
+ //
2382
+ this.parseColors = function ()
2383
+ {
2384
+
2385
+ // Save the original colors so that they can be restored when the canvas is reset
2386
+ if (this.original_colors.length === 0) {
2387
+ this.original_colors.data = RGraph.arrayClone(this.data);
2388
+ this.original_colors.backgroundVbars = RGraph.arrayClone(properties.backgroundVbars);
2389
+ this.original_colors.backgroundHbars = RGraph.arrayClone(properties.backgroundHbars);
2390
+ this.original_colors.lineColors = RGraph.arrayClone(properties.lineColors);
2391
+ this.original_colors.colorsDefault = RGraph.arrayClone(properties.colorsDefault);
2392
+ this.original_colors.crosshairsColor = RGraph.arrayClone(properties.crosshairsColor);
2393
+ this.original_colors.highlightStroke = RGraph.arrayClone(properties.highlightStroke);
2394
+ this.original_colors.highlightFill = RGraph.arrayClone(properties.highlightFill);
2395
+ this.original_colors.backgroundBarsColor1 = RGraph.arrayClone(properties.backgroundBarsColor1);
2396
+ this.original_colors.backgroundBarsColor2 = RGraph.arrayClone(properties.backgroundBarsColor2);
2397
+ this.original_colors.backgroundGridColor = RGraph.arrayClone(properties.backgroundGridColor);
2398
+ this.original_colors.backgroundColor = RGraph.arrayClone(properties.backgroundColor);
2399
+ this.original_colors.axesColor = RGraph.arrayClone(properties.axesColor);
2400
+ this.original_colors.marimekkoColors = RGraph.arrayClone(properties.marimekkoColors);
2401
+ this.original_colors.marimekkoColorsStroke= RGraph.arrayClone(properties.marimekkoColorsStroke);
2402
+ this.original_colors.marimekkoLabelsIngraphBackgroundStroke= RGraph.arrayClone(properties.marimekkoLabelsIngraphBackgroundStroke);
2403
+ this.original_colors.marimekkoLabelsIngraphBackgroundFill = RGraph.arrayClone(properties.marimekkoLabelsIngraphBackgroundFill);
2404
+ }
2405
+
2406
+
2407
+
2408
+
2409
+
2410
+ // Colors
2411
+ var data = this.data;
2412
+ if (data) {
2413
+ for (var dataset=0; dataset<data.length; ++dataset) {
2414
+ for (var i=0; i<this.data[dataset].length; ++i) {
2415
+
2416
+ // Boxplots
2417
+ if (this.data[dataset][i] && typeof this.data[dataset][i][1] == 'object' && this.data[dataset][i][1]) {
2418
+
2419
+ if (typeof this.data[dataset][i][1][5] == 'string') this.data[dataset][i][1][5] = this.parseSingleColorForGradient(this.data[dataset][i][1][5]);
2420
+ if (typeof this.data[dataset][i][1][6] == 'string') this.data[dataset][i][1][6] = this.parseSingleColorForGradient(this.data[dataset][i][1][6]);
2421
+ }
2422
+
2423
+ if (!RGraph.isNull(this.data[dataset][i])) {
2424
+ this.data[dataset][i][2] = this.parseSingleColorForGradient(this.data[dataset][i][2]);
2425
+ }
2426
+ }
2427
+ }
2428
+ }
2429
+
2430
+ // Parse HBars
2431
+ var hbars = properties.backgroundHbars;
2432
+ if (hbars) {
2433
+ for (i=0; i<hbars.length; ++i) {
2434
+ hbars[i][2] = this.parseSingleColorForGradient(hbars[i][2]);
2435
+ }
2436
+ }
2437
+
2438
+ // Parse HBars
2439
+ var vbars = properties.backgroundVbars;
2440
+ if (vbars) {
2441
+ for (i=0; i<vbars.length; ++i) {
2442
+ vbars[i][2] = this.parseSingleColorForGradient(vbars[i][2]);
2443
+ }
2444
+ }
2445
+
2446
+ // Parse line colors
2447
+ var colors = properties.lineColors;
2448
+ if (colors) {
2449
+ for (i=0; i<colors.length; ++i) {
2450
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
2451
+ }
2452
+ }
2453
+
2454
+ // Parse colors for marimekko charts
2455
+ var colors = properties.marimekkoColors;
2456
+ if (colors) {
2457
+ for (i=0; i<colors.length; ++i) {
2458
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
2459
+ }
2460
+ }
2461
+
2462
+ properties.colorsDefault = this.parseSingleColorForGradient(properties.colorsDefault);
2463
+ properties.crosshairsColor = this.parseSingleColorForGradient(properties.crosshairsColor);
2464
+ properties.highlightStroke = this.parseSingleColorForGradient(properties.highlightStroke);
2465
+ properties.highlightFill = this.parseSingleColorForGradient(properties.highlightFill);
2466
+ properties.backgroundBarsColor1 = this.parseSingleColorForGradient(properties.backgroundBarsColor1);
2467
+ properties.backgroundBarsColor2 = this.parseSingleColorForGradient(properties.backgroundBarsColor2);
2468
+ properties.backgroundGridColor = this.parseSingleColorForGradient(properties.backgroundGridColor);
2469
+ properties.backgroundColor = this.parseSingleColorForGradient(properties.backgroundColor);
2470
+ properties.axesColor = this.parseSingleColorForGradient(properties.axesColor);
2471
+ properties.marimekkoColorsStroke = this.parseSingleColorForGradient(properties.marimekkoColorsStroke);
2472
+ properties.marimekkoLabelsIngraphBackgroundStroke = this.parseSingleColorForGradient(properties.marimekkoLabelsIngraphBackgroundStroke);
2473
+ properties.marimekkoLabelsIngraphBackgroundFill = this.parseSingleColorForGradient(properties.marimekkoLabelsIngraphBackgroundFill);
2474
+ };
2475
+
2476
+
2477
+
2478
+
2479
+
2480
+
2481
+
2482
+
2483
+ //
2484
+ // Use this function to reset the object to the post-constructor state. Eg reset colors if
2485
+ // need be etc
2486
+ //
2487
+ this.reset = function ()
2488
+ {
2489
+ };
2490
+
2491
+
2492
+
2493
+
2494
+
2495
+
2496
+
2497
+
2498
+ //
2499
+ // This parses a single color value for a gradient
2500
+ //
2501
+ this.parseSingleColorForGradient = function (color)
2502
+ {
2503
+ if (!color || typeof color != 'string') {
2504
+ return color;
2505
+ }
2506
+
2507
+ if (color.match(/^gradient\((.*)\)$/i)) {
2508
+
2509
+
2510
+ // Allow for JSON gradients
2511
+ if (color.match(/^gradient\(({.*})\)$/i)) {
2512
+ return RGraph.parseJSONGradient({object: this, def: RegExp.$1});
2513
+ }
2514
+
2515
+ // Allow for JSON gradients
2516
+ if (color.match(/^gradient\(({.*})\)$/i)) {
2517
+ return RGraph.parseJSONGradient({object: this, def: RegExp.$1});
2518
+ }
2519
+
2520
+ var parts = RegExp.$1.split(':');
2521
+
2522
+ // Create the gradient
2523
+ var grad = this.context.createLinearGradient(0,this.canvas.height - properties.marginBottom, 0, properties.marginTop);
2524
+
2525
+ var diff = 1 / (parts.length - 1);
2526
+
2527
+ grad.addColorStop(0, RGraph.trim(parts[0]));
2528
+
2529
+ for (var j=1; j<parts.length; ++j) {
2530
+ grad.addColorStop(j * diff, RGraph.trim(parts[j]));
2531
+ }
2532
+ }
2533
+
2534
+ return grad ? grad : color;
2535
+ };
2536
+
2537
+
2538
+
2539
+
2540
+
2541
+
2542
+
2543
+
2544
+ //
2545
+ // This function handles highlighting an entire data-series for the interactive
2546
+ // key
2547
+ //
2548
+ // @param int index The index of the data series to be highlighted
2549
+ //
2550
+ this.interactiveKeyHighlight = function (index)
2551
+ {
2552
+ if (this.coords && this.coords[index] && this.coords[index].length) {
2553
+
2554
+ var obj = this;
2555
+
2556
+ this.coords[index].forEach(function (value, idx, arr)
2557
+ {
2558
+ obj.context.beginPath();
2559
+ obj.context.fillStyle = properties.keyInteractiveHighlightChartFill;
2560
+ obj.context.arc(value[0], value[1], properties.tickmarksSize + 3, 0, RGraph.TWOPI, false);
2561
+ obj.context.fill();
2562
+ });
2563
+ }
2564
+ };
2565
+
2566
+
2567
+
2568
+
2569
+
2570
+
2571
+
2572
+
2573
+ //
2574
+ // Using a function to add events makes it easier to facilitate method chaining
2575
+ //
2576
+ // @param string type The type of even to add
2577
+ // @param function func
2578
+ //
2579
+ this.on = function (type, func)
2580
+ {
2581
+ if (type.substr(0,2) !== 'on') {
2582
+ type = 'on' + type;
2583
+ }
2584
+
2585
+ if (typeof this[type] !== 'function') {
2586
+ this[type] = func;
2587
+ } else {
2588
+ RGraph.addCustomEventListener(this, type, func);
2589
+ }
2590
+
2591
+ return this;
2592
+ };
2593
+
2594
+
2595
+
2596
+
2597
+
2598
+
2599
+
2600
+
2601
+ //
2602
+ // This function runs once only
2603
+ // (put at the end of the file (before any effects))
2604
+ //
2605
+ this.firstDrawFunc = function ()
2606
+ {
2607
+ };
2608
+
2609
+
2610
+
2611
+
2612
+
2613
+
2614
+
2615
+
2616
+ //
2617
+ // Used in chaining. Runs a function there and then - not waiting for
2618
+ // the events to fire (eg the onbeforedraw event)
2619
+ //
2620
+ // @param function func The function to execute
2621
+ //
2622
+ this.exec = function (func)
2623
+ {
2624
+ func(this);
2625
+
2626
+ return this;
2627
+ };
2628
+
2629
+
2630
+
2631
+
2632
+
2633
+
2634
+
2635
+
2636
+ //
2637
+ // Draws a trendline on the Scatter chart. This is also known
2638
+ // as a "best-fit line"
2639
+ //
2640
+ //@param dataset The index of the dataset to use
2641
+ //
2642
+ this.drawTrendline = function ()
2643
+ {
2644
+ var args = RGraph.getArgs(arguments, 'dataset');
2645
+
2646
+
2647
+ var color = properties.trendlineColor,
2648
+ linewidth = properties.trendlineLinewidth,
2649
+ margin = properties.trendlineMargin;
2650
+
2651
+
2652
+ // Allow for trendlineColors as well
2653
+ if (RGraph.isArray(properties.trendlineColors)) {
2654
+ color = properties.trendlineColors;
2655
+ }
2656
+
2657
+
2658
+
2659
+ // handle the options being arrays
2660
+ if (typeof color === 'object' && color[args.dataset]) {
2661
+ color = color[args.dataset];
2662
+ } else if (typeof color === 'object') {
2663
+ color = 'gray';
2664
+ }
2665
+
2666
+ if (typeof linewidth === 'object' && typeof linewidth[args.dataset] === 'number') {
2667
+ linewidth = linewidth[args.dataset];
2668
+ } else if (typeof linewidth === 'object') {
2669
+ linewidth = 1;
2670
+ }
2671
+
2672
+ if (typeof margin === 'object' && typeof margin[args.dataset] === 'number') {
2673
+ margin = margin[args.dataset];
2674
+ } else if (typeof margin === 'object'){
2675
+ margin = 25;
2676
+ }
2677
+
2678
+
2679
+ // Step 1: Calculate the mean values of the X coords and the Y coords
2680
+ for (var i=0,totalX=0,totalY=0; i<this.data[args.dataset].length; ++i) {
2681
+ totalX += this.data[args.dataset][i][0];
2682
+ totalY += this.data[args.dataset][i][1];
2683
+ }
2684
+
2685
+ var averageX = totalX / this.data[args.dataset].length;
2686
+ var averageY = totalY / this.data[args.dataset].length;
2687
+
2688
+ // Step 2: Calculate the slope of the line
2689
+
2690
+ // a: The X/Y values minus the average X/Y value
2691
+ for (var i=0,xCoordMinusAverageX=[],yCoordMinusAverageY=[],valuesMultiplied=[],xCoordMinusAverageSquared=[]; i<this.data[args.dataset].length; ++i) {
2692
+ xCoordMinusAverageX[i] = this.data[args.dataset][i][0] - averageX;
2693
+ yCoordMinusAverageY[i] = this.data[args.dataset][i][1] - averageY;
2694
+
2695
+ // b. Multiply the averages
2696
+ valuesMultiplied[i] = xCoordMinusAverageX[i] * yCoordMinusAverageY[i];
2697
+ xCoordMinusAverageSquared[i] = xCoordMinusAverageX[i] * xCoordMinusAverageX[i];
2698
+ }
2699
+
2700
+ var sumOfValuesMultiplied = RGraph.arraySum(valuesMultiplied);
2701
+ var sumOfXCoordMinusAverageSquared = RGraph.arraySum(xCoordMinusAverageSquared);
2702
+
2703
+ // Calculate m (???)
2704
+ var m = sumOfValuesMultiplied / sumOfXCoordMinusAverageSquared;
2705
+ var b = averageY - (m * averageX);
2706
+
2707
+ // y = mx + b
2708
+
2709
+ coords = [
2710
+ [properties.xaxisScaleMin, m * properties.xaxisScaleMin + b],
2711
+ [properties.xaxisScaleMax, m * properties.xaxisScaleMax + b]
2712
+ ];
2713
+
2714
+ //
2715
+ // Draw the line
2716
+ //
2717
+
2718
+ // Set dotted, dash or a custom dash array
2719
+ if (properties.trendlineDashed) {
2720
+ this.context.setLineDash([4,4]);
2721
+ }
2722
+
2723
+ if (properties.trendlineDotted) {
2724
+ this.context.setLineDash([1,4]);
2725
+ }
2726
+
2727
+ if (!RGraph.isNull(properties.trendlineDashArray) && typeof properties.trendlineDashArray === 'object') {
2728
+ this.context.setLineDash(properties.trendlineDashArray);
2729
+ }
2730
+
2731
+
2732
+ // Clip the canvas again so that the line doesn't look overly long
2733
+ // (use the minimum an maximum points for this)
2734
+ for (var i=0,xValues=[],yValues=[]; i<this.data[args.dataset].length; ++i) {
2735
+ if (typeof this.data[args.dataset][i][0] === 'number') {
2736
+ xValues.push(this.data[args.dataset][i][0]);
2737
+ }
2738
+
2739
+ if (typeof this.data[args.dataset][i][1] === 'number') {
2740
+ yValues.push(this.data[args.dataset][i][1]);
2741
+ }
2742
+ }
2743
+
2744
+ // These are the minimum and maximum X/Y values for this dataset
2745
+ var x1 = RGraph.arrayMin({array: xValues});
2746
+ var y1 = RGraph.arrayMin({array: yValues});
2747
+ var x2 = RGraph.arrayMax({array: xValues});
2748
+ var y2 = RGraph.arrayMax({array: yValues});
2749
+
2750
+
2751
+ // Convert the X/Y values into coordinates on the canvas
2752
+ x1 = this.getXCoord(x1);
2753
+ y1 = this.getYCoord(y1, properties.outofbounds);
2754
+ x2 = this.getXCoord(x2);
2755
+ y2 = this.getYCoord(y2, properties.outofbounds);
2756
+
2757
+
2758
+ // Draw the line
2759
+ this.path(
2760
+ ' lw % sa b r % % % % cl b r % % % % cl b m % % l % % s % rs',
2761
+ linewidth,
2762
+
2763
+ // These are the rect arguments
2764
+ properties.marginLeft + margin,
2765
+ properties.marginTop + margin,
2766
+ this.canvas.width - properties.marginLeft - properties.marginRight - margin - margin,
2767
+ this.canvas.height - properties.marginTop - properties.marginBottom - margin - margin,
2768
+
2769
+ // These are the second rect arguments
2770
+ properties.trendlineClipping === false ? 0 : x1 - 25,
2771
+ properties.trendlineClipping === false ? 0 : y2 - 25,
2772
+ properties.trendlineClipping === false ? this.canvas.width : x2 - x1 + 25 + 25,
2773
+ properties.trendlineClipping === false ? this.canvas.height : y1 - y2 + 25 + 25,
2774
+
2775
+ // moveTo
2776
+ this.getXCoord(coords[0][0]), this.getYCoord(coords[0][1], true),
2777
+
2778
+ // lineTo
2779
+ this.getXCoord(coords[1][0]), this.getYCoord(coords[1][1], true),
2780
+
2781
+ // stroke color
2782
+ color
2783
+ );
2784
+
2785
+ // Reset the line dash array
2786
+ this.context.setLineDash([5,0]);
2787
+ };
2788
+
2789
+
2790
+
2791
+
2792
+
2793
+
2794
+
2795
+ //
2796
+ // Trace2
2797
+ //
2798
+ // This is a new version of the Trace effect which no longer requires jQuery and is more compatible
2799
+ // with other effects (eg Expand). This new effect is considerably simpler and less code.
2800
+ //
2801
+ // @param object Options for the effect. Currently only "frames" is available.
2802
+ // @param int A function that is called when the ffect is complete
2803
+ //
2804
+ this.trace = function ()
2805
+ {
2806
+ // Cancel any stop request if one is pending
2807
+ this.cancelStopAnimation();
2808
+
2809
+ var obj = this,
2810
+ callback = arguments[2],
2811
+ opt = arguments[0] || {},
2812
+ frames = opt.frames || 30,
2813
+ frame = 0,
2814
+ callback = arguments[1] || function () {}
2815
+
2816
+ this.set('animationTrace', true);
2817
+ this.set('animationTraceClip', 0);
2818
+
2819
+ function iterator ()
2820
+ {
2821
+ if (obj.stopAnimationRequested) {
2822
+
2823
+ // Reset the flag
2824
+ obj.stopAnimationRequested = false;
2825
+
2826
+ return;
2827
+ }
2828
+
2829
+ RGraph.clear(obj.canvas);
2830
+
2831
+ RGraph.redrawCanvas(obj.canvas);
2832
+
2833
+ if (frame++ < frames) {
2834
+ obj.set('animationTraceClip', frame / frames);
2835
+ RGraph.Effects.updateCanvas(iterator);
2836
+ } else {
2837
+ callback(obj);
2838
+ }
2839
+ }
2840
+
2841
+ iterator();
2842
+
2843
+ return this;
2844
+ };
2845
+
2846
+
2847
+
2848
+
2849
+
2850
+
2851
+
2852
+
2853
+ //
2854
+ // The explode effect.
2855
+ //
2856
+ // @param object Options for the effect.
2857
+ // @param int A function that is called when the ffect is complete
2858
+ //
2859
+ this.explode = function ()
2860
+ {
2861
+ // Cancel any stop request if one is pending
2862
+ this.cancelStopAnimation();
2863
+
2864
+ var obj = this,
2865
+ callback = arguments[2],
2866
+ opt = arguments[0] || {},
2867
+ frames = opt.frames || 15,
2868
+ frame = 0,
2869
+ callback = arguments[1] || function () {},
2870
+ originX = this.properties.xaxisScaleMax / 2,
2871
+ originY = 0,
2872
+ step = 1 / frames,
2873
+ original = RGraph.arrayClone(this.unmodified_data);
2874
+
2875
+ // First draw the chart, set the yaxisScaleMax to the maximum value that's calculated
2876
+ // and then animate
2877
+ this.draw();
2878
+ this.set('yaxisScaleMax', this.scale2.max);
2879
+
2880
+ // Determine the X origin - it defaults to the center
2881
+ if (opt.originx === 'left') {
2882
+ originX = 0;
2883
+ } else if (opt.originx === 'right') {
2884
+ originX = this.properties.xaxisScaleMax;
2885
+ } else {
2886
+ originX = this.properties.xaxisScaleMax / 2;
2887
+ }
2888
+
2889
+ // Determine the Y origin - it defaults to the bottom
2890
+ if (opt.originy === 'center') {
2891
+ originY = this.scale2.max / 2;
2892
+ } else if (opt.originy === 'top') {
2893
+ originY = this.scale2.max;
2894
+ } else {
2895
+ originY = 0;
2896
+ }
2897
+
2898
+
2899
+
2900
+ function iterator ()
2901
+ {
2902
+ if (obj.stopAnimationRequested) {
2903
+
2904
+ // Reset the flag
2905
+ obj.stopAnimationRequested = false;
2906
+
2907
+ return;
2908
+ }
2909
+ RGraph.clear(obj.canvas);
2910
+
2911
+ for (var i=0; i<obj.data.length; ++i) { // Loop through each dataset
2912
+ for (var j=0; j<obj.data[i].length; ++j) { // Loop through each point
2913
+ obj.data[i][j][0] =
2914
+ originX + (
2915
+ (original[i][j][0] - originX)
2916
+ * step
2917
+ * frame
2918
+ );
2919
+ obj.data[i][j][1] = originY + ((original[i][j][1] - originY) * step * frame);
2920
+ }
2921
+ }
2922
+
2923
+ RGraph.redrawCanvas(obj.canvas);
2924
+ if (frame++ < frames) {
2925
+ RGraph.Effects.updateCanvas(iterator);
2926
+ } else {
2927
+ RGraph.redrawCanvas(obj.canvas);
2928
+ callback(obj);
2929
+ }
2930
+ }
2931
+
2932
+ iterator();
2933
+
2934
+ return this;
2935
+ };
2936
+
2937
+
2938
+
2939
+
2940
+
2941
+
2942
+
2943
+
2944
+ //
2945
+ // Couple of functions that allow you to control the
2946
+ // animation effect
2947
+ //
2948
+ this.stopAnimation = function ()
2949
+ {
2950
+ // Reset the clip area
2951
+ this.set('animationTraceClip', 1);
2952
+
2953
+ // Reset the data
2954
+ this.data = RGraph.arrayClone(this.unmodified_data);
2955
+
2956
+ this.stopAnimationRequested = true;
2957
+ };
2958
+
2959
+ this.cancelStopAnimation = function ()
2960
+ {
2961
+ this.stopAnimationRequested = false;
2962
+ };
2963
+
2964
+
2965
+
2966
+
2967
+
2968
+
2969
+
2970
+
2971
+ //
2972
+ // This helps the Gantt reset colors when the reset function is called.
2973
+ // It handles going through the data and resetting the colors.
2974
+ //
2975
+ this.resetColorsToOriginalValues = function ()
2976
+ {
2977
+ //
2978
+ // Copy the original colors over for single-event-per-line data
2979
+ //
2980
+ for (var i=0,len=this.original_colors.data.length; i<len; ++i) {
2981
+ for (var j=0,len2=this.original_colors.data[i].length; j<len2;++j) {
2982
+
2983
+ // The color for the point
2984
+ this.data[i][j][2] = RGraph.arrayClone(this.original_colors.data[i][j][2]);
2985
+
2986
+ // Handle boxplots
2987
+ if (typeof this.data[i][j][1] === 'object') {
2988
+ this.data[i][j][1][5] = RGraph.arrayClone(this.original_colors.data[i][j][1][5]);
2989
+ this.data[i][j][1][6] = RGraph.arrayClone(this.original_colors.data[i][j][1][6]);
2990
+ }
2991
+ }
2992
+ }
2993
+ };
2994
+
2995
+
2996
+
2997
+
2998
+
2999
+
3000
+
3001
+
3002
+ // If only one tooltip has been given populate each data-piece with it
3003
+ this.populateTooltips = function ()
3004
+ {
3005
+ for (var i=0; i<this.data.length; ++i) { // for each dataset...
3006
+ for (var j=0; j<this.data[i].length; ++j) { // For each point in the dataset
3007
+ this.data[i][j][3] = properties.tooltips;
3008
+ }
3009
+ }
3010
+ };
3011
+
3012
+
3013
+
3014
+
3015
+
3016
+
3017
+
3018
+
3019
+ //
3020
+ // A worker function that handles Bar chart specific tooltip substitutions
3021
+ //
3022
+ this.tooltipSubstitutions = function (opt)
3023
+ {
3024
+ // Create the data for marimekko charts
3025
+ if (this.isMarimekko) {
3026
+ var marimekko_data = [];
3027
+ for (var i=0; i<this.properties.marimekkoData.length; ++i) {
3028
+ marimekko_data[i] = [];
3029
+ for (var j=0; j<this.properties.marimekkoData[i][1].length; ++j) {
3030
+ marimekko_data[i].push(this.properties.marimekkoData[i][1][j]);
3031
+ }
3032
+ }
3033
+ }
3034
+
3035
+
3036
+ // Marimekko charts
3037
+ if (this.isMarimekko) {
3038
+ var indexes = RGraph.sequentialIndexToGrouped(
3039
+ opt.index,
3040
+ marimekko_data
3041
+ );
3042
+
3043
+ var value = marimekko_data[indexes[0]][indexes[1]];
3044
+ var values = [marimekko_data[indexes[0]][indexes[1]]];
3045
+
3046
+ // Regular Scatter charts
3047
+ } else {
3048
+ var indexes = RGraph.sequentialIndexToGrouped(
3049
+ opt.index,
3050
+ this.data
3051
+ );
3052
+
3053
+ var value = this.data[indexes[0]][indexes[1]][1];
3054
+ var values = [this.data[indexes[0]][indexes[1]][1]];
3055
+ }
3056
+
3057
+
3058
+
3059
+ return {
3060
+ index: indexes[1],
3061
+ dataset: indexes[0],
3062
+ sequentialIndex: opt.index,
3063
+ value: value,
3064
+ values: values
3065
+ };
3066
+ };
3067
+
3068
+
3069
+
3070
+
3071
+
3072
+
3073
+
3074
+
3075
+ //
3076
+ // A worker function that returns the correct color/label/value
3077
+ //
3078
+ // @param object specific The indexes that are applicable
3079
+ // @param number index The appropriate index
3080
+ //
3081
+ this.tooltipsFormattedCustom = function (specific, index)
3082
+ {
3083
+ // The tooltipsFormattedKeyColors property has been specified so use that if
3084
+ // there's a relevant color
3085
+ if ( !RGraph.isNull(properties.tooltipsFormattedKeyColors)
3086
+ && typeof properties.tooltipsFormattedKeyColors === 'object'
3087
+ && typeof properties.tooltipsFormattedKeyColors[specific.dataset] === 'string'
3088
+ ) {
3089
+ var color = properties.tooltipsFormattedKeyColors[specific.dataset];
3090
+ }
3091
+
3092
+ // If a color is defined for this point then use it
3093
+ if (!this.isMarimekko && this.data[specific.dataset][specific.index][2]) {
3094
+ color = this.data[specific.dataset][specific.index][2];
3095
+ }
3096
+
3097
+
3098
+ var label = properties.tooltipsFormattedKeyLabels[specific.index]
3099
+ ? properties.tooltipsFormattedKeyLabels[specific.index]
3100
+ : '';
3101
+
3102
+ return {
3103
+ label: label,
3104
+ color: color
3105
+ };
3106
+ };
3107
+
3108
+
3109
+
3110
+
3111
+
3112
+
3113
+
3114
+
3115
+ //
3116
+ // This allows for static tooltip positioning
3117
+ //
3118
+ this.positionTooltipStatic = function (args)
3119
+ {
3120
+ // Make the data into a bog standard 2D array
3121
+ if (this.isMarimekko) {
3122
+ var marimekko_data = [];
3123
+ for (var i=0; i<this.properties.marimekkoData.length; ++i) {
3124
+ marimekko_data[i] = [];
3125
+ for (var j=0; j<this.properties.marimekkoData[i][1].length; ++j) {
3126
+ marimekko_data[i].push(this.properties.marimekkoData[i][1][j]);
3127
+ }
3128
+ }
3129
+ }
3130
+
3131
+
3132
+
3133
+
3134
+ var obj = args.object,
3135
+ e = args.event,
3136
+ tooltip = args.tooltip,
3137
+ index = args.index,
3138
+ canvasXY = RGraph.getCanvasXY(obj.canvas),
3139
+ indexes = RGraph.sequentialIndexToGrouped(args.index, this.isMarimekko ? marimekko_data : this.data),
3140
+ coords = this.isMarimekko ? this.coordsMarimekko[indexes[0]][indexes[1]] : this.coords[indexes[0]][indexes[1]];
3141
+
3142
+
3143
+
3144
+ // Position the tooltip in the X direction
3145
+ args.tooltip.style.left = (
3146
+ canvasXY[0] // The X coordinate of the canvas
3147
+ + (this.isMarimekko ? (coords[0] + (coords[2] / 2) ) : coords[0]) // The X coordinate of the point on the chart
3148
+ - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width
3149
+ + obj.properties.tooltipsOffsetx // Add any user defined offset
3150
+ ) + 'px';
3151
+
3152
+ args.tooltip.style.top = (
3153
+ canvasXY[1] // The Y coordinate of the canvas
3154
+ + coords[1] // The Y coordinate of the bar on the chart
3155
+ - tooltip.offsetHeight // The height of the tooltip
3156
+ - 15 // An arbitrary amount
3157
+ + obj.properties.tooltipsOffsety // Add any user defined offset
3158
+ ) + 'px';
3159
+ };
3160
+
3161
+
3162
+
3163
+
3164
+
3165
+
3166
+
3167
+
3168
+ //
3169
+ // This returns the relevant value for the formatted key
3170
+ // macro %{value}. THIS VALUE SHOULD NOT BE FORMATTED.
3171
+ //
3172
+ // @param number index The index in the dataset to get
3173
+ // the value for
3174
+ //
3175
+ this.getKeyValue = function (index)
3176
+ {
3177
+ if ( RGraph.isArray(this.properties.keyFormattedValueSpecific)
3178
+ && RGraph.isNumber(this.properties.keyFormattedValueSpecific[index])) {
3179
+
3180
+ return this.properties.keyFormattedValueSpecific[index];
3181
+
3182
+
3183
+
3184
+
3185
+ } else {
3186
+ var totalX = 0;
3187
+ var totalY = 0;
3188
+
3189
+ if (this.data[index]) {
3190
+ for (var i=0; i<this.data[index].length; ++i) {
3191
+ totalX += this.data[index][i][0];
3192
+ totalY += this.data[index][i][1];
3193
+ }
3194
+ }
3195
+
3196
+ return [totalX, totalY];
3197
+ }
3198
+ };
3199
+
3200
+
3201
+
3202
+
3203
+
3204
+
3205
+
3206
+
3207
+ //
3208
+ // Returns how many data-points there should be when a string
3209
+ // based key property has been specified. For example, this:
3210
+ //
3211
+ // key: '%{property:_labels[%{index}]} %{value_formatted}'
3212
+ //
3213
+ // ...depending on how many bits of data ther is might get
3214
+ // turned into this:
3215
+ //
3216
+ // key: [
3217
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3218
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3219
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3220
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3221
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3222
+ // ]
3223
+ //
3224
+ // ... ie in that case there would be 4 data-points so the
3225
+ // template is repeated 4 times.
3226
+ //
3227
+ this.getKeyNumDatapoints = function ()
3228
+ {
3229
+ return this.data[0].length;
3230
+ };
3231
+
3232
+
3233
+
3234
+
3235
+
3236
+
3237
+
3238
+
3239
+ //
3240
+ // The lasso fature allows you to draw around points.
3241
+ // The callback is passed an array of datapoints and
3242
+ // indexes (NOT values!) which you can use to get the
3243
+ // points from the scatter chart object.
3244
+ //
3245
+ this.installLasso = function ()
3246
+ {
3247
+ // Set this so that the event listeners can
3248
+ // access the object
3249
+ var obj = this;
3250
+
3251
+
3252
+
3253
+ if (this.properties.lasso) {
3254
+
3255
+ var localData_key = 'rgraph-scatter-chart-' + RGraph.md5(location.href + obj.id) + '-lasso-data';
3256
+
3257
+ //
3258
+ // So the default object isn't repeated
3259
+ //
3260
+ obj.lassoGetDefaultObject = function ()
3261
+ {
3262
+ return {state: {
3263
+ coords: [],
3264
+ mousedown: false
3265
+ }};
3266
+ };
3267
+
3268
+
3269
+
3270
+
3271
+
3272
+
3273
+ //
3274
+ // Save the lasso data to localstorage so
3275
+ // that it persists acoss refreshes
3276
+ //
3277
+ obj.lassoSaveData = function (data)
3278
+ {
3279
+ //
3280
+ // Save the state to localStorage by
3281
+ // default
3282
+ //
3283
+ if (obj.properties.lassoPersistLocal) {
3284
+ var str = JSON.stringify(data);
3285
+ window.localStorage[localData_key] = str;
3286
+ }
3287
+
3288
+ // Call the event if it's defined
3289
+ if (RGraph.isFunction(obj.properties.lassoPersistSave)) {
3290
+ obj.properties.lassoPersistSave(data);
3291
+ }
3292
+ };
3293
+
3294
+ //
3295
+ // Load the lasso data from localstorage
3296
+ //
3297
+ obj.lassoLoadData = function ()
3298
+ {
3299
+ // Load the data from localData
3300
+ if (obj.properties.lassoPersistLocal) {
3301
+ var str = window.localStorage[localData_key];
3302
+ }
3303
+
3304
+ var data = str ? JSON.parse(str) : obj.lassoGetDefaultObject().state;
3305
+
3306
+ // Call the event if it's defined
3307
+ if (RGraph.isFunction(obj.properties.lassoPersistLoad)) {
3308
+ data = obj.properties.lassoPersistLoad();
3309
+ }
3310
+
3311
+ return data;
3312
+ };
3313
+
3314
+ //
3315
+ // Reset the localStorageData
3316
+ //
3317
+ obj.lassoResetData = function ()
3318
+ {
3319
+ window.localStorage[localData_key] = obj.lassoGetDefaultObject();
3320
+ };
3321
+
3322
+
3323
+
3324
+
3325
+
3326
+
3327
+
3328
+
3329
+ //
3330
+ // Initialise the lasso state
3331
+ //
3332
+ RGraph.runOnce('scatter-chart-lasso-state-object-initialisation', function ()
3333
+ {
3334
+ //
3335
+ // Load the persistence data from the
3336
+ // localData array. Create an
3337
+ // obj.lassoLoadData() function.
3338
+ if (obj.properties.lassoPersist) {
3339
+ obj.lasso = {state: obj.lassoLoadData()};
3340
+
3341
+ } else {
3342
+ // This is repeated in the doubleclick/clear function below
3343
+ obj.lasso = obj.lassoGetDefaultObject();
3344
+
3345
+ }
3346
+ });
3347
+
3348
+
3349
+
3350
+
3351
+
3352
+
3353
+
3354
+
3355
+ //
3356
+ // A function that draws the highlight
3357
+ //
3358
+ this.drawLassoRects = function ()
3359
+ {
3360
+ if (!obj.lasso || !obj.lasso.state) {
3361
+ return;
3362
+ }
3363
+
3364
+ //
3365
+ // Loop through the coords
3366
+ //
3367
+
3368
+ for (let i=0; i<obj.lasso.state.coords.length; ++i) {
3369
+
3370
+ if (obj.lasso.state.coords[i]) {
3371
+ //
3372
+ // Start the path
3373
+ //
3374
+ obj.path('b ss % fs % lw % lc square',
3375
+ obj.properties.lassoStroke,
3376
+ obj.properties.lassoFill,
3377
+ obj.properties.lassoLinewidth
3378
+ );
3379
+
3380
+ for (let j=0; j<obj.lasso.state.coords[i].length; ++j) {
3381
+
3382
+ obj.context.rect(
3383
+ obj.lasso.state.coords[i][0],
3384
+ obj.lasso.state.coords[i][1],
3385
+ obj.lasso.state.coords[i][2],
3386
+ obj.lasso.state.coords[i][3]
3387
+ );
3388
+ }
3389
+
3390
+ //
3391
+ // Finish the and fill/stroke it
3392
+ //
3393
+ obj.context.closePath();
3394
+ obj.context.fill();
3395
+ obj.context.stroke();
3396
+ }
3397
+ }
3398
+ };
3399
+
3400
+
3401
+
3402
+
3403
+
3404
+
3405
+
3406
+
3407
+ //
3408
+ // Highlightpoints that have been selected
3409
+ // by the lasso
3410
+ //
3411
+ obj.drawLassoHighlightPoints = function (x, y = null)
3412
+ {
3413
+ // An array of points datasets/indexes has been given
3414
+ if (RGraph.isArray(x)) {
3415
+
3416
+ for (let i=0; i<x.length; ++i) {
3417
+ obj.path(
3418
+ 'b lw % a % % % 0 6.29 false s % f %',
3419
+ obj.properties.lassoHighlightLinewidth,
3420
+ obj.coords[x[i].dataset][x[i].index][0],
3421
+ obj.coords[x[i].dataset][x[i].index][1],
3422
+ obj.properties.tickmarksSize / 2 + 2,
3423
+ obj.properties.lassoHighlightStroke,
3424
+ obj.properties.lassoHighlightFill
3425
+ );
3426
+ }
3427
+
3428
+ // An x/y coordinates combo has been given
3429
+ } else {
3430
+
3431
+ obj.path(
3432
+ 'b lw % a % % % 0 6.29 false s % f %',
3433
+ obj.properties.lassoHighlightLinewidth,
3434
+ x,
3435
+ y,
3436
+ obj.properties.tickmarksSize / 2 + 2,
3437
+ obj.properties.lassoHighlightStroke,
3438
+ obj.properties.lassoHighlightFill
3439
+ );
3440
+ }
3441
+ };
3442
+
3443
+
3444
+
3445
+
3446
+
3447
+
3448
+
3449
+
3450
+ //
3451
+ // Clear the lasso state
3452
+ //
3453
+ this.clearLassoState = function ()
3454
+ {
3455
+ if (confirm('Are you sure that you want to clear ALL of the highlight rectangles?')) {
3456
+
3457
+ // This is repeated above
3458
+ obj.lasso = obj.lassoGetDefaultObject();
3459
+
3460
+ //
3461
+ // Save the reset state to localData
3462
+ // if persistence is enabled
3463
+ //
3464
+ if (obj.properties.lassoPersist) {
3465
+ obj.lassoSaveData(obj.lasso.state);
3466
+ }
3467
+
3468
+ RGraph.redrawCanvas(obj.canvas);
3469
+
3470
+ // Call the user defined function if necessary
3471
+ if (RGraph.isFunction(obj.properties.lassoClearCallback)) {
3472
+ obj.properties.lassoClearCallback(obj.lasso.state);
3473
+ }
3474
+ }
3475
+ };
3476
+
3477
+
3478
+
3479
+
3480
+
3481
+
3482
+
3483
+
3484
+
3485
+
3486
+
3487
+
3488
+
3489
+
3490
+ //
3491
+ // This is the lasso mousedown event listener
3492
+ //
3493
+ this.lassoMousedownEventListener = function (e)
3494
+ {
3495
+ if (e.button === 2) {
3496
+ return;
3497
+ }
3498
+
3499
+ var [mouseX, mouseY] = RGraph.getMouseXY(e);
3500
+
3501
+ // Reset the state
3502
+ if (!obj.lasso || !obj.lasso.state) {
3503
+ obj.lasso = obj.lassoGetDefaultObject();
3504
+ }
3505
+ obj.lasso.state.mousedown = true;
3506
+ obj.lasso.state.coordsLastIndex = obj.lasso.state.coords.length;
3507
+
3508
+ // Create a new lasso coords array
3509
+ obj.lasso.state.originX = mouseX;
3510
+ obj.lasso.state.originY = mouseY;
3511
+ };
3512
+
3513
+
3514
+
3515
+
3516
+
3517
+
3518
+
3519
+
3520
+ //
3521
+ // This is the lasso mousemove event listener
3522
+ //
3523
+ obj.lassoMousemoveEventListener = function (e)
3524
+ {
3525
+ var [mouseX, mouseY] = RGraph.getMouseXY(e);
3526
+
3527
+ if ( obj.lasso
3528
+ && obj.lasso.state
3529
+ && obj.lasso.state.mousedown
3530
+ ) {
3531
+
3532
+ RGraph.redrawCanvas(obj.canvas);
3533
+
3534
+
3535
+ // Store the coordinates of the
3536
+ // rectngle such that the X/Y
3537
+ // coordinates are always the top
3538
+ // left corner
3539
+ obj.lasso.state.coords[obj.lasso.state.coordsLastIndex] = [
3540
+ mouseX > obj.lasso.state.originX ? obj.lasso.state.originX : mouseX,
3541
+ mouseY > obj.lasso.state.originY ? obj.lasso.state.originY : mouseY,
3542
+ Math.abs(mouseX - obj.lasso.state.originX),
3543
+ Math.abs(mouseY - obj.lasso.state.originY)
3544
+ ];
3545
+
3546
+ // Set the strokestyle and the fillstyle
3547
+ obj.path('b ss % fs % lw %',
3548
+ obj.properties.lassoStroke,
3549
+ obj.properties.lassoFill,
3550
+ obj.properties.lassoLinewidth
3551
+ );
3552
+
3553
+ // Highlight the already-selected points
3554
+ obj.drawLassoRects();
3555
+ obj.drawLassoHighlightPoints(obj.lasso.state.points);
3556
+ }
3557
+ };
3558
+
3559
+
3560
+
3561
+
3562
+
3563
+
3564
+
3565
+
3566
+ //
3567
+ // This is the lasso mouseup event listener - where
3568
+ // the magic happens!
3569
+ //
3570
+ this.lassoWindowMouseupEventListener = function (e)
3571
+ {
3572
+ if (obj.lasso && obj.lasso.state && obj.lasso.state.mousedown) {
3573
+
3574
+ obj.lasso.state.mousedown = false;
3575
+
3576
+ // Start a new path for the
3577
+ // rectangles that will be used to
3578
+ // test the points
3579
+ obj.path('b');
3580
+
3581
+ for (let i=0; i<obj.lasso.state.coords.length; ++i) {
3582
+ if (RGraph.isArray(obj.lasso.state.coords[i])) {
3583
+ obj.path(
3584
+ 'r % % % % f % s %',
3585
+ obj.lasso.state.coords[i][0],
3586
+ obj.lasso.state.coords[i][1],
3587
+ obj.lasso.state.coords[i][2],
3588
+ obj.lasso.state.coords[i][3],
3589
+ 'transparent',
3590
+ 'transparent'
3591
+ );
3592
+ }
3593
+ }
3594
+
3595
+ // Reset the points array to stop it growing
3596
+ // uncontrollably
3597
+ obj.lasso.state.points = [];
3598
+
3599
+ // Determine all of the relevant points
3600
+ for (let i=0; i<obj.coords.length; ++i) {
3601
+ for (let j=0; j<obj.coords[i].length; ++j) {
3602
+
3603
+
3604
+ var [valueX, valueY] = obj.data[i][j];
3605
+ var coordX = obj.getXCoord(valueX);
3606
+ var coordY = obj.getYCoord(valueY);
3607
+
3608
+ if (obj.context.isPointInPath(coordX, coordY)) {
3609
+ obj.lasso.state.points = obj.lasso.state.points || [];
3610
+ obj.lasso.state.points.push({
3611
+ dataset: i,
3612
+ index: j
3613
+ });
3614
+ }
3615
+ }
3616
+ }
3617
+
3618
+ // Now highlight the selected points
3619
+ if (obj.lasso.state.points) {
3620
+
3621
+ for (let i=0; i<obj.lasso.state.points.length; ++i) {
3622
+
3623
+ var dataset = obj.lasso.state.points[i].dataset;
3624
+ var index = obj.lasso.state.points[i].index;
3625
+
3626
+ obj.drawLassoHighlightPoints(
3627
+ obj.coords[dataset][index][0],
3628
+ obj.coords[dataset][index][1]
3629
+ );
3630
+ }
3631
+ }
3632
+
3633
+
3634
+ //
3635
+ // Save the highlight data to
3636
+ // localStorage
3637
+ //
3638
+ if (obj.properties.lassoPersist) {
3639
+ obj.lassoSaveData(obj.lasso.state);
3640
+ }
3641
+
3642
+
3643
+
3644
+
3645
+
3646
+ // Call the callback function
3647
+ if (RGraph.isFunction(obj.properties.lassoCallback)) {
3648
+ obj.properties.lassoCallback(obj.lasso.state);
3649
+ }
3650
+ }
3651
+ };
3652
+
3653
+
3654
+
3655
+
3656
+
3657
+
3658
+
3659
+
3660
+ //
3661
+ // Allow people to clear the lasso state using
3662
+ // a double-click
3663
+ //
3664
+ this.lassoDblclickEventListener = function (e)
3665
+ {
3666
+ // Go through all of the rects to see if one
3667
+ // has been clicked and if so - remove it.
3668
+ if (obj.lasso.state.coords && obj.lasso.state.coords.length) {
3669
+
3670
+ var [mouseX, mouseY] = RGraph.getMouseXY(e);
3671
+
3672
+ for (let i=(obj.lasso.state.coords.length - 1); i>=0; --i) {
3673
+
3674
+ if ( RGraph.isArray(obj.lasso.state.coords)
3675
+ && obj.lasso.state.coords.length
3676
+ && obj.lasso.state.coords[i]
3677
+ && mouseX >= obj.lasso.state.coords[i][0]
3678
+ && mouseX <= (obj.lasso.state.coords[i][0] + obj.lasso.state.coords[i][2])
3679
+ && mouseY >= obj.lasso.state.coords[i][1]
3680
+ && mouseY <= (obj.lasso.state.coords[i][1] + obj.lasso.state.coords[i][3])
3681
+ ) {
3682
+ if (confirm('Are you sure that you want to remove this highlight rectangle?')) {
3683
+ obj.lasso.state.coords[i] = null;
3684
+
3685
+ // Call the window mouseup
3686
+ // event listener to
3687
+ // recalulate the points which
3688
+ // should be highlighted
3689
+ obj.lasso.state.mousedown = true;
3690
+ obj.lassoWindowMouseupEventListener(e);
3691
+
3692
+ //RGraph.redrawCanvas(obj.canvas);
3693
+ RGraph.redraw();
3694
+ }
3695
+ return;
3696
+ }
3697
+ }
3698
+ }
3699
+
3700
+
3701
+
3702
+ obj.clearLassoState();
3703
+ };
3704
+
3705
+
3706
+
3707
+
3708
+
3709
+
3710
+
3711
+
3712
+ // Install the above event listeners
3713
+ RGraph.runOnce('rgraph-install-scatter-chart-lasso-event-listeners', function ()
3714
+ {
3715
+ obj.canvas.addEventListener('mousedown', obj.lassoMousedownEventListener, false);
3716
+ obj.canvas.addEventListener('mousemove', obj.lassoMousemoveEventListener, false);
3717
+ obj.canvas.addEventListener('dblclick', obj.lassoDblclickEventListener, false);
3718
+ window.addEventListener('mouseup', obj.lassoWindowMouseupEventListener, false);
3719
+ });
3720
+
3721
+ // If the mouse button isn't pressed then redraw the
3722
+ // rectangles and point highlights
3723
+ if (
3724
+ this.lasso
3725
+ && this.lasso.state
3726
+ && !this.lasso.state.mousedown
3727
+ ) {
3728
+
3729
+ this.drawLassoRects();
3730
+ this.drawLassoHighlightPoints(this.lasso.state.points);
3731
+ }
3732
+ }
3733
+ };
3734
+
3735
+
3736
+
3737
+
3738
+
3739
+
3740
+
3741
+
3742
+ //
3743
+ // Draws a Marimekko chart
3744
+ //
3745
+ this.drawMarimekko = function ()
3746
+ {
3747
+ var data = RGraph.arrayClone(this.properties.marimekkoData);
3748
+
3749
+ // Calculate the total of all the X values
3750
+ for (var i=0,totalX=0; i<data.length; ++i) {
3751
+ totalX += data[i][0];
3752
+ }
3753
+
3754
+
3755
+ var graphWidth = this.canvas.width - this.properties.marginLeft - this.properties.marginRight;
3756
+ var graphHeight = this.canvas.height - this.properties.marginTop - this.properties.marginBottom;
3757
+ var x = this.properties.marginLeft;
3758
+ var coords = [];
3759
+
3760
+
3761
+
3762
+
3763
+
3764
+
3765
+
3766
+
3767
+ ////////////////////////////////
3768
+ // Draw the data on the chart //
3769
+ ////////////////////////////////
3770
+ for (var i=0,seq=0; i<data.length; ++i) {
3771
+
3772
+ var width = (data[i][0] / totalX) * graphWidth;
3773
+ var y = this.canvas.height - this.properties.marginBottom;
3774
+ coords[i] = [];
3775
+
3776
+ // Calulate the total Y value
3777
+ for (var j=0,totalY=0; j<data[i][1].length; ++j) {
3778
+ totalY += data[i][1][j];
3779
+ }
3780
+
3781
+ // Loop through the vertical values
3782
+ for (var j=0; j<data[i][1].length; ++j) {
3783
+
3784
+ var value = data[i][1][j];
3785
+ var height = (value / totalY) * graphHeight;
3786
+ var pc = (value / RGraph.arraySum(data[i][1])) * 100;
3787
+
3788
+ this.path(
3789
+ 'b r % % % % lw % s % f %',
3790
+ x, y - height, width, height,
3791
+ this.properties.marimekkoLinewidth,
3792
+ this.properties.marimekkoLinewidth ? this.properties.marimekkoColorsStroke : 'transparent',
3793
+ this.properties.marimekkoColorsSequential ? this.properties.marimekkoColors[seq] : this.properties.marimekkoColors[j]
3794
+ );
3795
+
3796
+ coords[i].push([x, y - height, width, height]);
3797
+
3798
+
3799
+
3800
+ //
3801
+ // Draw the ingraph label if requested
3802
+ //
3803
+ if (this.properties.marimekkoLabelsIngraph) {
3804
+
3805
+ if (!marimekkoLabelsIngraphTextConf) {
3806
+
3807
+ var marimekkoLabelsIngraphTextConf = RGraph.getTextConf({
3808
+ object: this,
3809
+ prefix: 'marimekkoLabelsIngraph'
3810
+ });
3811
+ }
3812
+
3813
+ var text = (this.properties.marimekkoLabelsIngraphSpecific && RGraph.isString(this.properties.marimekkoLabelsIngraphSpecific[seq]) ) ? (this.properties.marimekkoLabelsIngraphSpecific[seq] || '') : RGraph.numberFormat({
3814
+ object: this,
3815
+ number: Number(pc).toFixed(this.properties.marimekkoLabelsIngraphDecimals),
3816
+ value: Number(pc).toFixed(this.properties.marimekkoLabelsIngraphDecimals),
3817
+ unitspre: this.properties.marimekkoLabelsIngraphUnitsPre,
3818
+ unitspost: this.properties.marimekkoLabelsIngraphUnitsPost,
3819
+ point: this.properties.marimekkoLabelsIngraphPoint,
3820
+ thousand: this.properties.marimekkoLabelsIngraphThousand
3821
+ });
3822
+
3823
+ seq++;
3824
+
3825
+
3826
+
3827
+
3828
+
3829
+ RGraph.text({
3830
+ object: this,
3831
+ text: text,
3832
+ x: x + (width / 2) + this.properties.marimekkoLabelsIngraphOffsetx,
3833
+ y: y - (height / 2) + 5 + this.properties.marimekkoLabelsIngraphOffsety,
3834
+ color: marimekkoLabelsIngraphTextConf.color,
3835
+ italic: marimekkoLabelsIngraphTextConf.italic,
3836
+ bold: marimekkoLabelsIngraphTextConf.bold,
3837
+ font: marimekkoLabelsIngraphTextConf.font,
3838
+ size: marimekkoLabelsIngraphTextConf.size,
3839
+ halign: 'center',
3840
+ valign: 'center',
3841
+ tag: 'marimekkoLabelsIngraph',
3842
+ bounding: true,
3843
+ 'bounding.stroke': this.properties.marimekkoLabelsIngraphBackgroundStroke,
3844
+ 'bounding.fill': this.properties.marimekkoLabelsIngraphBackgroundFill
3845
+ });
3846
+ }
3847
+
3848
+
3849
+
3850
+
3851
+
3852
+
3853
+ y -= height;
3854
+ }
3855
+
3856
+
3857
+
3858
+
3859
+
3860
+ //
3861
+ // Draw the regular (horizonatal) Marimekko
3862
+ // label if it's been given
3863
+ //
3864
+ if (this.properties.marimekkoLabels) {
3865
+
3866
+
3867
+ var textConf = RGraph.getTextConf({
3868
+ object: this,
3869
+ prefix: 'marimekkoLabels'
3870
+ });
3871
+
3872
+ // Get the label
3873
+ if (RGraph.isString(this.properties.marimekkoLabels)) {
3874
+ var label = this.properties.marimekkoLabels;
3875
+ } else if (RGraph.isArray(this.properties.marimekkoLabels)) {
3876
+ var label = this.properties.marimekkoLabels[i];
3877
+ } else {
3878
+ var label = '';
3879
+ }
3880
+
3881
+ // Now do substitution on the label
3882
+ var label = RGraph.labelSubstitution({
3883
+ index: i,
3884
+ text: label || '',
3885
+ object: this,
3886
+ value: RGraph.arraySum(data[i][1]),
3887
+ decimals: this.properties.marimekkoLabelsFormattedDecimals,
3888
+ point: this.properties.marimekkoLabelsFormattedPoint,
3889
+ thousand: this.properties.marimekkoLabelsFormattedThousand,
3890
+ unitsPre: this.properties.marimekkoLabelsFormattedUnitsPre,
3891
+ unitsPost: this.properties.marimekkoLabelsFormattedUnitsPost
3892
+ });
3893
+
3894
+ RGraph.text({
3895
+ object: this,
3896
+ x: coords[i].at(-1)[0] + (coords[i].at(-1)[2] / 2) + this.properties.marimekkoLabelsOffsetx,
3897
+ y: coords[i].at(-1)[1] - 5 + this.properties.marimekkoLabelsOffsety,
3898
+ text: label,
3899
+ color: textConf.color,
3900
+ font: textConf.font,
3901
+ size: textConf.size,
3902
+ bold: textConf.bold,
3903
+ italic: textConf.italic,
3904
+ halign: 'center',
3905
+ valign: 'bottom'
3906
+ });
3907
+ }
3908
+ x += width;
3909
+ }
3910
+
3911
+
3912
+
3913
+
3914
+
3915
+ //
3916
+ // Store the coordinates
3917
+ //
3918
+ this.coordsMarimekko = coords;
3919
+
3920
+ return this;
3921
+ };
3922
+
3923
+
3924
+
3925
+
3926
+
3927
+
3928
+
3929
+
3930
+ //
3931
+ // This method handles the adjusting calculation for
3932
+ // when the mouse is moved
3933
+ //
3934
+ // @param object e The event object
3935
+ //
3936
+ this.adjusting_mousemove = function (e)
3937
+ {
3938
+ //
3939
+ // Handle adjusting for the Bar
3940
+ //
3941
+ if (properties.adjustable && RGraph.Registry.get('adjusting') && RGraph.Registry.get('adjusting').uid == this.uid) {
3942
+
3943
+ var shape = RGraph.Registry.get('adjusting.shape');
3944
+
3945
+ if (shape) {
3946
+
3947
+ RGraph.Registry.set('adjusting.shape', shape);
3948
+
3949
+ var x = this.getXValue(e);
3950
+ var y = this.getYValue(e);
3951
+ var [mouseX, mouseY] = RGraph.getMouseXY(e);
3952
+
3953
+
3954
+ // Y coordinate bounding
3955
+ if (mouseY <= shape.object.properties.marginTop) {
3956
+ y = shape.object.scale2.max;
3957
+ } else if (mouseY > (shape.object.canvas.height - shape.object.properties.marginBottom) ) {
3958
+ y = shape.object.scale2.min;
3959
+
3960
+ }
3961
+
3962
+ // X coordinate bounding
3963
+ if (mouseX <= shape.object.properties.marginLeft) {
3964
+ x = shape.object.properties.xaxisScaleMin;
3965
+ } else if (mouseX > (shape.object.canvas.width - shape.object.properties.marginRight) ) {
3966
+ x = shape.object.properties.xaxisScaleMax;
3967
+ }
3968
+
3969
+
3970
+ // Set the new X and Y values
3971
+ this.data[shape.dataset][shape.index][0] = x;
3972
+ this.data[shape.dataset][shape.index][1] = y;
3973
+
3974
+ RGraph.redrawCanvas(e.target);
3975
+
3976
+ RGraph.fireCustomEvent(this, 'onadjust');
3977
+ }
3978
+ }
3979
+ };
3980
+
3981
+
3982
+
3983
+
3984
+
3985
+
3986
+
3987
+
3988
+ //
3989
+ // Determines whether a point is adjustable or not.
3990
+ //
3991
+ // @param object A shape object
3992
+ //
3993
+ this.isAdjustable = function (shape)
3994
+ {
3995
+ if (RGraph.isNull(properties.adjustableOnly)) {
3996
+ return true;
3997
+ }
3998
+
3999
+ if (shape && RGraph.isArray(properties.adjustableOnly) && properties.adjustableOnly[shape.sequentialIndex]) {
4000
+ return true;
4001
+ }
4002
+
4003
+ return false;
4004
+ };
4005
+
4006
+
4007
+
4008
+
4009
+
4010
+
4011
+
4012
+
4013
+ //
4014
+ // Register the object
4015
+ //
4016
+ RGraph.register(this);
4017
+
4018
+
4019
+
4020
+
4021
+
4022
+
4023
+
4024
+
4025
+ //
4026
+ // This is the 'end' of the constructor so if the first argument
4027
+ // contains configuration data - handle that.
4028
+ //
4029
+ RGraph.parseObjectStyleConfig(this, conf.options);
4030
+ };
4031
+
4032
+
4033
+
4034
+
4035
+
4036
+
4037
+
4038
+
4039
+ //
4040
+ // This is a shortcut-style function that makes making
4041
+ // drilldown chart much easier for the user.
4042
+ //
4043
+ RGraph.Scatter.drilldown = function (opt)
4044
+ {
4045
+ var original = {
4046
+ xaxisScaleMin: opt.options.xaxisScaleMin,
4047
+ xaxisScaleMax: opt.options.xaxisScaleMax
4048
+ };
4049
+
4050
+ //
4051
+ // Set the initial max and min for the scale
4052
+ //
4053
+ opt.options.xaxisScaleMin = opt.options.xaxisScaleMin;
4054
+ opt.options.xaxisScaleMax = opt.options.xaxisScaleMax;
4055
+
4056
+ // Create the Scatter chart (it's actually an XY line chart
4057
+ var obj = new RGraph.Scatter({
4058
+ id: opt.id,
4059
+ data: RGraph.arrayClone(opt.data),
4060
+ options: opt.options
4061
+ });
4062
+
4063
+ if (opt.options.animation && typeof obj[opt.options.animation] === 'function') {
4064
+ obj[opt.options.animation]();
4065
+ } else {
4066
+ obj.draw();
4067
+ }
4068
+
4069
+ state = {};
4070
+
4071
+ // The mousedown listener
4072
+ obj.canvas.onmousedown = function (e)
4073
+ {
4074
+ if (e.button === 0 && obj.getXValue(e)) {
4075
+ var x = obj.getXValue(e);
4076
+
4077
+ state.start = x;
4078
+ }
4079
+ };
4080
+
4081
+ // The mouseup listener. This is where the magic
4082
+ // happens and the chart is updated.
4083
+ obj.canvas.onmouseup = function (e)
4084
+ {
4085
+ if (e.button === 0) {
4086
+ var x = obj.getXValue(e),
4087
+ min = Math.min(state.start, x),
4088
+ max = Math.max(state.start, x);
4089
+
4090
+ state.end = x;
4091
+
4092
+ obj.set({
4093
+ xaxisScaleMin: min,
4094
+ xaxisScaleMax: max
4095
+ });
4096
+
4097
+ obj.data[0].forEach(function (v, k, arr)
4098
+ {
4099
+ if (v[0] < min || v[0] > max) {
4100
+ arr[k] = [];
4101
+ }
4102
+ });
4103
+
4104
+ obj.set({
4105
+ contextmenu: [['Reset', function ()
4106
+ {
4107
+ obj.set({
4108
+ xaxisScaleMin: original.xaxisScaleMin,
4109
+ xaxisScaleMax: original.xaxisScaleMax,
4110
+ xaxisLabels: RGraph.arrayClone(opt.options.xaxisLabels)
4111
+ });
4112
+
4113
+ obj.data[0] = RGraph.arrayClone(opt.data);
4114
+
4115
+ RGraph.redraw();
4116
+ }]]
4117
+ });
4118
+
4119
+ // Set the labels
4120
+ var newlabels = [];
4121
+
4122
+ opt.options.xaxisLabels.forEach(function (v, k, arr)
4123
+ {
4124
+ if (v[1] > min && v[1] < max) {
4125
+ newlabels.push(v);
4126
+ }
4127
+ });
4128
+ obj.set('xaxisLabels', newlabels);
4129
+
4130
+ RGraph.redraw();
4131
+
4132
+ state = {};
4133
+ }
4134
+ };
4135
+
4136
+ // The mousemove handler. This simply highlights from
4137
+ // the mousedown start point to the current position.
4138
+ obj.canvas.onmousemove = function (e)
4139
+ {
4140
+ if (typeof state.start === 'number' && !state.end) {
4141
+
4142
+ var coordX1 = obj.getXCoord(state.start);
4143
+ var coordX2 = obj.getXCoord(obj.getXValue(e));
4144
+
4145
+ RGraph.redraw();
4146
+
4147
+ obj.context.fillStyle = 'rgba(0,0,0,0.15)';
4148
+ obj.context.fillRect(
4149
+ coordX1,
4150
+ obj.properties.marginTop,
4151
+ coordX2 - coordX1,
4152
+ obj.canvas.height - obj.properties.marginTop - obj.properties.marginBottom
4153
+ );
4154
+ }
4155
+ };
4156
+
4157
+ return obj;
4158
+ };
4159
+
4160
+
4161
+
4162
+
4163
+
4164
+
4165
+
4166
+
4167
+ //
4168
+ // This is a place holder - it doesn't do anything
4169
+ //
4170
+ RGraph.Scatter.drilldown.draw = function (options)
4171
+ {
4172
+ return RGraph.Scatter.drilldown(options);
4173
+ };
4174
+
4175
+
4176
+
4177
+
4178
+
4179
+
4180
+
4181
+
4182
+ //
4183
+ // This is a function that facilitates creating a
4184
+ // Marimekko chart.
4185
+ //
4186
+ RGraph.Scatter.Marimekko = function (opt)
4187
+ {
4188
+ this.options = opt.options;
4189
+ this.data = opt.data;
4190
+ this.id = opt.id;
4191
+
4192
+ //
4193
+ // The draw function for the Marimekko chart
4194
+ //
4195
+ this.draw = function ()
4196
+ {
4197
+ return new RGraph.Scatter({
4198
+ id: this.id,
4199
+ data: opt.data,
4200
+ options: {
4201
+ xaxisScaleMax: 100,
4202
+ yaxisScaleMax: 100,
4203
+ backgroundGrid: false,
4204
+ xaxis: false,
4205
+ yaxis: false,
4206
+ yaxisScaleUnitsPost: '%',
4207
+ ...this.options
4208
+ }
4209
+ }).exec(function (obj)
4210
+ {
4211
+ // Set this flag so that we can tell if this
4212
+ // is a marimekko chart
4213
+ obj.isMarimekko = true;
4214
+ }).draw();
4215
+ };
4216
+ };