rgraph-rails 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/license.txt +4 -16
  5. data/vendor/assets/javascripts/RGraph.bar.js +3734 -241
  6. data/vendor/assets/javascripts/RGraph.bipolar.js +2005 -115
  7. data/vendor/assets/javascripts/RGraph.common.annotate.js +395 -35
  8. data/vendor/assets/javascripts/RGraph.common.context.js +595 -30
  9. data/vendor/assets/javascripts/RGraph.common.core.js +5282 -405
  10. data/vendor/assets/javascripts/RGraph.common.csv.js +276 -19
  11. data/vendor/assets/javascripts/RGraph.common.deprecated.js +450 -35
  12. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1395 -86
  13. data/vendor/assets/javascripts/RGraph.common.effects.js +1545 -90
  14. data/vendor/assets/javascripts/RGraph.common.key.js +753 -54
  15. data/vendor/assets/javascripts/RGraph.common.resizing.js +563 -37
  16. data/vendor/assets/javascripts/RGraph.common.sheets.js +352 -29
  17. data/vendor/assets/javascripts/RGraph.common.tooltips.js +450 -32
  18. data/vendor/assets/javascripts/RGraph.common.zoom.js +219 -14
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +570 -35
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +544 -35
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +755 -52
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +645 -41
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +633 -37
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +514 -36
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +559 -39
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +548 -35
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +664 -36
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +812 -50
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +856 -51
  30. data/vendor/assets/javascripts/RGraph.fuel.js +964 -58
  31. data/vendor/assets/javascripts/RGraph.funnel.js +984 -55
  32. data/vendor/assets/javascripts/RGraph.gantt.js +1354 -77
  33. data/vendor/assets/javascripts/RGraph.gauge.js +1421 -87
  34. data/vendor/assets/javascripts/RGraph.hbar.js +2562 -146
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +1401 -80
  36. data/vendor/assets/javascripts/RGraph.line.js +4226 -244
  37. data/vendor/assets/javascripts/RGraph.meter.js +1280 -74
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +301 -19
  39. data/vendor/assets/javascripts/RGraph.odo.js +1264 -71
  40. data/vendor/assets/javascripts/RGraph.pie.js +2288 -137
  41. data/vendor/assets/javascripts/RGraph.radar.js +1847 -110
  42. data/vendor/assets/javascripts/RGraph.rose.js +1977 -108
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +1432 -80
  44. data/vendor/assets/javascripts/RGraph.scatter.js +3036 -168
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1120 -60
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +1067 -0
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +247 -0
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +3363 -0
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +277 -0
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1304 -0
  51. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +353 -0
  52. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +233 -0
  53. data/vendor/assets/javascripts/RGraph.svg.hbar.js +1141 -0
  54. data/vendor/assets/javascripts/RGraph.svg.line.js +1486 -0
  55. data/vendor/assets/javascripts/RGraph.svg.pie.js +781 -0
  56. data/vendor/assets/javascripts/RGraph.svg.radar.js +1326 -0
  57. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +817 -0
  58. data/vendor/assets/javascripts/RGraph.thermometer.js +1135 -62
  59. data/vendor/assets/javascripts/RGraph.vprogress.js +1470 -83
  60. data/vendor/assets/javascripts/RGraph.waterfall.js +1347 -80
  61. metadata +15 -3
@@ -1,169 +1,3037 @@
1
+ // version: 2017-01-02
2
+ /**
3
+ * o--------------------------------------------------------------------------------o
4
+ * | This file is part of the RGraph package - you can learn more at: |
5
+ * | |
6
+ * | http://www.rgraph.net |
7
+ * | |
8
+ * | RGraph is licensed under the Open Source MIT license. That means that it's |
9
+ * | totally free to use! |
10
+ * o--------------------------------------------------------------------------------o
11
+ */
1
12
 
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
- 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;}}
5
- 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;}}}}
6
- 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.properties={'chart.background.barcolor1':'rgba(0,0,0,0)','chart.background.barcolor2':'rgba(0,0,0,0)','chart.background.grid':true,'chart.background.grid.width':1,'chart.background.grid.color':'#ddd','chart.background.grid.hsize':20,'chart.background.grid.vsize':20,'chart.background.hbars':null,'chart.background.vbars':null,'chart.background.grid.vlines':true,'chart.background.grid.hlines':true,'chart.background.grid.border':true,'chart.background.grid.autofit':true,'chart.background.grid.autofit.align':true,'chart.background.grid.autofit.numhlines':5,'chart.background.grid.autofit.numvlines':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.text.size':12,'chart.text.angle':0,'chart.text.color':'black','chart.text.font':'Segoe UI, Arial, Verdana, sans-serif','chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':true,'chart.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.units.pre':'','chart.units.post':'','chart.numyticks':10,'chart.tickmarks':'cross','chart.tickmarks.image.halign':'center','chart.tickmarks.image.valign':'center','chart.tickmarks.image.offsetx':0,'chart.tickmarks.image.offsety':0,'chart.ticksize':5,'chart.numxticks':true,'chart.xaxis':true,'chart.gutter.left':25,'chart.gutter.right':25,'chart.gutter.top':25,'chart.gutter.bottom':30,'chart.colors.bubble.graduated':true,'chart.xmin':0,'chart.xmax':0,'chart.ymax':null,'chart.ymin':0,'chart.scale.decimals':0,'chart.scale.point':'.','chart.scale.thousand':',','chart.scale.zerostart':true,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.bold':true,'chart.title.font':null,'chart.title.xaxis':'','chart.title.xaxis.bold':true,'chart.title.xaxis.size':null,'chart.title.xaxis.font':null,'chart.title.xaxis.color':null,'chart.title.yaxis':'','chart.title.yaxis.bold':true,'chart.title.yaxis.size':null,'chart.title.yaxis.font':null,'chart.title.yaxis.color':null,'chart.title.xaxis.pos':null,'chart.title.yaxis.pos':null,'chart.title.yaxis.x':null,'chart.title.yaxis.y':null,'chart.title.xaxis.x':null,'chart.title.xaxis.y':null,'chart.title.x':null,'chart.title.y':null,'chart.title.halign':null,'chart.title.valign':null,'chart.labels':[],'chart.labels.bold':false,'chart.labels.color':null,'chart.labels.ingraph':null,'chart.labels.above':false,'chart.labels.above.size':8,'chart.labels.above.decimals':0,'chart.labels.offsetx':0,'chart.labels.offsety':0,'chart.ylabels.offsetx':0,'chart.ylabels.offsety':0,'chart.ylabels':true,'chart.ylabels.count':5,'chart.ylabels.invert':false,'chart.ylabels.specific':null,'chart.ylabels.inside':false,'chart.contextmenu':null,'chart.defaultcolor':'black','chart.xaxispos':'bottom','chart.yaxispos':'left','chart.crosshairs':false,'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.hline':true,'chart.crosshairs.vline':true,'chart.annotatable':false,'chart.annotate.color':'black','chart.line':false,'chart.line.linewidth':1,'chart.line.colors':['green','red'],'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.noaxes':false,'chart.noyaxis':false,'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.text.color':'black','chart.axis.color':'black','chart.zoom.factor':1.5,'chart.zoom.fade.in':true,'chart.zoom.fade.out':true,'chart.zoom.hdir':'right','chart.zoom.vdir':'down','chart.zoom.frames':25,'chart.zoom.delay':16.666,'chart.zoom.shadow':true,'chart.zoom.background':true,'chart.zoom.action':'zoom','chart.boxplot.width':1,'chart.boxplot.capped':true,'chart.resizable':false,'chart.resize.handle.background':null,'chart.xmin':0,'chart.labels.specific.align':'left','chart.xscale':false,'chart.xscale.units.pre':'','chart.xscale.units.post':'','chart.xscale.numlabels':10,'chart.xscale.formatter':null,'chart.xscale.decimals':0,'chart.xscale.thousand':',','chart.xscale.point':'.','chart.noendxtick':false,'chart.noendytick':true,'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}
7
- 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]=[];}
8
- if(this.data[i][j]&&typeof(this.data[i][j][0])=='string'){this.data[i][j][0]=RGraph.parseDate(this.data[i][j][0]);}}}
9
- 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]);}}
10
- for(var i=0;i<this.data_arr.length;++i){this['$'+i]={}}
11
- if(!this.canvas){alert('[SCATTER] No canvas support');return;}
12
- if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
13
- var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
14
- if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
15
- this.set=this.Set=function(name)
16
- {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
17
- if(name.substr(0,6)!='chart.'){name='chart.'+name;}
18
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
19
- if(name=='chart.xticks'){name=='chart.numxticks';}
20
- if(name=='chart.line.colors'){prop['chart.colors']=value;}
21
- if(name=='chart.tooltip.hotspot'){name='chart.tooltips.hotspot';}
22
- if(name=='chart.yaxispos'&&value!='left'&&value!='right'){alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '"+value+"' Changing it to left");value='left';}
23
- if(name=='chart.xaxispos'){if(value!='bottom'&&value!='center'){alert('[SCATTER] ('+this.id+') chart.xaxispos should be center or bottom. Tried to set it to: '+value+' Changing it to center');value='center';}}
24
- if(name=='chart.noxaxis'){name='chart.xaxis';value=!value;}
25
- prop[name.toLowerCase()]=value;return this;};this.get=this.Get=function(name)
26
- {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
27
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
28
- return prop[name];};this.draw=this.Draw=function()
29
- {if(typeof prop['chart.background.image']==='string'){RG.DrawBackgroundImage(this);}
30
- RG.fireCustomEvent(this,'onbeforedraw');if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
31
- this.coordsText=[];this.gutterLeft=prop['chart.gutter.left'];this.gutterRight=prop['chart.gutter.right'];this.gutterTop=prop['chart.gutter.top'];this.gutterBottom=prop['chart.gutter.bottom'];this.hasTooltips=false;var overHotspot=false;this.coords=[];if(typeof(prop['chart.xmin'])=='string')prop['chart.xmin']=RG.parseDate(prop['chart.xmin']);if(typeof(prop['chart.xmax'])=='string')prop['chart.xmax']=RG.parseDate(prop['chart.xmax']);if(!RGraph.ISOLD){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);}}}}
32
- this.max=0;if(typeof prop['chart.ymax']==='number'){this.max=prop['chart.ymax'];this.min=prop['chart.ymin']?prop['chart.ymin']:0;this.scale2=RG.getScale2(this,{'max':this.max,'min':this.min,'strict':true,'scale.thousand':prop['chart.scale.thousand'],'scale.point':prop['chart.scale.point'],'scale.decimals':prop['chart.scale.decimals'],'ylabels.count':prop['chart.ylabels.count'],'scale.round':prop['chart.scale.round'],'units.pre':prop['chart.units.pre'],'units.post':prop['chart.units.post']});this.max=this.scale2.max;this.min=this.scale2.min;var decimals=prop['chart.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]));}}}
33
- this.min=prop['chart.ymin']?prop['chart.ymin']:0;this.scale2=RG.getScale2(this,{'max':this.max,'min':this.min,'scale.thousand':prop['chart.scale.thousand'],'scale.point':prop['chart.scale.point'],'scale.decimals':prop['chart.scale.decimals'],'ylabels.count':prop['chart.ylabels.count'],'scale.round':prop['chart.scale.round'],'units.pre':prop['chart.units.pre'],'units.post':prop['chart.units.post']});this.max=this.scale2.max;this.min=this.scale2.min;}
34
- this.grapharea=ca.height-this.gutterTop-this.gutterBottom;RG.background.Draw(this);if(prop['chart.background.hbars']&&prop['chart.background.hbars'].length){RG.DrawBars(this);}
35
- if(prop['chart.background.vbars']&&prop['chart.background.vbars'].length){this.DrawVBars();}
36
- if(!prop['chart.noaxes']){this.DrawAxes();}
37
- 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();}
38
- 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);}
39
- if(prop['chart.line']){for(var i=0,len=this.data.length;i<len;i+=1){this.DrawMarks(i);}}
40
- if(prop['chart.animation.trace']){co.restore();}
41
- if(prop['chart.contextmenu']){RG.ShowContext(this);}
42
- if(prop['chart.key']&&prop['chart.key'].length){RG.DrawKey(this,prop['chart.key'],prop['chart.line.colors']);}
43
- if(prop['chart.labels.above']){this.DrawAboveLabels();}
44
- this.DrawInGraphLabels(this);if(prop['chart.resizable']){RG.AllowResizing(this);}
45
- RG.InstallEventListeners(this);if(this.firstDraw){RG.fireCustomEvent(this,'onfirstdraw');this.firstDraw=false;this.firstDrawFunc();}
46
- RG.FireCustomEvent(this,'ondraw');return this;}
47
- this.drawAxes=this.DrawAxes=function()
48
- {var graphHeight=ca.height-this.gutterTop-this.gutterBottom;co.beginPath();co.strokeStyle=prop['chart.axis.color'];co.lineWidth=(prop['chart.axis.linewidth']||1)+0.001;if(prop['chart.noyaxis']==false){if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,this.gutterTop);co.lineTo(this.gutterLeft,ca.height-this.gutterBottom);}else{co.moveTo(ca.width-this.gutterRight,this.gutterTop);co.lineTo(ca.width-this.gutterRight,ca.height-this.gutterBottom);}}
49
- if(prop['chart.xaxis']){if(prop['chart.xaxispos']=='center'){co.moveTo(this.gutterLeft,Math.round(this.gutterTop+((ca.height-this.gutterTop-this.gutterBottom)/2)));co.lineTo(ca.width-this.gutterRight,Math.round(this.gutterTop+((ca.height-this.gutterTop-this.gutterBottom)/2)));}else{co.moveTo(this.gutterLeft,ca.height-this.gutterBottom);co.lineTo(ca.width-this.gutterRight,ca.height-this.gutterBottom);}}
50
- if(prop['chart.noyaxis']==false){var numyticks=prop['chart.numyticks'];for(i=0;i<numyticks;++i){var y=((ca.height-this.gutterTop-this.gutterBottom)/numyticks)*i;y=y+this.gutterTop;if(prop['chart.xaxispos']=='center'&&i==(numyticks/2)){continue;}
51
- if(prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,Math.round(y));co.lineTo(this.gutterLeft-3,Math.round(y));}else{co.moveTo(ca.width-this.gutterRight+3,Math.round(y));co.lineTo(ca.width-this.gutterRight,Math.round(y));}}
52
- if(prop['chart.numyticks']>0){if(prop['chart.xaxispos']=='center'&&prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,Math.round(ca.height-this.gutterBottom));co.lineTo(this.gutterLeft-3,Math.round(ca.height-this.gutterBottom));}else if(prop['chart.xaxispos']=='center'){co.moveTo(ca.width-this.gutterRight+3,Math.round(ca.height-this.gutterBottom));co.lineTo(ca.width-this.gutterRight,Math.round(ca.height-this.gutterBottom));}}
53
- if(prop['chart.xaxis']==false&&prop['chart.yaxispos']=='left'){co.moveTo(this.gutterLeft,Math.round(ca.height-this.gutterBottom));co.lineTo(this.gutterLeft-3,Math.round(ca.height-this.gutterBottom));}else if(prop['chart.xaxis']==false&&prop['chart.yaxispos']=='right'){co.moveTo(ca.width-this.gutterRight,Math.round(ca.height-this.gutterBottom));co.lineTo(ca.width-this.gutterRight+3,Math.round(ca.height-this.gutterBottom));}}
54
- if(prop['chart.numxticks']>0&&prop['chart.xaxis']){var x=0;var y=(prop['chart.xaxispos']=='center')?this.gutterTop+(this.grapharea/2):(ca.height-this.gutterBottom);this.xTickGap=(prop['chart.labels']&&prop['chart.labels'].length)?((ca.width-this.gutterLeft-this.gutterRight)/prop['chart.labels'].length):(ca.width-this.gutterLeft-this.gutterRight)/10;if(typeof(prop['chart.numxticks'])=='number'){this.xTickGap=(ca.width-this.gutterLeft-this.gutterRight)/prop['chart.numxticks'];}
55
- for(x=(this.gutterLeft+(prop['chart.yaxispos']=='left'&&prop['chart.noyaxis']==false?this.xTickGap:0));x<=(ca.width-this.gutterRight-(prop['chart.yaxispos']=='left'||prop['chart.noyaxis']==true?-1:1));x+=this.xTickGap){if(prop['chart.yaxispos']=='left'&&prop['chart.noendxtick']==true&&x==(ca.width-this.gutterRight)){continue;}else if(prop['chart.yaxispos']=='right'&&prop['chart.noendxtick']==true&&x==this.gutterLeft){continue;}
56
- co.moveTo(Math.round(x),y-(prop['chart.xaxispos']=='center'?3:0));co.lineTo(Math.round(x),y+3);}}
57
- co.stroke();co.lineWidth=1;};this.drawLabels=this.DrawLabels=function()
58
- {co.fillStyle=prop['chart.text.color'];var font=prop['chart.text.font'],xMin=prop['chart.xmin'],xMax=prop['chart.xmax'],yMax=this.scale2.max,yMin=prop['chart.ymin']?prop['chart.ymin']:0,text_size=prop['chart.text.size'],units_pre=prop['chart.units.pre'],units_post=prop['chart.units.post'],numYLabels=prop['chart.ylabels.count'],invert=prop['chart.ylabels.invert'],inside=prop['chart.ylabels.inside'],context=co,canvas=ca,boxed=false,offsetx=prop['chart.ylabels.offsetx'],offsety=prop['chart.ylabels.offsety']
59
- this.halfTextHeight=text_size/2;this.halfGraphHeight=(ca.height-this.gutterTop-this.gutterBottom)/2;if(prop['chart.ylabels']){var xPos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var align=prop['chart.yaxispos']=='right'?'left':'right';if(inside){if(prop['chart.yaxispos']=='left'){xPos=prop['chart.gutter.left']+5;align='left';boxed=true;}else{xPos=ca.width-prop['chart.gutter.right']-5;align='right';boxed=true;}}
60
- if(prop['chart.xaxispos']=='center'){if(typeof(prop['chart.ylabels.specific'])=='object'&&prop['chart.ylabels.specific']!=null&&prop['chart.ylabels.specific'].length){var labels=prop['chart.ylabels.specific'];if(prop['chart.ymin']>0){labels=[];for(var i=0;i<(prop['chart.ylabels.specific'].length-1);++i){labels.push(prop['chart.ylabels.specific'][i]);}}
61
- for(var i=0;i<labels.length;++i){var y=this.gutterTop+(i*(this.grapharea/(labels.length*2)));RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':y+offsety,'text':labels[i],'valign':'center','halign':align,'bounding':boxed,'tag':'labels.specific'});}
62
- var reversed_labels=RG.array_reverse(labels);for(var i=0;i<reversed_labels.length;++i){var y=this.gutterTop+(this.grapharea/2)+((i+1)*(this.grapharea/(labels.length*2)));RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':y+offsety,'text':reversed_labels[i],'valign':'center','halign':align,'bounding':boxed,'tag':'labels.specific'});}
63
- if(prop['chart.ymin']!=0){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':(this.grapharea/2)+this.gutterTop+offsety,'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length-1],'valign':'center','halign':align,'bounding':boxed,'tag':'labels.specific'});}}
64
- if(!prop['chart.ylabels.specific']&&typeof numYLabels=='number'){for(var i=0,len=this.scale2.labels.length;i<len;i+=1){if(!invert){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+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':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+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'});}}
65
- for(var i=0,len=this.scale2.labels.length;i<len;i+=1){if(!invert){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+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;}
66
- RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+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'});}}
67
- if(!invert&&(yMin>0||prop['chart.scale.zerostart'])){RG.text2(this,{font:font,size:text_size,x:xPos+offsetx,y:this.gutterTop+this.halfGraphHeight+offsety,valign:'center',halign:align,bounding:boxed,boundingFill:'white',text:RG.numberFormat(this,yMin.toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),tag:'scale'});}
68
- if(invert){RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.number_format(this,yMin.toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+(this.halfGraphHeight*2)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':'-'+RG.numberFormat(this,yMin.toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}}}else{var xPos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var align=prop['chart.yaxispos']=='right'?'left':'right';if(inside){if(prop['chart.yaxispos']=='left'){xPos=prop['chart.gutter.left']+5;align='left';boxed=true;}else{xPos=ca.width-this.gutterRight-5;align='right';boxed=true;}}
69
- if(typeof prop['chart.ylabels.specific']=='object'&&prop['chart.ylabels.specific']){var labels=prop['chart.ylabels.specific'];if(prop['chart.ymin']>9999){labels=[];for(var i=0;i<(prop['chart.ylabels.specific'].length-1);++i){labels.push(prop['chart.ylabels.specific'][i]);}}
70
- for(var i=0,len=labels.length;i<len;i+=1){var y=this.gutterTop+(i*(this.grapharea/(len-1)));RG.Text2(this,{'font':font,'size':text_size,'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.gutterTop-this.gutterBottom)/numYLabels;RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+((i+1)*interval)+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':this.scale2.labels[i],'tag':'scale'});}
71
- if(!prop['chart.xaxis']&&!prop['chart.ymin']){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.numberFormat(this,(this.min).toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}}else{for(var i=0,len=this.scale2.labels.length;i<len;i+=1){RG.Text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+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'});}
72
- if(!prop['chart.xaxis']&&prop['chart.ymin']==0){RG.text2(this,{font:font,size:text_size,x:xPos+offsetx,y:ca.height-this.gutterBottom+offsety,valign:'center',halign:align,boundin:boxed,boundingFill:'white',text:RG.numberFormat(this,(0).toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),tag:'scale'});}}}
73
- if((prop['chart.ymin']||prop['chart.scale.zerostart'])&&!invert){RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':ca.height-this.gutterBottom+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.numberFormat(this,prop['chart.ymin'].toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}else if(invert){RG.text2(this,{'font':font,'size':text_size,'x':xPos+offsetx,'y':this.gutterTop+offsety,'valign':'center','halign':align,'bounding':boxed,'boundingFill':'white','text':RG.numberFormat(this,prop['chart.ymin'].toFixed(this.scale2.min===0?0:prop['chart.scale.decimals']),units_pre,units_post),'tag':'scale'});}}}}
74
- if(prop['chart.xscale']){var numXLabels=prop['chart.xscale.numlabels'],y=ca.height-this.gutterBottom+5+(text_size/2),units_pre_x=prop['chart.xscale.units.pre'],units_post_x=prop['chart.xscale.units.post'],decimals=prop['chart.xscale.decimals'],point=prop['chart.xscale.point'],thousand=prop['chart.xscale.thousand'],color=prop['chart.labels.color'],bold=prop['chart.labels.bold'],offsetx=prop['chart.labels.offsetx'],offsety=prop['chart.labels.offsety']
75
- if(!prop['chart.xmax']){var xmax=0;var xmin=prop['chart.xmin'];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.xmax'];xmin=prop['chart.xmin']}
76
- this.xscale2=RG.getScale2(this,{'max':xmax,'min':xmin,'scale.decimals':decimals,'scale.point':point,'scale.thousand':thousand,'units.pre':units_pre_x,'units.post':units_post_x,'ylabels.count':numXLabels,'strict':true});this.Set('chart.xmax',this.xscale2.max);var interval=(ca.width-this.gutterLeft-this.gutterRight)/this.xscale2.labels.length;for(var i=0,len=this.xscale2.labels.length;i<len;i+=1){var num=((prop['chart.xmax']-prop['chart.xmin'])*((i+1)/numXLabels))+(xmin||0),x=this.gutterLeft+((i+1)*interval),text=typeof prop['chart.xscale.formatter']==='function'?String(prop['chart.xscale.formatter'](this,num)):this.xscale2.labels[i];RG.text2(this,{'color':color,'font':font,'size':text_size,'bold':bold,'x':x+offsetx,'y':y+offsety,'valign':'center','halign':'center','text':text,'tag':'xscale'});}
77
- var text=typeof prop['chart.xscale.formatter']==='function'?String(prop['chart.xscale.formatter'](this,prop['chart.xmin'])):String(prop['chart.xmin']);RG.text2(this,{'color':color,'font':font,'size':text_size,'bold':bold,'x':this.gutterLeft+offsetx,'y':y+offsety,'valign':'center','halign':'center','text':text,'tag':'xscale'});}else{var graphArea=ca.width-this.gutterLeft-this.gutterRight;var xInterval=graphArea/prop['chart.labels'].length;var xPos=this.gutterLeft;var yPos=(ca.height-this.gutterBottom)+3;var labels=prop['chart.labels'];var color=prop['chart.labels.color'];var bold=prop['chart.labels.bold'];var offsetx=prop['chart.labels.offsetx'];var offsety=prop['chart.labels.offsety'];var angle=0;var valign='top';var halign='center';if(prop['chart.text.angle']>0){angle=-1*prop['chart.text.angle'];valign='center';halign='right';yPos+=10;}
78
- for(i=0;i<labels.length;++i){if(typeof(labels[i])=='object'){if(prop['chart.labels.specific.align']=='center'){var rightEdge=0;if(labels[i+1]&&labels[i+1][1]){rightEdge=labels[i+1][1];}else{rightEdge=prop['chart.xmax'];}
79
- var offset=(this.getXCoord(rightEdge)-this.getXCoord(labels[i][1]))/2;}else{var offset=5;}
80
- RG.text2(this,{'color':color,'font':font,'size':prop['chart.text.size'],'bold':bold,'x':this.getXCoord(labels[i][1])+offset+offsetx,'y':yPos+offsety,'valign':valign,'halign':angle!=0?'right':(prop['chart.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.gutterLeft+(graphArea*((labels[i][1]-xMin)/(prop['chart.xmax']-xMin)))),ca.height-this.gutterBottom);co.lineTo(ma.round(this.gutterLeft+(graphArea*((labels[i][1]-xMin)/(prop['chart.xmax']-xMin)))),ca.height-this.gutterBottom+20);co.stroke();}else{RG.text2(this,{'color':color,'font':font,'size':prop['chart.text.size'],'bold':bold,'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.gutterLeft+graphArea,ca.height-this.gutterBottom);co.lineTo(this.gutterLeft+graphArea,ca.height-this.gutterBottom+20);co.stroke();}}};this.drawMarks=this.DrawMarks=function(i)
83
- {this.coords[i]=[];var xmax=prop['chart.xmax'];var default_color=prop['chart.defaultcolor'];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'];var tickSize=prop['chart.ticksize'];var xMin=prop['chart.xmin'];var x=((x-xMin)/(xMax-xMin))*(ca.width-this.gutterLeft-this.gutterRight);var originalX=x;var 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]);var y1=this.getYCoord(y[1]);var y2=this.getYCoord(y[2]);var y3=this.getYCoord(y[3]);var y4=this.getYCoord(y[4]);var col1=y[5];var col2=y[6];var boxWidth=typeof(y[7])=='number'?y[7]:prop['chart.boxplot.width'];}else{var yCoord=this.getYCoord(y);}
88
- x+=this.gutterLeft;co.beginPath();co.strokeStyle=color;if(prop['chart.boxplot']){boxWidth=(boxWidth/prop['chart.xmax'])*(ca.width-this.gutterLeft-this.gutterRight);var halfBoxWidth=boxWidth/2;if(prop['chart.line.visible']){co.beginPath();co.strokeRect(x-halfBoxWidth,y1,boxWidth,y3-y1);if(col1){co.fillStyle=col1;co.fillRect(x-halfBoxWidth,y1,boxWidth,y2-y1);}
89
- if(col2){co.fillStyle=col2;co.fillRect(x-halfBoxWidth,y2,boxWidth,y3-y2);}
90
- co.stroke();co.beginPath();if(prop['chart.boxplot.capped']){co.moveTo(x-halfBoxWidth,Math.round(y0));co.lineTo(x+halfBoxWidth,Math.round(y0));}
91
- co.moveTo(Math.round(x),y0);co.lineTo(Math.round(x),y1);if(prop['chart.boxplot.capped']){co.moveTo(x-halfBoxWidth,Math.round(y4));co.lineTo(x+halfBoxWidth,Math.round(y4));}
92
- co.moveTo(Math.round(x),y4);co.lineTo(Math.round(x),y3);co.stroke();}}
93
- 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.gutterLeft-this.gutterRight
94
- var graphheight=ca.height-this.gutterTop-this.gutterBottom;var xVal=((x-this.gutterLeft)/graphWidth)*xMax;var yVal=((graphheight-(yCoord-this.gutterTop))/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.image.halign']==='center')x-=(this.width/2);if(prop['chart.tickmarks.image.halign']==='right')x-=this.width;if(prop['chart.tickmarks.image.valign']==='center')yCoord-=(this.height/2);if(prop['chart.tickmarks.image.valign']==='bottom')yCoord-=this.height;x+=prop['chart.tickmarks.image.offsetx'];yCoord+=prop['chart.tickmarks.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.gutterLeft-this.gutterRight;if(vbars){var xmax=prop['chart.xmax'];var xmin=prop['chart.xmin'];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.gutterLeft;var width=(value[1]/(xmax-xmin))*graphWidth;co.fillStyle=value[2];co.fillRect(x,this.gutterTop,width,(ca.height-this.gutterTop-this.gutterBottom));}}};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;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':obj.Get('chart.text.font'),'size':obj.Get('chart.text.size'),'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,'bounding.fill':(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.units.pre'];var units_post=prop['chart.units.post'];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];RG.Text2(this,{'font':font,'size':size,'x':x_pos,'y':y_pos-5-size,'text':x_val.toFixed(prop['chart.labels.above.decimals'])+', '+y_val.toFixed(prop['chart.labels.above.decimals']),'valign':'center','halign':'center','bounding':true,'boundingFill':'rgba(255, 255, 255, 0.7)','tag':'labels.above'});}}}};this.getYValue=this.getValue=function(arg)
118
- {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];}
119
- var obj=this;if(mouseY<this.gutterTop||mouseY>(ca.height-this.gutterBottom)||mouseX<this.gutterLeft||mouseX>(ca.width-this.gutterRight)){return null;}
120
- if(prop['chart.xaxispos']=='center'){var value=(((this.grapharea/2)-(mouseY-this.gutterTop))/this.grapharea)*(this.max-this.min)
121
- value*=2;if(value>=0){value+=this.min
122
- if(prop['chart.ylabels.invert']){value-=this.min;value=this.max-value;}}else{value-=this.min;if(prop['chart.ylabels.invert']){value+=this.min;value=this.max+value;value*=-1;}}}else{var value=((this.grapharea-(mouseY-this.gutterTop))/this.grapharea)*(this.max-this.min)
123
- value+=this.min;if(prop['chart.ylabels.invert']){value-=this.min;value=this.max-value;}}
124
- return value;};this.getXValue=function(arg)
125
- {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];}
126
- var obj=this;if(mouseY<this.gutterTop||mouseY>(ca.height-this.gutterBottom)||mouseX<this.gutterLeft||mouseX>(ca.width-this.gutterRight)){return null;}
127
- var width=(ca.width-this.gutterLeft-this.gutterRight);var value=((mouseX-this.gutterLeft)/width)*(prop['chart.xmax']-prop['chart.xmin'])
128
- value+=prop['chart.xmin'];return value;};this.highlight=this.Highlight=function(shape)
129
- {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)
130
- {var mouseXY=RG.getMouseXY(e);if(mouseXY[0]>(this.gutterLeft-3)&&mouseXY[0]<(ca.width-this.gutterRight+3)&&mouseXY[1]>(this.gutterTop-3)&&mouseXY[1]<((ca.height-this.gutterBottom)+3)){return this;}};this.getXCoord=function(value)
131
- {if(typeof value!='number'&&typeof value!='string'){return null;}
132
- if(typeof value==='string'){value=RG.parseDate(value);}
133
- var xmin=prop['chart.xmin'];var xmax=prop['chart.xmax'];var x;if(value<xmin)return null;if(value>xmax)return null;var gutterRight=this.gutterRight;var gutterLeft=this.gutterLeft;if(prop['chart.yaxispos']=='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;}
134
- return x;};this.getYCoord=this.getYCoordFromValue=function(value)
135
- {if(typeof(value)!='number'){return null;}
136
- var invert=prop['chart.ylabels.invert'];var xaxispos=prop['chart.xaxispos'];var graphHeight=ca.height-this.gutterTop-this.gutterBottom;var halfGraphHeight=graphHeight/2;var ymax=this.max;var ymin=prop['chart.ymin'];var coord=0;if(value>ymax||(prop['chart.xaxispos']=='bottom'&&value<ymin)||(prop['chart.xaxispos']=='center'&&((value>0&&value<ymin)||(value<0&&value>(-1*ymin))))){return null;}
137
- if(xaxispos=='center'){coord=((Math.abs(value)-ymin)/(ymax-ymin))*halfGraphHeight;if(invert){coord=halfGraphHeight-coord;}
138
- if(value<0){coord+=this.gutterTop;coord+=halfGraphHeight;}else{coord=halfGraphHeight-coord;coord+=this.gutterTop;}}else{coord=((value-ymin)/(ymax-ymin))*graphHeight;if(invert){coord=graphHeight-coord;}
139
- coord=graphHeight-coord;coord=this.gutterTop+coord;}
140
- return coord;};RG.Scatter.Bubble=function(scatter,min,max,width,data)
141
- {this.scatter=scatter;this.min=min;this.max=max;this.width=width;this.data=data;this.coords=[];this.type='scatter.bubble'
142
- this.set=this.Set=function(name,value)
143
- {this.scatter.set(name,value);return this;};this.get=this.Get=function(name)
144
- {this.scatter.get(name);};this.draw=this.Draw=function()
145
- {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)
146
- {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.defaultcolor'];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];}}
147
- this.scatter.Draw();return this;};};this.parseColors=function()
148
- {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.defaultcolor']=RG.array_clone(prop['chart.defaultcolor']);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.barcolor1']=RG.array_clone(prop['chart.background.barcolor1']);this.original_colors['chart.background.barcolor2']=RG.array_clone(prop['chart.background.barcolor2']);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.axis.color']=RG.array_clone(prop['chart.axis.color']);}
149
- 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]);}
150
- if(!RG.isNull(this.data[dataset][i])){this.data[dataset][i][2]=this.parseSingleColorForGradient(this.data[dataset][i][2]);}}}}
151
- var hbars=prop['chart.background.hbars'];if(hbars){for(i=0;i<hbars.length;++i){hbars[i][2]=this.parseSingleColorForGradient(hbars[i][2]);}}
152
- var vbars=prop['chart.background.vbars'];if(vbars){for(i=0;i<vbars.length;++i){vbars[i][2]=this.parseSingleColorForGradient(vbars[i][2]);}}
153
- var colors=prop['chart.line.colors'];if(colors){for(i=0;i<colors.length;++i){colors[i]=this.parseSingleColorForGradient(colors[i]);}}
154
- prop['chart.defaultcolor']=this.parseSingleColorForGradient(prop['chart.defaultcolor']);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.barcolor1']=this.parseSingleColorForGradient(prop['chart.background.barcolor1']);prop['chart.background.barcolor2']=this.parseSingleColorForGradient(prop['chart.background.barcolor2']);prop['chart.background.grid.color']=this.parseSingleColorForGradient(prop['chart.background.grid.color']);prop['chart.background.color']=this.parseSingleColorForGradient(prop['chart.background.color']);prop['chart.axis.color']=this.parseSingleColorForGradient(prop['chart.axis.color']);};this.reset=function()
155
- {};this.parseSingleColorForGradient=function(color)
156
- {if(!color||typeof(color)!='string'){return color;}
157
- if(color.match(/^gradient\((.*)\)$/i)){var parts=RegExp.$1.split(':');var grad=co.createLinearGradient(0,ca.height-prop['chart.gutter.bottom'],0,prop['chart.gutter.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]));}}
158
- return grad?grad:color;};this.interactiveKeyHighlight=function(index)
159
- {if(this.coords&&this.coords[index]&&this.coords[index].length){this.coords[index].forEach(function(value,idx,arr)
160
- {co.beginPath();co.fillStyle=prop['chart.key.interactive.highlight.chart.fill'];co.arc(value[0],value[1],prop['chart.ticksize']+3,0,RG.TWOPI,false);co.fill();});}};this.on=function(type,func)
161
- {if(type.substr(0,2)!=='on'){type='on'+type;}
162
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
163
- return this;};this.firstDrawFunc=function()
164
- {};this.trace=this.trace2=function()
165
- {var obj=this,callback=arguments[2],opt=arguments[0]||{},frames=opt.frames||30,frame=0,callback=arguments[1]||function(){}
166
- obj.Set('animationTrace',true);obj.Set('animationTraceClip',0);function iterator()
167
- {RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(frame++<frames){obj.set('animationTraceClip',frame/frames);RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
168
- iterator();return this;};this.resetColorsToOriginalValues=function()
169
- {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.att(ca);RG.Register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};
13
+ RGraph = window.RGraph || {isRGraph: true};
14
+
15
+
16
+
17
+
18
+ /**
19
+ * The scatter graph constructor
20
+ *
21
+ * @param object canvas The cxanvas object
22
+ * @param array data The chart data
23
+ */
24
+ RGraph.Scatter = function (conf)
25
+ {
26
+ /**
27
+ * Allow for object config style
28
+ */
29
+ if ( typeof conf === 'object'
30
+ && typeof conf.data === 'object'
31
+ && typeof conf.id === 'string') {
32
+
33
+ var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
34
+
35
+ this.data = new Array(conf.data.length);
36
+
37
+ // Store the data set(s)
38
+ this.data = RGraph.arrayClone(conf.data);
39
+
40
+
41
+ // Account for just one dataset being given
42
+ if (typeof conf.data === 'object' && typeof conf.data[0] === 'object' && (typeof conf.data[0][0] === 'number' || typeof conf.data[0][0] === 'string')) {
43
+ var tmp = RGraph.arrayClone(conf.data);
44
+ conf.data = new Array();
45
+ conf.data[0] = RGraph.arrayClone(tmp);
46
+
47
+ this.data = RGraph.arrayClone(conf.data);
48
+ }
49
+
50
+ } else {
51
+
52
+ var conf = {id: conf};
53
+ conf.data = arguments[1];
54
+
55
+
56
+ this.data = [];
57
+
58
+ // Handle multiple datasets being given as one argument
59
+ if (arguments[1][0] && arguments[1][0][0] && typeof arguments[1][0][0] == 'object') {
60
+ // Store the data set(s)
61
+ for (var i=0; i<arguments[1].length; ++i) {
62
+ this.data[i] = RGraph.arrayClone(arguments[1][i]);
63
+ }
64
+
65
+ // Handle multiple data sets being supplied as seperate arguments
66
+ } else {
67
+
68
+ // Store the data set(s)
69
+ for (var i=1; i<arguments.length; ++i) {
70
+ this.data[i - 1] = RGraph.arrayClone(arguments[i]);
71
+ }
72
+ }
73
+ }
74
+
75
+ // If necessary convert X/Y values passed as strings to numbers
76
+ for (var i=0,len=this.data.length; i<len; ++i) { // Datasets
77
+ for (var j=0,len2=this.data[i].length; j<len2; ++j) { // Points
78
+
79
+ // Handle the conversion of X values
80
+ if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][0] === 'string') {
81
+ if (this.data[i][j][0].match(/^[.0-9]+$/)) {
82
+ this.data[i][j][0] = parseFloat(this.data[i][j][0]);
83
+ } else if (this.data[i][j][0] === '') {
84
+ this.data[i][j][0] = 0;
85
+ }
86
+ }
87
+
88
+ // Handle the conversion of Y values
89
+ if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][1] === 'string') {
90
+ if (this.data[i][j][1].match(/[.0-9]+/)) {
91
+ this.data[i][j][1] = parseFloat(this.data[i][j][1]);
92
+ } else if (this.data[i][j][1] === '') {
93
+ this.data[i][j][1] = 0;
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+
100
+ this.id = conf.id;
101
+ this.canvas = document.getElementById(this.id);
102
+ this.canvas.__object__ = this;
103
+ this.context = this.canvas.getContext ? this.canvas.getContext('2d') : null;
104
+ this.max = 0;
105
+ this.coords = [];
106
+ this.type = 'scatter';
107
+ this.isRGraph = true;
108
+ this.uid = RGraph.CreateUID();
109
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
110
+ this.colorsParsed = false;
111
+ this.coordsText = [];
112
+ this.original_colors = [];
113
+ this.firstDraw = true; // After the first draw this will be false
114
+
115
+
116
+
117
+
118
+ // Handle multiple datasets being given as one argument
119
+ //if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0]) == 'object') {
120
+ // // Store the data set(s)
121
+ // for (var i=0; i<arguments[1].length; ++i) {
122
+ // this.data[i] = arguments[1][i];
123
+ // }
124
+
125
+ // Handle multiple data sets being supplied as seperate arguments
126
+ //} else {
127
+ // Store the data set(s)
128
+ //for (var i=1; i<arguments.length; ++i) {
129
+ // this.data[i - 1] = arguments[i];
130
+ //}
131
+ //}
132
+
133
+
134
+ // Various config properties
135
+ this.properties = {
136
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
137
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
138
+ 'chart.background.grid': true,
139
+ 'chart.background.grid.width': 1,
140
+ 'chart.background.grid.color': '#ddd',
141
+ 'chart.background.grid.hsize': 20,
142
+ 'chart.background.grid.vsize': 20,
143
+ 'chart.background.hbars': null,
144
+ 'chart.background.vbars': null,
145
+ 'chart.background.grid.vlines': true,
146
+ 'chart.background.grid.hlines': true,
147
+ 'chart.background.grid.border': true,
148
+ 'chart.background.grid.autofit':true,
149
+ 'chart.background.grid.autofit.align': true,
150
+ 'chart.background.grid.autofit.numhlines': 5,
151
+ 'chart.background.grid.autofit.numvlines': 20,
152
+ 'chart.background.image': null,
153
+ 'chart.background.image.stretch': true,
154
+ 'chart.background.image.x': null,
155
+ 'chart.background.image.y': null,
156
+ 'chart.background.image.w': null,
157
+ 'chart.background.image.h': null,
158
+ 'chart.background.image.align': null,
159
+ 'chart.background.color': null,
160
+
161
+ 'chart.text.size': 12,
162
+ 'chart.text.angle': 0,
163
+ 'chart.text.color': 'black',
164
+ 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
165
+ 'chart.text.accessible': true,
166
+ 'chart.text.accessible.overflow': 'visible',
167
+ 'chart.text.accessible.pointerevents': true,
168
+
169
+ 'chart.tooltips': [], // Default must be an empty array
170
+ 'chart.tooltips.effect': 'fade',
171
+ 'chart.tooltips.event': 'onmousemove',
172
+ 'chart.tooltips.hotspot': 3,
173
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
174
+ 'chart.tooltips.highlight': true,
175
+ 'chart.tooltips.coords.page': false,
176
+
177
+ 'chart.units.pre': '',
178
+ 'chart.units.post': '',
179
+ 'chart.numyticks': 10,
180
+ 'chart.tickmarks': 'cross',
181
+
182
+ 'chart.tickmarks.image.halign': 'center',
183
+ 'chart.tickmarks.image.valign': 'center',
184
+ 'chart.tickmarks.image.offsetx': 0,
185
+ 'chart.tickmarks.image.offsety': 0,
186
+ 'chart.ticksize': 5,
187
+ 'chart.numxticks': true,
188
+ 'chart.xaxis': true,
189
+
190
+ 'chart.gutter.left': 25,
191
+ 'chart.gutter.right': 25,
192
+ 'chart.gutter.top': 25,
193
+ 'chart.gutter.bottom': 30,
194
+
195
+ 'chart.colors.bubble.graduated':true,
196
+ 'chart.xmin': 0,
197
+ 'chart.xmax': 0,
198
+ 'chart.ymax': null,
199
+ 'chart.ymin': 0,
200
+ 'chart.scale.decimals': 0,
201
+ 'chart.scale.point': '.',
202
+ 'chart.scale.thousand': ',',
203
+ 'chart.scale.zerostart': true,
204
+ 'chart.title': '',
205
+ 'chart.title.background': null,
206
+ 'chart.title.hpos': null,
207
+ 'chart.title.vpos': null,
208
+ 'chart.title.bold': true,
209
+ 'chart.title.font': null,
210
+ 'chart.title.xaxis': '',
211
+ 'chart.title.xaxis.bold': true,
212
+ 'chart.title.xaxis.size': null,
213
+ 'chart.title.xaxis.font': null,
214
+ 'chart.title.xaxis.color': null,
215
+ 'chart.title.yaxis': '',
216
+ 'chart.title.yaxis.bold': true,
217
+ 'chart.title.yaxis.size': null,
218
+ 'chart.title.yaxis.font': null,
219
+ 'chart.title.yaxis.color': null,
220
+ 'chart.title.xaxis.pos': null,
221
+ 'chart.title.yaxis.pos': null,
222
+ 'chart.title.yaxis.x': null,
223
+ 'chart.title.yaxis.y': null,
224
+ 'chart.title.xaxis.x': null,
225
+ 'chart.title.xaxis.y': null,
226
+ 'chart.title.x': null,
227
+ 'chart.title.y': null,
228
+ 'chart.title.halign': null,
229
+ 'chart.title.valign': null,
230
+ 'chart.labels': [],
231
+ 'chart.labels.bold': false,
232
+ 'chart.labels.color': null,
233
+ 'chart.labels.ingraph': null,
234
+ 'chart.labels.above': false,
235
+ 'chart.labels.above.size': 8,
236
+ 'chart.labels.above.decimals': 0,
237
+ 'chart.labels.offsetx': 0,
238
+ 'chart.labels.offsety': 0,
239
+ 'chart.ylabels.offsetx': 0,
240
+ 'chart.ylabels.offsety': 0,
241
+ 'chart.ylabels': true,
242
+ 'chart.ylabels.count': 5,
243
+ 'chart.ylabels.invert': false,
244
+ 'chart.ylabels.specific': null,
245
+ 'chart.ylabels.inside': false,
246
+ 'chart.contextmenu': null,
247
+ 'chart.defaultcolor': 'black',
248
+ 'chart.xaxispos': 'bottom',
249
+ 'chart.yaxispos': 'left',
250
+ 'chart.crosshairs': false,
251
+ 'chart.crosshairs.color': '#333',
252
+ 'chart.crosshairs.linewidth': 1,
253
+ 'chart.crosshairs.coords': false,
254
+ 'chart.crosshairs.coords.fixed':true,
255
+ 'chart.crosshairs.coords.fadeout':false,
256
+ 'chart.crosshairs.coords.labels.x': 'X',
257
+ 'chart.crosshairs.coords.labels.y': 'Y',
258
+ 'chart.crosshairs.hline': true,
259
+ 'chart.crosshairs.vline': true,
260
+ 'chart.annotatable': false,
261
+ 'chart.annotate.color': 'black',
262
+ 'chart.line': false,
263
+ 'chart.line.linewidth': 1,
264
+ 'chart.line.colors': ['green', 'red'],
265
+ 'chart.line.shadow.color': 'rgba(0,0,0,0)',
266
+ 'chart.line.shadow.blur': 2,
267
+ 'chart.line.shadow.offsetx': 3,
268
+ 'chart.line.shadow.offsety': 3,
269
+ 'chart.line.stepped': false,
270
+ 'chart.line.visible': true,
271
+ 'chart.noaxes': false,
272
+ 'chart.noyaxis': false,
273
+ 'chart.key': null,
274
+ 'chart.key.background': 'white',
275
+ 'chart.key.position': 'graph',
276
+ 'chart.key.halign': 'right',
277
+ 'chart.key.shadow': false,
278
+ 'chart.key.shadow.color': '#666',
279
+ 'chart.key.shadow.blur': 3,
280
+ 'chart.key.shadow.offsetx': 2,
281
+ 'chart.key.shadow.offsety': 2,
282
+ 'chart.key.position.gutter.boxed': false,
283
+ 'chart.key.position.x': null,
284
+ 'chart.key.position.y': null,
285
+
286
+ 'chart.key.interactive': false,
287
+ 'chart.key.interactive.highlight.chart.fill': 'rgba(255,0,0,0.9)',
288
+ 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
289
+
290
+ 'chart.key.color.shape': 'square',
291
+ 'chart.key.rounded': true,
292
+ 'chart.key.linewidth': 1,
293
+ 'chart.key.colors': null,
294
+ 'chart.key.text.color': 'black',
295
+ 'chart.axis.color': 'black',
296
+ 'chart.zoom.factor': 1.5,
297
+ 'chart.zoom.fade.in': true,
298
+ 'chart.zoom.fade.out': true,
299
+ 'chart.zoom.hdir': 'right',
300
+ 'chart.zoom.vdir': 'down',
301
+ 'chart.zoom.frames': 25,
302
+ 'chart.zoom.delay': 16.666,
303
+ 'chart.zoom.shadow': true,
304
+ 'chart.zoom.background': true,
305
+ 'chart.zoom.action': 'zoom',
306
+ 'chart.boxplot.width': 1,
307
+ 'chart.boxplot.capped': true,
308
+ 'chart.resizable': false,
309
+ 'chart.resize.handle.background': null,
310
+ 'chart.xmin': 0,
311
+ 'chart.labels.specific.align': 'left',
312
+ 'chart.xscale': false,
313
+ 'chart.xscale.units.pre': '',
314
+ 'chart.xscale.units.post': '',
315
+ 'chart.xscale.numlabels': 10,
316
+ 'chart.xscale.formatter': null,
317
+ 'chart.xscale.decimals': 0,
318
+ 'chart.xscale.thousand': ',',
319
+ 'chart.xscale.point': '.',
320
+ 'chart.noendxtick': false,
321
+ 'chart.noendytick': true,
322
+ 'chart.events.mousemove': null,
323
+ 'chart.events.click': null,
324
+ 'chart.highlight.stroke': 'rgba(0,0,0,0)',
325
+ 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
326
+ 'chart.clearto': 'rgba(0,0,0,0)',
327
+ 'chart.animation.trace': false,
328
+ 'chart.animation.trace.clip': 1
329
+ }
330
+
331
+ /**
332
+ * This allows the data points to be given as dates as well as numbers. Formats supported by RGraph.parseDate() are accepted.
333
+ *
334
+ * ALSO: unrelated but this loop is also used to convert null values to an
335
+ * empty array
336
+ */
337
+ for (var i=0; i<this.data.length; ++i) {
338
+ for (var j=0; j<this.data[i].length; ++j) {
339
+
340
+ // Convert null data points to an empty erray
341
+ if ( RGraph.isNull(this.data[i][j]) ) {
342
+ this.data[i][j] = [];
343
+ }
344
+
345
+ // Allow for the X point to be dates
346
+ if (this.data[i][j] && typeof(this.data[i][j][0]) == 'string') {
347
+ this.data[i][j][0] = RGraph.parseDate(this.data[i][j][0]);
348
+ }
349
+ }
350
+ }
351
+
352
+
353
+ /**
354
+ * Now make the data_arr array - all the data as one big array
355
+ */
356
+ this.data_arr = [];
357
+
358
+ for (var i=0; i<this.data.length; ++i) {
359
+ for (var j=0; j<this.data[i].length; ++j) {
360
+ this.data_arr.push(this.data[i][j]);
361
+ }
362
+ }
363
+
364
+ // Create the $ objects so that they can be used
365
+ for (var i=0; i<this.data_arr.length; ++i) {
366
+ this['$' + i] = {}
367
+ }
368
+
369
+
370
+ // Check for support
371
+ if (!this.canvas) {
372
+ alert('[SCATTER] No canvas support');
373
+ return;
374
+ }
375
+
376
+
377
+ /**
378
+ * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
379
+ * done already
380
+ */
381
+ if (!this.canvas.__rgraph_aa_translated__) {
382
+ this.context.translate(0.5,0.5);
383
+
384
+ this.canvas.__rgraph_aa_translated__ = true;
385
+ }
386
+
387
+
388
+
389
+ // Short variable names
390
+ var RG = RGraph,
391
+ ca = this.canvas,
392
+ co = ca.getContext('2d'),
393
+ prop = this.properties,
394
+ pa2 = RG.path2,
395
+ win = window,
396
+ doc = document,
397
+ ma = Math
398
+
399
+
400
+
401
+ /**
402
+ * "Decorate" the object with the generic effects if the effects library has been included
403
+ */
404
+ if (RG.Effects && typeof RG.Effects.decorate === 'function') {
405
+ RG.Effects.decorate(this);
406
+ }
407
+
408
+
409
+
410
+
411
+
412
+
413
+
414
+ /**
415
+ * A simple setter
416
+ *
417
+ * @param string name The name of the property to set
418
+ * @param string value The value of the property
419
+ */
420
+ this.set =
421
+ this.Set = function (name)
422
+ {
423
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
424
+
425
+ /**
426
+ * the number of arguments is only one and it's an
427
+ * object - parse it for configuration data and return.
428
+ */
429
+ if (arguments.length === 1 && typeof name === 'object') {
430
+ RG.parseObjectStyleConfig(this, name);
431
+ return this;
432
+ }
433
+
434
+
435
+
436
+ /**
437
+ * This should be done first - prepend the propertyy name with "chart." if necessary
438
+ */
439
+ if (name.substr(0,6) != 'chart.') {
440
+ name = 'chart.' + name;
441
+ }
442
+
443
+
444
+
445
+
446
+ // Convert uppercase letters to dot+lower case letter
447
+ while(name.match(/([A-Z])/)) {
448
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
449
+ }
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+ /**
458
+ * BC for chart.xticks
459
+ */
460
+ if (name == 'chart.xticks') {
461
+ name == 'chart.numxticks';
462
+ }
463
+
464
+ /**
465
+ * This is here because the key expects a name of "chart.colors"
466
+ */
467
+ if (name == 'chart.line.colors') {
468
+ prop['chart.colors'] = value;
469
+ }
470
+
471
+ /**
472
+ * Allow compatibility with older property names
473
+ */
474
+ if (name == 'chart.tooltip.hotspot') {
475
+ name = 'chart.tooltips.hotspot';
476
+ }
477
+
478
+
479
+ /**
480
+ * chart.yaxispos should be left or right
481
+ */
482
+ if (name == 'chart.yaxispos' && value != 'left' && value != 'right') {
483
+ alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '" + value + "' Changing it to left");
484
+ value = 'left';
485
+ }
486
+
487
+ /**
488
+ * Check for xaxispos
489
+ */
490
+ if (name == 'chart.xaxispos' ) {
491
+ if (value != 'bottom' && value != 'center') {
492
+ alert('[SCATTER] (' + this.id + ') chart.xaxispos should be center or bottom. Tried to set it to: ' + value + ' Changing it to center');
493
+ value = 'center';
494
+ }
495
+ }
496
+
497
+ /**
498
+ * Compatibility for chart.noxaxisoption
499
+ */
500
+ if (name == 'chart.noxaxis' ) {
501
+ name = 'chart.xaxis';
502
+ value = !value;
503
+ }
504
+ prop[name.toLowerCase()] = value;
505
+
506
+ return this;
507
+ };
508
+
509
+
510
+
511
+
512
+ /**
513
+ * A simple getter
514
+ *
515
+ * @param string name The name of the property to set
516
+ */
517
+ this.get =
518
+ this.Get = function (name)
519
+ {
520
+ /**
521
+ * This should be done first - prepend the property name with "chart." if necessary
522
+ */
523
+ if (name.substr(0,6) != 'chart.') {
524
+ name = 'chart.' + name;
525
+ }
526
+
527
+ // Convert uppercase letters to dot+lower case letter
528
+ while(name.match(/([A-Z])/)) {
529
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
530
+ }
531
+
532
+ return prop[name];
533
+ };
534
+
535
+
536
+
537
+
538
+ /**
539
+ * The function you call to draw the Scatter chart
540
+ */
541
+ this.draw =
542
+ this.Draw = function ()
543
+ {
544
+ // MUST be the first thing done!
545
+ if (typeof prop['chart.background.image'] === 'string') {
546
+ RG.DrawBackgroundImage(this);
547
+ }
548
+
549
+
550
+ /**
551
+ * Fire the onbeforedraw event
552
+ */
553
+ RG.fireCustomEvent(this, 'onbeforedraw');
554
+
555
+
556
+ /**
557
+ * Parse the colors. This allows for simple gradient syntax
558
+ */
559
+ if (!this.colorsParsed) {
560
+ this.parseColors();
561
+
562
+ // Don't want to do this again
563
+ this.colorsParsed = true;
564
+ }
565
+
566
+
567
+
568
+
569
+ /**
570
+ * Stop this growing uncontrollably
571
+ */
572
+ this.coordsText = [];
573
+
574
+
575
+
576
+
577
+ /**
578
+ * This is new in May 2011 and facilitates indiviual gutter settings,
579
+ * eg chart.gutter.left
580
+ */
581
+ this.gutterLeft = prop['chart.gutter.left'];
582
+ this.gutterRight = prop['chart.gutter.right'];
583
+ this.gutterTop = prop['chart.gutter.top'];
584
+ this.gutterBottom = prop['chart.gutter.bottom'];
585
+
586
+ // Go through all the data points and see if a tooltip has been given
587
+ this.hasTooltips = false;
588
+ var overHotspot = false;
589
+
590
+ // Reset the coords array
591
+ this.coords = [];
592
+
593
+ /**
594
+ * This facilitates the xmax, xmin and X values being dates
595
+ */
596
+ if (typeof(prop['chart.xmin']) == 'string') prop['chart.xmin'] = RG.parseDate(prop['chart.xmin']);
597
+ if (typeof(prop['chart.xmax']) == 'string') prop['chart.xmax'] = RG.parseDate(prop['chart.xmax']);
598
+
599
+ /**
600
+ * Look for tooltips and populate chart.tooltips
601
+ *
602
+ * NB 26/01/2011 Updated so that chart.tooltips is ALWAYS populated
603
+ */
604
+ if (!RGraph.ISOLD) {
605
+ this.Set('chart.tooltips', []);
606
+ for (var i=0,len=this.data.length; i<len; i+=1) {
607
+ for (var j =0,len2=this.data[i].length;j<len2; j+=1) {
608
+
609
+ if (this.data[i][j] && this.data[i][j][3]) {
610
+ prop['chart.tooltips'].push(this.data[i][j][3]);
611
+ this.hasTooltips = true;
612
+ } else {
613
+ prop['chart.tooltips'].push(null);
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ // Reset the maximum value
620
+ this.max = 0;
621
+
622
+ // Work out the maximum Y value
623
+ //if (prop['chart.ymax'] && prop['chart.ymax'] > 0) {
624
+ if (typeof prop['chart.ymax'] === 'number') {
625
+
626
+ this.max = prop['chart.ymax'];
627
+ this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
628
+
629
+
630
+ this.scale2 = RG.getScale2(this, {
631
+ 'max':this.max,
632
+ 'min':this.min,
633
+ 'strict':true,
634
+ 'scale.thousand':prop['chart.scale.thousand'],
635
+ 'scale.point':prop['chart.scale.point'],
636
+ 'scale.decimals':prop['chart.scale.decimals'],
637
+ 'ylabels.count':prop['chart.ylabels.count'],
638
+ 'scale.round':prop['chart.scale.round'],
639
+ 'units.pre': prop['chart.units.pre'],
640
+ 'units.post': prop['chart.units.post']
641
+ });
642
+
643
+ this.max = this.scale2.max;
644
+ this.min = this.scale2.min;
645
+ var decimals = prop['chart.scale.decimals'];
646
+
647
+ } else {
648
+
649
+ var i = 0;
650
+ var j = 0;
651
+
652
+ for (i=0,len=this.data.length; i<len; i+=1) {
653
+ for (j=0,len2=this.data[i].length; j<len2; j+=1) {
654
+ if (!RG.isNull(this.data[i][j]) && this.data[i][j][1] != null) {
655
+ 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]));
656
+ }
657
+ }
658
+ }
659
+
660
+ this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
661
+
662
+ this.scale2 = RG.getScale2(this, {
663
+ 'max':this.max,
664
+ 'min':this.min,
665
+ 'scale.thousand':prop['chart.scale.thousand'],
666
+ 'scale.point':prop['chart.scale.point'],
667
+ 'scale.decimals':prop['chart.scale.decimals'],
668
+ 'ylabels.count':prop['chart.ylabels.count'],
669
+ 'scale.round':prop['chart.scale.round'],
670
+ 'units.pre': prop['chart.units.pre'],
671
+ 'units.post': prop['chart.units.post']
672
+ });
673
+
674
+ this.max = this.scale2.max;
675
+ this.min = this.scale2.min;
676
+ }
677
+
678
+ this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
679
+
680
+
681
+
682
+ // Progressively Draw the chart
683
+ RG.background.Draw(this);
684
+
685
+ /**
686
+ * Draw any horizontal bars that have been specified
687
+ */
688
+ if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length) {
689
+ RG.DrawBars(this);
690
+ }
691
+
692
+ /**
693
+ * Draw any vertical bars that have been specified
694
+ */
695
+ if (prop['chart.background.vbars'] && prop['chart.background.vbars'].length) {
696
+ this.DrawVBars();
697
+ }
698
+
699
+ if (!prop['chart.noaxes']) {
700
+ this.DrawAxes();
701
+ }
702
+
703
+ this.DrawLabels();
704
+
705
+ // Clip the canvas so that the trace2 effect is facilitated
706
+ if (prop['chart.animation.trace']) {
707
+ co.save();
708
+ co.beginPath();
709
+ co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
710
+ co.clip();
711
+ }
712
+
713
+ for(i=0; i<this.data.length; ++i) {
714
+ this.DrawMarks(i);
715
+
716
+ // Set the shadow
717
+ co.shadowColor = prop['chart.line.shadow.color'];
718
+ co.shadowOffsetX = prop['chart.line.shadow.offsetx'];
719
+ co.shadowOffsetY = prop['chart.line.shadow.offsety'];
720
+ co.shadowBlur = prop['chart.line.shadow.blur'];
721
+
722
+ this.DrawLine(i);
723
+
724
+ // Turn the shadow off
725
+ RG.NoShadow(this);
726
+ }
727
+
728
+
729
+ if (prop['chart.line']) {
730
+ for (var i=0,len=this.data.length;i<len; i+=1) {
731
+ this.DrawMarks(i); // Call this again so the tickmarks appear over the line
732
+ }
733
+ }
734
+
735
+ if (prop['chart.animation.trace']) {
736
+ co.restore();
737
+ }
738
+
739
+
740
+
741
+ /**
742
+ * Setup the context menu if required
743
+ */
744
+ if (prop['chart.contextmenu']) {
745
+ RG.ShowContext(this);
746
+ }
747
+
748
+
749
+
750
+ /**
751
+ * Draw the key if necessary
752
+ */
753
+ if (prop['chart.key'] && prop['chart.key'].length) {
754
+ RG.DrawKey(this, prop['chart.key'], prop['chart.line.colors']);
755
+ }
756
+
757
+
758
+ /**
759
+ * Draw " above" labels if enabled
760
+ */
761
+ if (prop['chart.labels.above']) {
762
+ this.DrawAboveLabels();
763
+ }
764
+
765
+ /**
766
+ * Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js
767
+ */
768
+ this.DrawInGraphLabels(this);
769
+
770
+
771
+ /**
772
+ * This function enables resizing
773
+ */
774
+ if (prop['chart.resizable']) {
775
+ RG.AllowResizing(this);
776
+ }
777
+
778
+
779
+ /**
780
+ * This installs the event listeners
781
+ */
782
+ RG.InstallEventListeners(this);
783
+
784
+
785
+ /**
786
+ * Fire the onfirstdraw event
787
+ */
788
+ if (this.firstDraw) {
789
+ RG.fireCustomEvent(this, 'onfirstdraw');
790
+ this.firstDraw = false;
791
+ this.firstDrawFunc();
792
+ }
793
+
794
+
795
+
796
+ /**
797
+ * Fire the RGraph ondraw event
798
+ */
799
+ RG.FireCustomEvent(this, 'ondraw');
800
+
801
+
802
+ return this;
803
+ }
804
+
805
+
806
+
807
+
808
+ /**
809
+ * Draws the axes of the scatter graph
810
+ */
811
+ this.drawAxes =
812
+ this.DrawAxes = function ()
813
+ {
814
+ var graphHeight = ca.height - this.gutterTop - this.gutterBottom;
815
+
816
+ co.beginPath();
817
+ co.strokeStyle = prop['chart.axis.color'];
818
+ co.lineWidth = (prop['chart.axis.linewidth'] || 1) + 0.001; // Strange Chrome bug
819
+
820
+ // Draw the Y axis
821
+ if (prop['chart.noyaxis'] == false) {
822
+ if (prop['chart.yaxispos'] == 'left') {
823
+ co.moveTo(this.gutterLeft, this.gutterTop);
824
+ co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
825
+ } else {
826
+ co.moveTo(ca.width - this.gutterRight, this.gutterTop);
827
+ co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
828
+ }
829
+ }
830
+
831
+
832
+ // Draw the X axis
833
+ if (prop['chart.xaxis']) {
834
+ if (prop['chart.xaxispos'] == 'center') {
835
+ co.moveTo(this.gutterLeft, ma.round(this.gutterTop + ((ca.height - this.gutterTop - this.gutterBottom) / 2)));
836
+ co.lineTo(ca.width - this.gutterRight, ma.round(this.gutterTop + ((ca.height - this.gutterTop - this.gutterBottom) / 2)));
837
+ } else {
838
+
839
+ var y = this.getYCoord(this.scale2.min > 0 ? this.scale2.min : 0);
840
+
841
+ co.moveTo(this.gutterLeft, y);
842
+ co.lineTo(ca.width - this.gutterRight, y);
843
+ }
844
+ }
845
+
846
+ // Draw the Y tickmarks
847
+ if (prop['chart.noyaxis'] === false) {
848
+
849
+ var numyticks = prop['chart.numyticks'];
850
+
851
+ //for (y=this.gutterTop; y < ca.height - this.gutterBottom + (prop['chart.xaxispos'] == 'center' ? 1 : 0) ; y+=(graphHeight / numyticks)) {
852
+ for (i=0; i<numyticks; ++i) {
853
+
854
+ var y = ((ca.height - this.gutterTop - this.gutterBottom) / numyticks) * i;
855
+ y = y + this.gutterTop;
856
+
857
+ if (prop['chart.xaxispos'] == 'center' && i == (numyticks / 2)) {
858
+ continue;
859
+ }
860
+
861
+ if (prop['chart.yaxispos'] == 'left') {
862
+ co.moveTo(this.gutterLeft, ma.round(y));
863
+ co.lineTo(this.gutterLeft - 3, ma.round(y));
864
+ } else {
865
+ co.moveTo(ca.width - this.gutterRight +3, Math.round(y));
866
+ co.lineTo(ca.width - this.gutterRight, Math.round(y));
867
+ }
868
+ }
869
+
870
+ /**
871
+ * Draw the end Y tickmark if the X axis is in the centre
872
+ */
873
+ if (prop['chart.numyticks'] > 0) {
874
+ if (prop['chart.xaxispos'] == 'center' && prop['chart.yaxispos'] == 'left') {
875
+ co.moveTo(this.gutterLeft, ma.round(ca.height - this.gutterBottom));
876
+ co.lineTo(this.gutterLeft - 3, ma.round(ca.height - this.gutterBottom));
877
+ } else if (prop['chart.xaxispos'] == 'center') {
878
+ co.moveTo(ca.width - this.gutterRight + 3, ma.round(ca.height - this.gutterBottom));
879
+ co.lineTo(ca.width - this.gutterRight, ma.round(ca.height - this.gutterBottom));
880
+ }
881
+ }
882
+
883
+ /**
884
+ * Draw an extra tick if the X axis isn't being shown
885
+ */
886
+ if (prop['chart.xaxis'] === false && prop['chart.yaxispos'] === 'left') {
887
+ co.moveTo(this.gutterLeft, ma.round(ca.height - this.gutterBottom));
888
+ co.lineTo(this.gutterLeft - 3, ma.round(ca.height - this.gutterBottom));
889
+ } else if (prop['chart.xaxis'] === false && prop['chart.yaxispos'] === 'right') {
890
+ co.moveTo(ca.width - this.gutterRight, ma.round(ca.height - this.gutterBottom));
891
+ co.lineTo(ca.width - this.gutterRight + 3, ma.round(ca.height - this.gutterBottom));
892
+ }
893
+
894
+
895
+
896
+
897
+
898
+ //
899
+ // If: the X axis is offset
900
+ // the Y axis is being shown
901
+ // there are Y tickmarks
902
+ //
903
+ if (
904
+ prop['chart.xaxispos'] === 'bottom'
905
+ && prop['chart.numyticks'] > 0
906
+ ) {
907
+
908
+ if (prop['chart.yaxispos'] == 'left') {
909
+ co.moveTo(this.gutterLeft, ma.round(this.getYCoord(prop['chart.ymin'])));
910
+ co.lineTo(this.gutterLeft - 3, ma.round(this.getYCoord(prop['chart.ymin'])));
911
+ } else {
912
+ co.moveTo(ca.width - this.gutterRight +3, ma.round(this.getYCoord(prop['chart.ymin'])));
913
+ co.lineTo(ca.width - this.gutterRight, ma.round(this.getYCoord(prop['chart.ymin'])));
914
+ }
915
+ }
916
+ }
917
+
918
+
919
+ /**
920
+ * Draw the X tickmarks
921
+ */
922
+ if (prop['chart.numxticks'] > 0 && prop['chart.xaxis']) {
923
+
924
+ var x = 0,
925
+ y = this.getYCoord(prop['chart.ylabels.invert'] ? this.scale2.max : 0) - 3,
926
+ size = 3;
927
+
928
+ if (prop['chart.ymin'] === 0 && prop['chart.xaxispos'] === 'bottom') {
929
+ y += 3;
930
+ }
931
+
932
+ this.xTickGap = (prop['chart.labels'] && prop['chart.labels'].length) ? ((ca.width - this.gutterLeft - this.gutterRight ) / prop['chart.labels'].length) : (ca.width - this.gutterLeft - this.gutterRight) / 10;
933
+
934
+ /**
935
+ * This allows the number of X tickmarks to be specified
936
+ */
937
+ if (typeof(prop['chart.numxticks']) == 'number') {
938
+ this.xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
939
+ }
940
+
941
+
942
+ for (x=(this.gutterLeft + (prop['chart.yaxispos'] == 'left' && prop['chart.noyaxis'] == false ? this.xTickGap : 0) );
943
+ x <= (ca.width - this.gutterRight - (prop['chart.yaxispos'] == 'left' || prop['chart.noyaxis'] == true ? -1 : 1));
944
+ x += this.xTickGap) {
945
+
946
+ if (prop['chart.yaxispos'] == 'left' && prop['chart.noendxtick'] == true && x == (ca.width - this.gutterRight) ) {
947
+ continue;
948
+ } else if (prop['chart.yaxispos'] == 'right' && prop['chart.noendxtick'] == true && x == this.gutterLeft) {
949
+ continue;
950
+ }
951
+
952
+ co.moveTo(ma.round(x), y);
953
+ co.lineTo(ma.round(x), y + (prop['chart.xaxispos'] === 'center' || prop['chart.ymin'] < 0 ? size * 2 : size));
954
+ }
955
+ }
956
+
957
+ co.stroke();
958
+
959
+ /**
960
+ * Reset the linewidth back to one
961
+ */
962
+ co.lineWidth = 1;
963
+ };
964
+
965
+
966
+
967
+
968
+
969
+
970
+
971
+
972
+
973
+
974
+
975
+ /**
976
+ * Draws the labels on the scatter graph
977
+ */
978
+ this.drawLabels =
979
+ this.DrawLabels = function ()
980
+ {
981
+ co.fillStyle = prop['chart.text.color'];
982
+
983
+ var font = prop['chart.text.font'],
984
+ xMin = prop['chart.xmin'],
985
+ xMax = prop['chart.xmax'],
986
+ yMax = this.scale2.max,
987
+ yMin = prop['chart.ymin'] ? prop['chart.ymin'] : 0,
988
+ text_size = prop['chart.text.size'],
989
+ units_pre = prop['chart.units.pre'],
990
+ units_post = prop['chart.units.post'],
991
+ numYLabels = prop['chart.ylabels.count'],
992
+ invert = prop['chart.ylabels.invert'],
993
+ inside = prop['chart.ylabels.inside'],
994
+ context = co,
995
+ canvas = ca,
996
+ boxed = false,
997
+ offsetx = prop['chart.ylabels.offsetx'],
998
+ offsety = prop['chart.ylabels.offsety']
999
+
1000
+ this.halfTextHeight = text_size / 2;
1001
+
1002
+
1003
+ this.halfGraphHeight = (ca.height - this.gutterTop - this.gutterBottom) / 2;
1004
+
1005
+ /**
1006
+ * Draw the Y yaxis labels, be it at the top or center
1007
+ */
1008
+ if (prop['chart.ylabels']) {
1009
+
1010
+ var xPos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1011
+ var align = prop['chart.yaxispos'] == 'right' ? 'left' : 'right';
1012
+
1013
+ /**
1014
+ * Now change the two things above if chart.ylabels.inside is specified
1015
+ */
1016
+ if (inside) {
1017
+ if (prop['chart.yaxispos'] == 'left') {
1018
+ xPos = prop['chart.gutter.left'] + 5;
1019
+ align = 'left';
1020
+ boxed = true;
1021
+ } else {
1022
+ xPos = ca.width - prop['chart.gutter.right'] - 5;
1023
+ align = 'right';
1024
+ boxed = true;
1025
+ }
1026
+ }
1027
+
1028
+ if (prop['chart.xaxispos'] == 'center') {
1029
+
1030
+
1031
+ /**
1032
+ * Specific Y labels
1033
+ */
1034
+ if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific'] != null && prop['chart.ylabels.specific'].length) {
1035
+
1036
+ var labels = prop['chart.ylabels.specific'];
1037
+
1038
+ if (prop['chart.ymin'] > 0) {
1039
+ labels = [];
1040
+ for (var i=0; i<(prop['chart.ylabels.specific'].length - 1); ++i) {
1041
+ labels.push(prop['chart.ylabels.specific'][i]);
1042
+ }
1043
+ }
1044
+
1045
+ for (var i=0; i<labels.length; ++i) {
1046
+ var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) );
1047
+ RG.Text2(this, {
1048
+ 'font':font,
1049
+ 'size':text_size,
1050
+ 'x':xPos + offsetx,
1051
+ 'y':y + offsety,
1052
+ 'text':labels[i],
1053
+ 'valign':'center',
1054
+ 'halign':align,
1055
+ 'bounding':boxed,
1056
+ 'tag': 'labels.specific'
1057
+ });
1058
+ }
1059
+
1060
+ var reversed_labels = RG.array_reverse(labels);
1061
+
1062
+ for (var i=0; i<reversed_labels.length; ++i) {
1063
+ var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) );
1064
+ RG.Text2(this, {
1065
+ 'font':font,
1066
+ 'size':text_size,
1067
+ 'x':xPos + offsetx,
1068
+ 'y':y + offsety,
1069
+ 'text':reversed_labels[i],
1070
+ 'valign':'center',
1071
+ 'halign':align,
1072
+ 'bounding':boxed,
1073
+ 'tag': 'labels.specific'
1074
+ });
1075
+ }
1076
+
1077
+ /**
1078
+ * Draw the center label if chart.ymin is specified
1079
+ */
1080
+ if (prop['chart.ymin'] != 0) {
1081
+ RG.Text2(this, {
1082
+ 'font':font,
1083
+ 'size':text_size,
1084
+ 'x':xPos + offsetx,
1085
+ 'y':(this.grapharea / 2) + this.gutterTop + offsety,
1086
+ 'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length - 1],
1087
+ 'valign':'center',
1088
+ 'halign':align,
1089
+ 'bounding':boxed,
1090
+ 'tag': 'labels.specific'
1091
+ });
1092
+ }
1093
+ }
1094
+
1095
+
1096
+ if (!prop['chart.ylabels.specific'] && typeof numYLabels == 'number') {
1097
+
1098
+ /**
1099
+ * Draw the top half
1100
+ */
1101
+ for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
1102
+
1103
+ //var value = ((this.max - this.min)/ numYLabels) * (i+1);
1104
+ //value = (invert ? this.max - value : value);
1105
+ //if (!invert) value += this.min;
1106
+ //value = value.toFixed(prop['chart.scale.decimals']);
1107
+
1108
+ if (!invert) {
1109
+ RG.Text2(this, {
1110
+ 'font':font,
1111
+ 'size': text_size,
1112
+ 'x': xPos + offsetx,
1113
+ 'y': this.gutterTop + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
1114
+ 'valign': 'center',
1115
+ 'halign':align,
1116
+ 'bounding': boxed,
1117
+ 'boundingFill': 'white',
1118
+ 'text': this.scale2.labels[i],
1119
+ 'tag': 'scale'
1120
+ });
1121
+ } else {
1122
+ RG.Text2(this, {
1123
+ 'font':font,
1124
+ 'size': text_size,
1125
+ 'x': xPos + offsetx,
1126
+ 'y': this.gutterTop + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
1127
+ 'valign': 'center',
1128
+ 'halign':align,
1129
+ 'bounding': boxed,
1130
+ 'boundingFill': 'white',
1131
+ 'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
1132
+ 'tag': 'scale'
1133
+ });
1134
+ }
1135
+ }
1136
+
1137
+ /**
1138
+ * Draw the bottom half
1139
+ */
1140
+ for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
1141
+
1142
+ //var value = (((this.max - this.min)/ numYLabels) * i) + this.min;
1143
+ // value = (invert ? value : this.max - (value - this.min)).toFixed(prop['chart.scale.decimals']);
1144
+
1145
+ if (!invert) {
1146
+ RG.Text2(this, {
1147
+ 'font':font,
1148
+ 'size': text_size,
1149
+ 'x': xPos + offsetx,
1150
+ 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
1151
+ 'valign': 'center',
1152
+ 'halign':align,
1153
+ 'bounding': boxed,
1154
+ 'boundingFill': 'white',
1155
+ 'text': '-' + this.scale2.labels[len - (i+1)],
1156
+ 'tag': 'scale'
1157
+ });
1158
+ } else {
1159
+
1160
+ // This ensures that the center label isn't drawn twice
1161
+ if (i == (len - 1)&& invert) {
1162
+ continue;
1163
+ }
1164
+
1165
+ RG.Text2(this, {
1166
+ 'font':font,
1167
+ 'size': text_size,
1168
+ 'x': xPos + offsetx,
1169
+ 'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
1170
+ 'valign': 'center',
1171
+ 'halign':align,
1172
+ 'bounding': boxed,
1173
+ 'boundingFill': 'white',
1174
+ 'text': '-' + this.scale2.labels[i],
1175
+ 'tag': 'scale'
1176
+ });
1177
+ }
1178
+ }
1179
+
1180
+
1181
+
1182
+
1183
+ // If ymin is specified draw that
1184
+ if (!invert && (yMin > 0 || prop['chart.scale.zerostart'])) {
1185
+
1186
+ RG.text2(this, {
1187
+ font:font,
1188
+ size: text_size,
1189
+ x: xPos + offsetx,
1190
+ y: this.gutterTop + this.halfGraphHeight + offsety,
1191
+ valign: 'center',
1192
+ halign:align,
1193
+ bounding: boxed,
1194
+ boundingFill: 'white',
1195
+ text: RG.numberFormat(this, yMin.toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1196
+ tag: 'scale'
1197
+ });
1198
+ }
1199
+
1200
+ if (invert) {
1201
+ RG.text2(this, {
1202
+ 'font':font,
1203
+ 'size': text_size,
1204
+ 'x': xPos + offsetx,
1205
+ 'y': this.gutterTop + offsety,
1206
+ 'valign': 'center',
1207
+ 'halign':align,
1208
+ 'bounding': boxed,
1209
+ 'boundingFill': 'white',
1210
+ 'text': RG.number_format(this, yMin.toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1211
+ 'tag': 'scale'
1212
+ });
1213
+
1214
+ RG.text2(this, {
1215
+ 'font':font,
1216
+ 'size': text_size,
1217
+ 'x': xPos + offsetx,
1218
+ 'y': this.gutterTop + (this.halfGraphHeight * 2) + offsety,
1219
+ 'valign': 'center',
1220
+ 'halign':align,
1221
+ 'bounding': boxed,
1222
+ 'boundingFill': 'white',
1223
+ 'text': '-' + RG.numberFormat(this, yMin.toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1224
+ 'tag': 'scale'
1225
+ });
1226
+ }
1227
+ }
1228
+
1229
+ // X axis at the bottom
1230
+ } else {
1231
+
1232
+ var xPos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1233
+ var align = prop['chart.yaxispos'] == 'right' ? 'left' : 'right';
1234
+
1235
+ if (inside) {
1236
+ if (prop['chart.yaxispos'] == 'left') {
1237
+ xPos = prop['chart.gutter.left'] + 5;
1238
+ align = 'left';
1239
+ boxed = true;
1240
+ } else {
1241
+ xPos = ca.width - this.gutterRight - 5;
1242
+ align = 'right';
1243
+ boxed = true;
1244
+ }
1245
+ }
1246
+
1247
+ /**
1248
+ * Specific Y labels
1249
+ */
1250
+ if (typeof prop['chart.ylabels.specific'] == 'object' && prop['chart.ylabels.specific']) {
1251
+
1252
+ var labels = prop['chart.ylabels.specific'];
1253
+
1254
+ // Lose the last label
1255
+ if (prop['chart.ymin'] > 9999) {
1256
+ labels = [];
1257
+ for (var i=0; i<(prop['chart.ylabels.specific'].length - 1); ++i) {
1258
+ labels.push(prop['chart.ylabels.specific'][i]);
1259
+ }
1260
+ }
1261
+
1262
+ for (var i=0,len=labels.length; i<len; i+=1) {
1263
+
1264
+ var y = this.gutterTop + (i * (this.grapharea / (len - 1)) );
1265
+
1266
+ RG.Text2(this, {
1267
+ 'font':font,
1268
+ 'size':text_size,
1269
+ 'x':xPos + offsetx,
1270
+ 'y':y + offsety,
1271
+ 'text':labels[i],
1272
+ 'halign':align,
1273
+ 'valign':'center',
1274
+ 'bounding':boxed,
1275
+ 'tag': 'scale'
1276
+ });
1277
+ }
1278
+
1279
+ /**
1280
+ * X axis at the bottom with a scale
1281
+ */
1282
+ } else {
1283
+
1284
+ if (typeof(numYLabels) == 'number') {
1285
+
1286
+ if (invert) {
1287
+
1288
+ for (var i=0; i<numYLabels; ++i) {
1289
+
1290
+ //var value = ((this.max - this.min)/ numYLabels) * i;
1291
+ // value = value.toFixed(prop['chart.scale.decimals']);
1292
+ var interval = (ca.height - this.gutterTop - this.gutterBottom) / numYLabels;
1293
+
1294
+ RG.Text2(this, {
1295
+ 'font':font,
1296
+ 'size': text_size,
1297
+ 'x': xPos + offsetx,
1298
+ 'y': this.gutterTop + ((i+1) * interval) + offsety,
1299
+ 'valign': 'center',
1300
+ 'halign':align,
1301
+ 'bounding': boxed,
1302
+ 'boundingFill': 'white',
1303
+ 'text': this.scale2.labels[i],
1304
+ 'tag': 'scale'
1305
+ });
1306
+ }
1307
+
1308
+
1309
+ // No X axis being shown and there's no ymin. If ymin IS set its added further down
1310
+ if (!prop['chart.xaxis'] && !prop['chart.ymin']) {
1311
+
1312
+ RG.Text2(this, {
1313
+ 'font':font,
1314
+ 'size': text_size,
1315
+ 'x': xPos + offsetx,
1316
+ 'y': this.gutterTop + offsety,
1317
+ 'valign': 'center',
1318
+ 'halign':align,
1319
+ 'bounding': boxed,
1320
+ 'boundingFill': 'white',
1321
+ 'text': RG.numberFormat(this, (this.min).toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1322
+ 'tag': 'scale'
1323
+ });
1324
+ }
1325
+
1326
+ } else {
1327
+
1328
+ for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
1329
+
1330
+ //var value = ((this.max - this.min)/ numYLabels) * (i+1);
1331
+ // value = (invert ? this.max - value : value);
1332
+ // if (!invert) value += this.min;
1333
+ // value = value.toFixed(prop['chart.scale.decimals']);
1334
+
1335
+ RG.Text2(this, {
1336
+ 'font':font,
1337
+ 'size': text_size,
1338
+ 'x': xPos + offsetx,
1339
+ 'y': this.gutterTop + this.grapharea - (((i + 1)/this.scale2.labels.length) * this.grapharea) + offsety,
1340
+ 'valign': 'center',
1341
+ 'halign':align,
1342
+ 'bounding': boxed,
1343
+ 'boundingFill': 'white',
1344
+ 'text': this.scale2.labels[i],
1345
+ 'tag': 'scale'
1346
+ });
1347
+ }
1348
+
1349
+ if (!prop['chart.xaxis'] && prop['chart.ymin'] == 0) {
1350
+ RG.text2(this, {
1351
+ font:font,
1352
+ size: text_size,
1353
+ x: xPos + offsetx,
1354
+ y: ca.height - this.gutterBottom + offsety,
1355
+ valign: 'center',
1356
+ halign:align,
1357
+ boundin: boxed,
1358
+ boundingFill: 'white',
1359
+ text: RG.numberFormat(this, (0).toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1360
+ tag: 'scale'
1361
+ });
1362
+ }
1363
+ }
1364
+ }
1365
+
1366
+ if ( (prop['chart.ymin'] || prop['chart.scale.zerostart']) && !invert) {
1367
+ RG.text2(this, {
1368
+ 'font':font,
1369
+ 'size': text_size,
1370
+ 'x': xPos + offsetx,
1371
+ 'y': ca.height - this.gutterBottom + offsety,
1372
+ 'valign': 'center',
1373
+ 'halign':align,
1374
+ 'bounding': boxed,
1375
+ 'boundingFill': 'white',
1376
+ 'text': RG.numberFormat(this, prop['chart.ymin'].toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1377
+ 'tag': 'scale'
1378
+ });
1379
+
1380
+ } else if (invert) {
1381
+ RG.text2(this, {
1382
+ 'font':font,
1383
+ 'size': text_size,
1384
+ 'x': xPos + offsetx,
1385
+ 'y': this.gutterTop + offsety,
1386
+ 'valign': 'center',
1387
+ 'halign':align,
1388
+ 'bounding': boxed,
1389
+ 'boundingFill': 'white',
1390
+ 'text': RG.numberFormat(this, prop['chart.ymin'].toFixed(this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']), units_pre, units_post),
1391
+ 'tag': 'scale'
1392
+ });
1393
+ }
1394
+ }
1395
+ }
1396
+ }
1397
+
1398
+
1399
+
1400
+
1401
+ /**
1402
+ * Draw an X scale
1403
+ */
1404
+ if (prop['chart.xscale']) {
1405
+
1406
+ var numXLabels = prop['chart.xscale.numlabels'],
1407
+ y = ca.height - this.gutterBottom + 5 + (text_size / 2),
1408
+ units_pre_x = prop['chart.xscale.units.pre'],
1409
+ units_post_x = prop['chart.xscale.units.post'],
1410
+ decimals = prop['chart.xscale.decimals'],
1411
+ point = prop['chart.xscale.point'],
1412
+ thousand = prop['chart.xscale.thousand'],
1413
+ color = prop['chart.labels.color'],
1414
+ bold = prop['chart.labels.bold'],
1415
+ offsetx = prop['chart.labels.offsetx'],
1416
+ offsety = prop['chart.labels.offsety']
1417
+
1418
+
1419
+ if (!prop['chart.xmax']) {
1420
+
1421
+ var xmax = 0;
1422
+ var xmin = prop['chart.xmin'];
1423
+
1424
+ for (var ds=0,len=this.data.length; ds<len; ds+=1) {
1425
+ for (var point=0,len2=this.data[ds].length; point<len2; point+=1) {
1426
+ xmax = Math.max(xmax, this.data[ds][point][0]);
1427
+ }
1428
+ }
1429
+ } else {
1430
+ xmax = prop['chart.xmax'];
1431
+ xmin = prop['chart.xmin']
1432
+ }
1433
+
1434
+ this.xscale2 = RG.getScale2(this, {
1435
+ 'max':xmax,
1436
+ 'min': xmin,
1437
+ 'scale.decimals': decimals,
1438
+ 'scale.point': point,
1439
+ 'scale.thousand': thousand,
1440
+ 'units.pre': units_pre_x,
1441
+ 'units.post': units_post_x,
1442
+ 'ylabels.count': numXLabels,
1443
+ 'strict': true
1444
+ });
1445
+
1446
+ this.Set('chart.xmax', this.xscale2.max);
1447
+ var interval = (ca.width - this.gutterLeft - this.gutterRight) / this.xscale2.labels.length;
1448
+
1449
+ for (var i=0,len=this.xscale2.labels.length; i<len; i+=1) {
1450
+
1451
+ var num = ( (prop['chart.xmax'] - prop['chart.xmin']) * ((i+1) / numXLabels)) + (xmin || 0),
1452
+ x = this.gutterLeft + ((i+1) * interval),
1453
+
1454
+ // Repeated a few lines down
1455
+ text = typeof prop['chart.xscale.formatter'] === 'function' ? String(prop['chart.xscale.formatter'](this, num)) : this.xscale2.labels[i];
1456
+
1457
+ RG.text2(this, {
1458
+ 'color': color,
1459
+ 'font':font,
1460
+ 'size': text_size,
1461
+ 'bold': bold,
1462
+ 'x': x + offsetx,
1463
+ 'y': y + offsety,
1464
+ 'valign': 'center',
1465
+ 'halign':'center',
1466
+ 'text':text,
1467
+ 'tag': 'xscale'
1468
+ });
1469
+ }
1470
+
1471
+ // If the Y axis is on the right hand side - draw the left most X label
1472
+ // ** Always added now **
1473
+
1474
+ // Repeated a few lines up
1475
+ var text = typeof prop['chart.xscale.formatter'] === 'function' ? String(prop['chart.xscale.formatter'](this, prop['chart.xmin'])) : String(prop['chart.xmin']);
1476
+
1477
+ RG.text2(this, {
1478
+ 'color': color,
1479
+ 'font':font,
1480
+ 'size': text_size,
1481
+ 'bold':bold,
1482
+ 'x': this.gutterLeft + offsetx,
1483
+ 'y': y + offsety,
1484
+ 'valign': 'center',
1485
+ 'halign':'center',
1486
+ 'text': text,
1487
+ 'tag': 'xscale'
1488
+ });
1489
+
1490
+ /**
1491
+ * Draw X labels
1492
+ */
1493
+ } else {
1494
+
1495
+ // Put the text on the X axis
1496
+ var graphArea = ca.width - this.gutterLeft - this.gutterRight;
1497
+ var xInterval = graphArea / prop['chart.labels'].length;
1498
+ var xPos = this.gutterLeft;
1499
+ var yPos = (ca.height - this.gutterBottom) + 3;
1500
+ var labels = prop['chart.labels'];
1501
+ var color = prop['chart.labels.color'];
1502
+ var bold = prop['chart.labels.bold'];
1503
+ var offsetx = prop['chart.labels.offsetx'];
1504
+ var offsety = prop['chart.labels.offsety'];
1505
+
1506
+ /**
1507
+ * Text angle
1508
+ */
1509
+ var angle = 0;
1510
+ var valign = 'top';
1511
+ var halign = 'center';
1512
+
1513
+ if (prop['chart.text.angle'] > 0) {
1514
+ angle = -1 * prop['chart.text.angle'];
1515
+ valign = 'center';
1516
+ halign = 'right';
1517
+ yPos += 10;
1518
+ }
1519
+
1520
+ for (i=0; i<labels.length; ++i) {
1521
+
1522
+ if (typeof(labels[i]) == 'object') {
1523
+
1524
+ if (prop['chart.labels.specific.align'] == 'center') {
1525
+ var rightEdge = 0;
1526
+
1527
+ if (labels[i+1] && labels[i+1][1]) {
1528
+ rightEdge = labels[i+1][1];
1529
+ } else {
1530
+ rightEdge = prop['chart.xmax'];
1531
+ }
1532
+
1533
+ var offset = (this.getXCoord(rightEdge) - this.getXCoord(labels[i][1])) / 2;
1534
+
1535
+ } else {
1536
+ var offset = 5;
1537
+ }
1538
+
1539
+
1540
+ RG.text2(this, {
1541
+ 'color': color,
1542
+ 'font':font,
1543
+ 'size': prop['chart.text.size'],
1544
+ 'bold': bold,
1545
+ 'x': this.getXCoord(labels[i][1]) + offset + offsetx,
1546
+ 'y': yPos + offsety,
1547
+ 'valign': valign,
1548
+ 'halign':angle != 0 ? 'right' : (prop['chart.labels.specific.align'] == 'center' ? 'center' : 'left'),
1549
+ 'text':String(labels[i][0]),
1550
+ 'angle':angle,
1551
+ 'marker':false,
1552
+ 'tag': 'labels.specific'
1553
+ });
1554
+
1555
+ /**
1556
+ * Draw the gray indicator line
1557
+ */
1558
+ co.beginPath();
1559
+ co.strokeStyle = '#bbb';
1560
+ co.moveTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom);
1561
+ co.lineTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom + 20);
1562
+ co.stroke();
1563
+
1564
+ } else {
1565
+
1566
+ RG.text2(this, {
1567
+ 'color': color,
1568
+ 'font':font,
1569
+ 'size': prop['chart.text.size'],
1570
+ 'bold': bold,
1571
+ 'x': xPos + (xInterval / 2) + offsetx,
1572
+ 'y': yPos + offsety,
1573
+ 'valign': valign,
1574
+ 'halign':halign,
1575
+ 'text':String(labels[i]),
1576
+ 'angle':angle,
1577
+ 'tag': 'labels'
1578
+ });
1579
+ }
1580
+
1581
+ // Do this for the next time around
1582
+ xPos += xInterval;
1583
+ }
1584
+
1585
+ /**
1586
+ * Draw the final indicator line
1587
+ */
1588
+ if (typeof(labels[0]) == 'object') {
1589
+ co.beginPath();
1590
+ co.strokeStyle = '#bbb';
1591
+ co.moveTo(this.gutterLeft + graphArea, ca.height - this.gutterBottom);
1592
+ co.lineTo(this.gutterLeft + graphArea, ca.height - this.gutterBottom + 20);
1593
+ co.stroke();
1594
+ }
1595
+ }
1596
+ };
1597
+
1598
+
1599
+
1600
+
1601
+
1602
+
1603
+
1604
+
1605
+
1606
+
1607
+
1608
+
1609
+
1610
+
1611
+ /**
1612
+ * Draws the actual scatter graph marks
1613
+ *
1614
+ * @param i integer The dataset index
1615
+ */
1616
+ this.drawMarks =
1617
+ this.DrawMarks = function (i)
1618
+ {
1619
+ /**
1620
+ * Reset the coords array
1621
+ */
1622
+ this.coords[i] = [];
1623
+
1624
+ /**
1625
+ * Plot the values
1626
+ */
1627
+ var xmax = prop['chart.xmax'];
1628
+ var default_color = prop['chart.defaultcolor'];
1629
+
1630
+ for (var j=0,len=this.data[i].length; j<len; j+=1) {
1631
+ /**
1632
+ * This is here because tooltips are optional
1633
+ */
1634
+ var data_points = this.data[i];
1635
+
1636
+ // Allow for null points
1637
+ if (RG.isNull(data_points[j])) {
1638
+ continue;
1639
+ }
1640
+
1641
+ var xCoord = data_points[j][0];
1642
+ var yCoord = data_points[j][1];
1643
+ var color = data_points[j][2] ? data_points[j][2] : default_color;
1644
+ var tooltip = (data_points[j] && data_points[j][3]) ? data_points[j][3] : null;
1645
+
1646
+
1647
+ this.DrawMark(
1648
+ i,
1649
+ xCoord,
1650
+ yCoord,
1651
+ xmax,
1652
+ this.scale2.max,
1653
+ color,
1654
+ tooltip,
1655
+ this.coords[i],
1656
+ data_points,
1657
+ j
1658
+ );
1659
+ }
1660
+ };
1661
+
1662
+
1663
+
1664
+
1665
+ /**
1666
+ * Draws a single scatter mark
1667
+ */
1668
+ this.drawMark =
1669
+ this.DrawMark = function (data_set_index, x, y, xMax, yMax, color, tooltip, coords, data, data_index)
1670
+ {
1671
+ var tickmarks = prop['chart.tickmarks'],
1672
+ tickSize = prop['chart.ticksize'],
1673
+ xMin = prop['chart.xmin'],
1674
+ x = ((x - xMin) / (xMax - xMin)) * (ca.width - this.gutterLeft - this.gutterRight),
1675
+ originalX = x,
1676
+ originalY = y;
1677
+
1678
+ /**
1679
+ * This allows chart.tickmarks to be an array
1680
+ */
1681
+ if (tickmarks && typeof(tickmarks) == 'object') {
1682
+ tickmarks = tickmarks[data_set_index];
1683
+ }
1684
+
1685
+
1686
+ /**
1687
+ * This allows chart.ticksize to be an array
1688
+ */
1689
+ if (typeof(tickSize) == 'object') {
1690
+ var tickSize = tickSize[data_set_index];
1691
+ var halfTickSize = tickSize / 2;
1692
+ } else {
1693
+ var halfTickSize = tickSize / 2;
1694
+ }
1695
+
1696
+
1697
+ /**
1698
+ * This bit is for boxplots only
1699
+ */
1700
+ if ( y
1701
+ && typeof y === 'object'
1702
+ && typeof y[0] === 'number'
1703
+ && typeof y[1] === 'number'
1704
+ && typeof y[2] === 'number'
1705
+ && typeof y[3] === 'number'
1706
+ && typeof y[4] === 'number'
1707
+ ) {
1708
+
1709
+ //var yMin = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
1710
+ this.Set('chart.boxplot', true);
1711
+ //this.graphheight = ca.height - this.gutterTop - this.gutterBottom;
1712
+
1713
+ //if (prop['chart.xaxispos'] == 'center') {
1714
+ // this.graphheight /= 2;
1715
+ //}
1716
+
1717
+
1718
+ var y0 = this.getYCoord(y[0]),
1719
+ y1 = this.getYCoord(y[1]),
1720
+ y2 = this.getYCoord(y[2]),
1721
+ y3 = this.getYCoord(y[3]),
1722
+ y4 = this.getYCoord(y[4]),
1723
+ col1 = y[5],
1724
+ col2 = y[6],
1725
+ boxWidth = typeof y[7] == 'number' ? y[7] : prop['chart.boxplot.width'];
1726
+
1727
+ //var y = this.graphheight - y2;
1728
+
1729
+ } else {
1730
+
1731
+ /**
1732
+ * The new way of getting the Y coord. This function (should) handle everything
1733
+ */
1734
+ var yCoord = this.getYCoord(y);
1735
+ }
1736
+
1737
+ //if (prop['chart.xaxispos'] == 'center'] {
1738
+ // y /= 2;
1739
+ // y += this.halfGraphHeight;
1740
+ //
1741
+ // if (prop['chart.ylabels.invert']) {
1742
+ // p(y)
1743
+ // }
1744
+ //}
1745
+
1746
+ /**
1747
+ * Account for the X axis being at the centre
1748
+ */
1749
+ // This is so that points are on the graph, and not the gutter - which helps
1750
+ x += this.gutterLeft;
1751
+ //y = ca.height - this.gutterBottom - y;
1752
+
1753
+
1754
+
1755
+
1756
+ co.beginPath();
1757
+
1758
+ // Color
1759
+ co.strokeStyle = color;
1760
+
1761
+
1762
+
1763
+ /**
1764
+ * Boxplots
1765
+ */
1766
+ if (prop['chart.boxplot']) {
1767
+
1768
+ // boxWidth is a scale value, so convert it to a pixel vlue
1769
+ boxWidth = (boxWidth / prop['chart.xmax']) * (ca.width - this.gutterLeft - this.gutterRight);
1770
+
1771
+ var halfBoxWidth = boxWidth / 2;
1772
+
1773
+ if (prop['chart.line.visible']) {
1774
+ co.beginPath();
1775
+
1776
+ // Set the outline color of the box
1777
+
1778
+ if (typeof y[8] === 'string') {
1779
+ co.strokeStyle = y[8];
1780
+ }
1781
+ co.strokeRect(x - halfBoxWidth, y1, boxWidth, y3 - y1);
1782
+
1783
+ // Draw the upper coloured box if a value is specified
1784
+ if (col1) {
1785
+ co.fillStyle = col1;
1786
+ co.fillRect(x - halfBoxWidth, y1, boxWidth, y2 - y1);
1787
+ }
1788
+
1789
+ // Draw the lower coloured box if a value is specified
1790
+ if (col2) {
1791
+ co.fillStyle = col2;
1792
+ co.fillRect(x - halfBoxWidth, y2, boxWidth, y3 - y2);
1793
+ }
1794
+ co.stroke();
1795
+
1796
+ // Now draw the whiskers
1797
+ co.beginPath();
1798
+ if (prop['chart.boxplot.capped']) {
1799
+ co.moveTo(x - halfBoxWidth, ma.round(y0));
1800
+ co.lineTo(x + halfBoxWidth, ma.round(y0));
1801
+ }
1802
+
1803
+ co.moveTo(ma.round(x), y0);
1804
+ co.lineTo(ma.round(x ), y1);
1805
+
1806
+ if (prop['chart.boxplot.capped']) {
1807
+ co.moveTo(x - halfBoxWidth, ma.round(y4));
1808
+ co.lineTo(x + halfBoxWidth, ma.round(y4));
1809
+ }
1810
+
1811
+ co.moveTo(ma.round(x), y4);
1812
+ co.lineTo(ma.round(x), y3);
1813
+
1814
+ co.stroke();
1815
+ }
1816
+ }
1817
+
1818
+
1819
+ /**
1820
+ * Draw the tickmark, but not for boxplots
1821
+ */
1822
+ if (prop['chart.line.visible'] && typeof(y) == 'number' && !y0 && !y1 && !y2 && !y3 && !y4) {
1823
+
1824
+ if (tickmarks == 'circle') {
1825
+ co.arc(x, yCoord, halfTickSize, 0, 6.28, 0);
1826
+ co.fillStyle = color;
1827
+ co.fill();
1828
+
1829
+ } else if (tickmarks == 'plus') {
1830
+
1831
+ co.moveTo(x, yCoord - halfTickSize);
1832
+ co.lineTo(x, yCoord + halfTickSize);
1833
+ co.moveTo(x - halfTickSize, yCoord);
1834
+ co.lineTo(x + halfTickSize, yCoord);
1835
+ co.stroke();
1836
+
1837
+ } else if (tickmarks == 'square') {
1838
+ co.strokeStyle = color;
1839
+ co.fillStyle = color;
1840
+ co.fillRect(
1841
+ x - halfTickSize,
1842
+ yCoord - halfTickSize,
1843
+ tickSize,
1844
+ tickSize
1845
+ );
1846
+ //co.fill();
1847
+
1848
+ } else if (tickmarks == 'cross') {
1849
+
1850
+ co.moveTo(x - halfTickSize, yCoord - halfTickSize);
1851
+ co.lineTo(x + halfTickSize, yCoord + halfTickSize);
1852
+ co.moveTo(x + halfTickSize, yCoord - halfTickSize);
1853
+ co.lineTo(x - halfTickSize, yCoord + halfTickSize);
1854
+
1855
+ co.stroke();
1856
+
1857
+ /**
1858
+ * Diamond shape tickmarks
1859
+ */
1860
+ } else if (tickmarks == 'diamond') {
1861
+ co.fillStyle = co.strokeStyle;
1862
+
1863
+ co.moveTo(x, yCoord - halfTickSize);
1864
+ co.lineTo(x + halfTickSize, yCoord);
1865
+ co.lineTo(x, yCoord + halfTickSize);
1866
+ co.lineTo(x - halfTickSize, yCoord);
1867
+ co.lineTo(x, yCoord - halfTickSize);
1868
+
1869
+ co.fill();
1870
+ co.stroke();
1871
+
1872
+ /**
1873
+ * Custom tickmark style
1874
+ */
1875
+ } else if (typeof(tickmarks) == 'function') {
1876
+
1877
+ var graphWidth = ca.width - this.gutterLeft - this.gutterRight,
1878
+ graphheight = ca.height - this.gutterTop - this.gutterBottom,
1879
+ xVal = ((x - this.gutterLeft) / graphWidth) * xMax,
1880
+ yVal = ((graphheight - (yCoord - this.gutterTop)) / graphheight) * yMax;
1881
+
1882
+ tickmarks(this, data, x, yCoord, xVal, yVal, xMax, yMax, color, data_set_index, data_index)
1883
+
1884
+
1885
+
1886
+
1887
+
1888
+
1889
+
1890
+
1891
+
1892
+
1893
+
1894
+
1895
+
1896
+
1897
+
1898
+
1899
+
1900
+ /**
1901
+ * Image based tickmark
1902
+ */
1903
+ // lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index
1904
+ } else if (
1905
+ typeof tickmarks === 'string' &&
1906
+ (
1907
+ tickmarks.substr(0, 6) === 'image:' ||
1908
+ tickmarks.substr(0, 5) === 'data:' ||
1909
+ tickmarks.substr(0, 1) === '/' ||
1910
+ tickmarks.substr(0, 3) === '../' ||
1911
+ tickmarks.substr(0, 7) === 'images/'
1912
+ )
1913
+ ) {
1914
+
1915
+ var img = new Image();
1916
+
1917
+ if (tickmarks.substr(0, 6) === 'image:') {
1918
+ img.src = tickmarks.substr(6);
1919
+ } else {
1920
+ img.src = tickmarks;
1921
+ }
1922
+
1923
+
1924
+ img.onload = function ()
1925
+ {
1926
+ if (prop['chart.tickmarks.image.halign'] === 'center') x -= (this.width / 2);
1927
+ if (prop['chart.tickmarks.image.halign'] === 'right') x -= this.width;
1928
+
1929
+ if (prop['chart.tickmarks.image.valign'] === 'center') yCoord -= (this.height / 2);
1930
+ if (prop['chart.tickmarks.image.valign'] === 'bottom') yCoord -= this.height;
1931
+
1932
+ x += prop['chart.tickmarks.image.offsetx'];
1933
+ yCoord += prop['chart.tickmarks.image.offsety'];
1934
+
1935
+ co.drawImage(this, x, yCoord);
1936
+ }
1937
+
1938
+
1939
+
1940
+
1941
+
1942
+ /**
1943
+ * No tickmarks
1944
+ */
1945
+ } else if (tickmarks === null) {
1946
+
1947
+ /**
1948
+ * Unknown tickmark type
1949
+ */
1950
+ } else {
1951
+ alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks );
1952
+ }
1953
+ }
1954
+
1955
+ /**
1956
+ * Add the tickmark to the coords array
1957
+ */
1958
+
1959
+ if ( prop['chart.boxplot']
1960
+ && typeof y0 === 'number'
1961
+ && typeof y1 === 'number'
1962
+ && typeof y2 === 'number'
1963
+ && typeof y3 === 'number'
1964
+ && typeof y4 === 'number') {
1965
+
1966
+ x = [x - halfBoxWidth, x + halfBoxWidth];
1967
+ yCoord = [y0, y1, y2, y3, y4];
1968
+ }
1969
+
1970
+ coords.push([x, yCoord, tooltip]);
1971
+ };
1972
+
1973
+
1974
+
1975
+
1976
+ /**
1977
+ * Draws an optional line connecting the tick marks.
1978
+ *
1979
+ * @param i The index of the dataset to use
1980
+ */
1981
+ this.drawLine =
1982
+ this.DrawLine = function (i)
1983
+ {
1984
+ if (typeof(prop['chart.line.visible']) == 'boolean' && prop['chart.line.visible'] == false) {
1985
+ return;
1986
+ }
1987
+
1988
+ if (prop['chart.line'] && this.coords[i].length >= 2) {
1989
+
1990
+ if (prop['chart.line.dash'] && typeof co.setLineDash === 'function') {
1991
+ co.setLineDash(prop['chart.line.dash']);
1992
+ }
1993
+
1994
+ co.lineCap = 'round';
1995
+ co.lineJoin = 'round';
1996
+ co.lineWidth = this.getLineWidth(i);// i is the index of the set of coordinates
1997
+ co.strokeStyle = prop['chart.line.colors'][i];
1998
+
1999
+ co.beginPath();
2000
+
2001
+ var prevY = null;
2002
+ var currY = null;
2003
+
2004
+ for (var j=0,len=this.coords[i].length; j<len; j+=1) {
2005
+
2006
+
2007
+ var xPos = this.coords[i][j][0];
2008
+ var yPos = this.coords[i][j][1];
2009
+
2010
+ if (j > 0) prevY = this.coords[i][j - 1][1];
2011
+ currY = yPos;
2012
+
2013
+ if (j == 0 || RG.is_null(prevY) || RG.is_null(currY)) {
2014
+ co.moveTo(xPos, yPos);
2015
+ } else {
2016
+
2017
+ // Stepped?
2018
+ var stepped = prop['chart.line.stepped'];
2019
+
2020
+ if ( (typeof stepped == 'boolean' && stepped)
2021
+ || (typeof stepped == 'object' && stepped[i])
2022
+ ) {
2023
+ co.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]);
2024
+ }
2025
+
2026
+ co.lineTo(xPos, yPos);
2027
+ }
2028
+ }
2029
+ co.stroke();
2030
+
2031
+ /**
2032
+ * Set the linedash back to the default
2033
+ */
2034
+ if (prop['chart.line.dash'] && typeof co.setLineDash === 'function') {
2035
+ co.setLineDash([1,0]);
2036
+ }
2037
+ }
2038
+
2039
+ /**
2040
+ * Set the linewidth back to 1
2041
+ */
2042
+ co.lineWidth = 1;
2043
+ };
2044
+
2045
+
2046
+
2047
+
2048
+ /**
2049
+ * Returns the linewidth
2050
+ *
2051
+ * @param number i The index of the "line" (/set of coordinates)
2052
+ */
2053
+ this.getLineWidth =
2054
+ this.GetLineWidth = function (i)
2055
+ {
2056
+ var linewidth = prop['chart.line.linewidth'];
2057
+
2058
+ if (typeof linewidth == 'number') {
2059
+ return linewidth;
2060
+
2061
+ } else if (typeof linewidth == 'object') {
2062
+ if (linewidth[i]) {
2063
+ return linewidth[i];
2064
+ } else {
2065
+ return linewidth[0];
2066
+ }
2067
+
2068
+ alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');
2069
+ }
2070
+ };
2071
+
2072
+
2073
+
2074
+
2075
+ /**
2076
+ * Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function
2077
+ * is not common
2078
+ */
2079
+ this.drawVBars =
2080
+ this.DrawVBars = function ()
2081
+ {
2082
+
2083
+ var vbars = prop['chart.background.vbars'];
2084
+ var graphWidth = ca.width - this.gutterLeft - this.gutterRight;
2085
+
2086
+ if (vbars) {
2087
+
2088
+ var xmax = prop['chart.xmax'];
2089
+ var xmin = prop['chart.xmin'];
2090
+
2091
+ for (var i=0,len=vbars.length; i<len; i+=1) {
2092
+
2093
+ var key = i;
2094
+ var value = vbars[key];
2095
+
2096
+ /**
2097
+ * Accomodate date/time values
2098
+ */
2099
+ if (typeof value[0] == 'string') value[0] = RG.parseDate(value[0]);
2100
+ if (typeof value[1] == 'string') value[1] = RG.parseDate(value[1]) - value[0];
2101
+
2102
+ var x = (( (value[0] - xmin) / (xmax - xmin) ) * graphWidth) + this.gutterLeft;
2103
+ var width = (value[1] / (xmax - xmin) ) * graphWidth;
2104
+
2105
+ co.fillStyle = value[2];
2106
+ co.fillRect(x, this.gutterTop, width, (ca.height - this.gutterTop - this.gutterBottom));
2107
+ }
2108
+ }
2109
+ };
2110
+
2111
+
2112
+
2113
+
2114
+ /**
2115
+ * Draws in-graph labels.
2116
+ *
2117
+ * @param object obj The graph object
2118
+ */
2119
+ this.drawInGraphLabels =
2120
+ this.DrawInGraphLabels = function (obj)
2121
+ {
2122
+ var labels = obj.Get('chart.labels.ingraph');
2123
+ var labels_processed = [];
2124
+
2125
+ if (!labels) {
2126
+ return;
2127
+ }
2128
+
2129
+ // Defaults
2130
+ var fgcolor = 'black';
2131
+ var bgcolor = 'white';
2132
+ var direction = 1;
2133
+
2134
+ /**
2135
+ * Preprocess the labels array. Numbers are expanded
2136
+ */
2137
+ for (var i=0,len=labels.length; i<len; i+=1) {
2138
+ if (typeof(labels[i]) == 'number') {
2139
+ for (var j=0; j<labels[i]; ++j) {
2140
+ labels_processed.push(null);
2141
+ }
2142
+ } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
2143
+ labels_processed.push(labels[i]);
2144
+
2145
+ } else {
2146
+ labels_processed.push('');
2147
+ }
2148
+ }
2149
+
2150
+ /**
2151
+ * Turn off any shadow
2152
+ */
2153
+ RG.NoShadow(obj);
2154
+
2155
+ if (labels_processed && labels_processed.length > 0) {
2156
+
2157
+ var i=0;
2158
+
2159
+ for (var set=0; set<obj.coords.length; ++set) {
2160
+ for (var point = 0; point<obj.coords[set].length; ++point) {
2161
+ if (labels_processed[i]) {
2162
+ var x = obj.coords[set][point][0];
2163
+ var y = obj.coords[set][point][1];
2164
+ var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
2165
+
2166
+ var text_x = x;
2167
+ var text_y = y - 5 - length;
2168
+
2169
+ co.moveTo(x, y - 5);
2170
+ co.lineTo(x, y - 5 - length);
2171
+
2172
+ co.stroke();
2173
+ co.beginPath();
2174
+
2175
+ // This draws the arrow
2176
+ co.moveTo(x, y - 5);
2177
+ co.lineTo(x - 3, y - 10);
2178
+ co.lineTo(x + 3, y - 10);
2179
+ co.closePath();
2180
+
2181
+
2182
+ co.beginPath();
2183
+ // Fore ground color
2184
+ co.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
2185
+ RG.text2(this, {
2186
+ 'font':obj.Get('chart.text.font'),
2187
+ 'size':obj.Get('chart.text.size'),
2188
+ 'x':text_x,
2189
+ 'y':text_y,
2190
+ 'text':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
2191
+ 'valign':'bottom',
2192
+ 'halign':'center',
2193
+ 'bounding':true,
2194
+ 'bounding.fill':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white',
2195
+ 'tag': 'labels.ingraph'
2196
+ });
2197
+ co.fill();
2198
+ }
2199
+
2200
+ i++;
2201
+ }
2202
+ }
2203
+ }
2204
+ };
2205
+
2206
+
2207
+
2208
+
2209
+ /**
2210
+ * This function makes it much easier to get the (if any) point that is currently being hovered over.
2211
+ *
2212
+ * @param object e The event object
2213
+ */
2214
+ this.getShape =
2215
+ this.getPoint = function (e)
2216
+ {
2217
+ var mouseXY = RG.getMouseXY(e);
2218
+ var mouseX = mouseXY[0];
2219
+ var mouseY = mouseXY[1];
2220
+ var overHotspot = false;
2221
+ var offset = prop['chart.tooltips.hotspot']; // This is how far the hotspot extends
2222
+
2223
+ for (var set=0,len=this.coords.length; set<len; ++set) {
2224
+ for (var i=0,len2=this.coords[set].length; i<len2; ++i) {
2225
+
2226
+ var x = this.coords[set][i][0];
2227
+ var y = this.coords[set][i][1];
2228
+ var tooltip = this.data[set][i][3];
2229
+
2230
+ if (typeof(y) == 'number') {
2231
+ if (mouseX <= (x + offset) &&
2232
+ mouseX >= (x - offset) &&
2233
+ mouseY <= (y + offset) &&
2234
+ mouseY >= (y - offset)) {
2235
+
2236
+ var tooltip = RG.parseTooltipText(this.data[set][i][3], 0);
2237
+ var index_adjusted = i;
2238
+
2239
+ for (var ds=(set-1); ds >=0; --ds) {
2240
+ index_adjusted += this.data[ds].length;
2241
+ }
2242
+
2243
+ return {
2244
+ 0: this, 1: x, 2: y, 3: set, 4: i, 5: this.data[set][i][3],
2245
+ 'object': this, 'x': x, 'y': y, 'dataset': set, 'index': i, 'tooltip': tooltip, 'index_adjusted': index_adjusted
2246
+ };
2247
+ }
2248
+ } else if (RG.is_null(y)) {
2249
+ // Nothing to see here
2250
+
2251
+ } else {
2252
+
2253
+ var mark = this.data[set][i];
2254
+
2255
+ /**
2256
+ * Determine the width
2257
+ */
2258
+ var width = prop['chart.boxplot.width'];
2259
+
2260
+ if (typeof(mark[1][7]) == 'number') {
2261
+ width = mark[1][7];
2262
+ }
2263
+
2264
+ if ( typeof(x) == 'object'
2265
+ && mouseX > x[0]
2266
+ && mouseX < x[1]
2267
+ && mouseY < y[1]
2268
+ && mouseY > y[3]
2269
+ ) {
2270
+
2271
+ var tooltip = RG.parseTooltipText(this.data[set][i][3], 0);
2272
+
2273
+ return {
2274
+ 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],
2275
+ 'object': this, 'x': x[0], 'y': y[1], 'width': x[1] - x[0], 'height': y[3] - y[1], 'dataset': set, 'index': i, 'tooltip': tooltip
2276
+ };
2277
+ }
2278
+ }
2279
+ }
2280
+ }
2281
+ };
2282
+
2283
+
2284
+
2285
+
2286
+ /**
2287
+ * Draws the above line labels
2288
+ */
2289
+ this.drawAboveLabels =
2290
+ this.DrawAboveLabels = function ()
2291
+ {
2292
+ var size = prop['chart.labels.above.size'];
2293
+ var font = prop['chart.text.font'];
2294
+ var units_pre = prop['chart.units.pre'];
2295
+ var units_post = prop['chart.units.post'];
2296
+
2297
+
2298
+ for (var set=0,len=this.coords.length; set<len; ++set) {
2299
+ for (var point=0,len2=this.coords[set].length; point<len2; ++point) {
2300
+
2301
+ var x_val = this.data[set][point][0];
2302
+ var y_val = this.data[set][point][1];
2303
+
2304
+ if (!RG.is_null(y_val)) {
2305
+
2306
+ // Use the top most value from a box plot
2307
+ if (RG.is_array(y_val)) {
2308
+ var max = 0;
2309
+ for (var i=0; i<y_val; ++i) {
2310
+ max = Math.max(max, y_val[i]);
2311
+ }
2312
+
2313
+ y_val = max;
2314
+ }
2315
+
2316
+ var x_pos = this.coords[set][point][0];
2317
+ var y_pos = this.coords[set][point][1];
2318
+
2319
+ RG.Text2(this, {
2320
+ 'font':font,
2321
+ 'size':size,
2322
+ 'x':x_pos,
2323
+ 'y':y_pos - 5 - size,
2324
+ 'text':x_val.toFixed(prop['chart.labels.above.decimals']) + ', ' + y_val.toFixed(prop['chart.labels.above.decimals']),
2325
+ 'valign':'center',
2326
+ 'halign':'center',
2327
+ 'bounding':true,
2328
+ 'boundingFill':'rgba(255, 255, 255, 0.7)',
2329
+ 'tag': 'labels.above'
2330
+ });
2331
+ }
2332
+ }
2333
+ }
2334
+ };
2335
+
2336
+
2337
+
2338
+
2339
+ /**
2340
+ * When you click on the chart, this method can return the Y value at that point. It works for any point on the
2341
+ * chart (that is inside the gutters) - not just points within the Bars.
2342
+ *
2343
+ * @param object e The event object
2344
+ */
2345
+ this.getYValue =
2346
+ this.getValue = function (arg)
2347
+ {
2348
+ if (arg.length == 2) {
2349
+ var mouseX = arg[0];
2350
+ var mouseY = arg[1];
2351
+ } else {
2352
+ var mouseCoords = RG.getMouseXY(arg);
2353
+ var mouseX = mouseCoords[0];
2354
+ var mouseY = mouseCoords[1];
2355
+ }
2356
+
2357
+ var obj = this;
2358
+
2359
+ if ( mouseY < this.gutterTop
2360
+ || mouseY > (ca.height - this.gutterBottom)
2361
+ || mouseX < this.gutterLeft
2362
+ || mouseX > (ca.width - this.gutterRight)
2363
+ ) {
2364
+ return null;
2365
+ }
2366
+
2367
+ if (prop['chart.xaxispos'] == 'center') {
2368
+ var value = (((this.grapharea / 2) - (mouseY - this.gutterTop)) / this.grapharea) * (this.max - this.min)
2369
+ value *= 2;
2370
+
2371
+
2372
+ // Account for each side of the X axis
2373
+ if (value >= 0) {
2374
+ value += this.min
2375
+
2376
+ if (prop['chart.ylabels.invert']) {
2377
+ value -= this.min;
2378
+ value = this.max - value;
2379
+ }
2380
+
2381
+ } else {
2382
+
2383
+ value -= this.min;
2384
+ if (prop['chart.ylabels.invert']) {
2385
+ value += this.min;
2386
+ value = this.max + value;
2387
+ value *= -1;
2388
+ }
2389
+ }
2390
+
2391
+ } else {
2392
+
2393
+ var value = ((this.grapharea - (mouseY - this.gutterTop)) / this.grapharea) * (this.max - this.min)
2394
+ value += this.min;
2395
+
2396
+ if (prop['chart.ylabels.invert']) {
2397
+ value -= this.min;
2398
+ value = this.max - value;
2399
+ }
2400
+ }
2401
+
2402
+ return value;
2403
+ };
2404
+
2405
+
2406
+
2407
+
2408
+ /**
2409
+ * When you click on the chart, this method can return the X value at that point.
2410
+ *
2411
+ * @param mixed arg This can either be an event object or the X coordinate
2412
+ * @param number If specifying the X coord as the first arg then this should be the Y coord
2413
+ */
2414
+ this.getXValue = function (arg)
2415
+ {
2416
+ if (arg.length == 2) {
2417
+ var mouseX = arg[0];
2418
+ var mouseY = arg[1];
2419
+ } else {
2420
+ var mouseXY = RG.getMouseXY(arg);
2421
+ var mouseX = mouseXY[0];
2422
+ var mouseY = mouseXY[1];
2423
+ }
2424
+ var obj = this;
2425
+
2426
+ if ( mouseY < this.gutterTop
2427
+ || mouseY > (ca.height - this.gutterBottom)
2428
+ || mouseX < this.gutterLeft
2429
+ || mouseX > (ca.width - this.gutterRight)
2430
+ ) {
2431
+ return null;
2432
+ }
2433
+
2434
+ var width = (ca.width - this.gutterLeft - this.gutterRight);
2435
+ var value = ((mouseX - this.gutterLeft) / width) * (prop['chart.xmax'] - prop['chart.xmin'])
2436
+ value += prop['chart.xmin'];
2437
+
2438
+ return value;
2439
+ };
2440
+
2441
+
2442
+
2443
+
2444
+ /**
2445
+ * Each object type has its own Highlight() function which highlights the appropriate shape
2446
+ *
2447
+ * @param object shape The shape to highlight
2448
+ */
2449
+ this.highlight =
2450
+ this.Highlight = function (shape)
2451
+ {
2452
+ if (typeof prop['chart.highlight.style'] === 'function') {
2453
+ (prop['chart.highlight.style'])(shape);
2454
+ } else {
2455
+ // Boxplot highlight
2456
+ if (shape['height']) {
2457
+ RG.Highlight.Rect(this, shape);
2458
+
2459
+ // Point highlight
2460
+ } else {
2461
+ RG.Highlight.Point(this, shape);
2462
+ }
2463
+ }
2464
+ };
2465
+
2466
+
2467
+
2468
+
2469
+ /**
2470
+ * The getObjectByXY() worker method. Don't call this call:
2471
+ *
2472
+ * RG.ObjectRegistry.getObjectByXY(e)
2473
+ *
2474
+ * @param object e The event object
2475
+ */
2476
+ this.getObjectByXY = function (e)
2477
+ {
2478
+ var mouseXY = RG.getMouseXY(e);
2479
+
2480
+ if (
2481
+ mouseXY[0] > (this.gutterLeft - 3)
2482
+ && mouseXY[0] < (ca.width - this.gutterRight + 3)
2483
+ && mouseXY[1] > (this.gutterTop - 3)
2484
+ && mouseXY[1] < ((ca.height - this.gutterBottom) + 3)
2485
+ ) {
2486
+
2487
+ return this;
2488
+ }
2489
+ };
2490
+
2491
+
2492
+
2493
+
2494
+ /**
2495
+ * This function can be used when the canvas is clicked on (or similar - depending on the event)
2496
+ * to retrieve the relevant X coordinate for a particular value.
2497
+ *
2498
+ * @param int value The value to get the X coordinate for
2499
+ */
2500
+ this.getXCoord = function (value)
2501
+ {
2502
+ if (typeof value != 'number' && typeof value != 'string') {
2503
+ return null;
2504
+ }
2505
+
2506
+ // Allow for date strings to be passed
2507
+ if (typeof value === 'string') {
2508
+ value = RG.parseDate(value);
2509
+ }
2510
+
2511
+ var xmin = prop['chart.xmin'];
2512
+ var xmax = prop['chart.xmax'];
2513
+ var x;
2514
+
2515
+ if (value < xmin) return null;
2516
+ if (value > xmax) return null;
2517
+
2518
+ var gutterRight = this.gutterRight;
2519
+ var gutterLeft = this.gutterLeft;
2520
+
2521
+ if (prop['chart.yaxispos'] == 'right') {
2522
+ x = ((value - xmin) / (xmax - xmin)) * (ca.width - gutterLeft - gutterRight);
2523
+ x = (ca.width - gutterRight - x);
2524
+ } else {
2525
+ x = ((value - xmin) / (xmax - xmin)) * (ca.width - gutterLeft - gutterRight);
2526
+ x = x + gutterLeft;
2527
+ }
2528
+
2529
+ return x;
2530
+ };
2531
+
2532
+
2533
+
2534
+
2535
+ /**
2536
+ * This function positions a tooltip when it is displayed
2537
+ *
2538
+ * @param obj object The chart object
2539
+ * @param int x The X coordinate specified for the tooltip
2540
+ * @param int y The Y coordinate specified for the tooltip
2541
+ * @param objec tooltip The tooltips DIV element
2542
+ *
2543
+ this.positionTooltip = function (obj, x, y, tooltip, idx)
2544
+ {
2545
+ var shape = RG.Registry.Get('chart.tooltip.shape');
2546
+ var dataset = shape['dataset'];
2547
+ var index = shape['index'];
2548
+ var coordX = obj.coords[dataset][index][0]
2549
+ var coordY = obj.coords[dataset][index][1]
2550
+ var canvasXY = RG.getCanvasXY(obj.canvas);
2551
+ var mouseXY = RG.getMouseXY(window.event);
2552
+ var gutterLeft = obj.gutterLeft;
2553
+ var gutterTop = obj.gutterTop;
2554
+ var width = tooltip.offsetWidth;
2555
+ var height = tooltip.offsetHeight;
2556
+ tooltip.style.left = 0;
2557
+ tooltip.style.top = 0;
2558
+
2559
+ // Is the coord a boxplot
2560
+ //var isBoxplot = typeof(coordY) == 'object' ? true : false;
2561
+
2562
+ // Show any overflow (ie the arrow)
2563
+ tooltip.style.overflow = '';
2564
+
2565
+
2566
+ // Reposition the tooltip if at the edges:
2567
+
2568
+ // LEFT edge //////////////////////////////////////////////////////////////////
2569
+ if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
2570
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + 'px';
2571
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
2572
+
2573
+ // RIGHT edge //////////////////////////////////////////////////////////////////
2574
+ } else if ((canvasXY[0] + (coordX[0] || coordX) + (width / 2)) > doc.body.offsetWidth) {
2575
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + 'px';
2576
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
2577
+
2578
+ // Default positioning - CENTERED //////////////////////////////////////////////////////////////////
2579
+ } else {
2580
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + 'px';
2581
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
2582
+ }
2583
+ };*/
2584
+
2585
+
2586
+
2587
+
2588
+ /**
2589
+ * Returns the applicable Y COORDINATE when given a Y value
2590
+ *
2591
+ * @param int value The value to use
2592
+ * @return int The appropriate Y coordinate
2593
+ */
2594
+ this.getYCoord =
2595
+ this.getYCoordFromValue = function (value)
2596
+ {
2597
+ if (typeof(value) != 'number') {
2598
+ return null;
2599
+ }
2600
+
2601
+ var invert = prop['chart.ylabels.invert'];
2602
+ var xaxispos = prop['chart.xaxispos'];
2603
+ var graphHeight = ca.height - this.gutterTop - this.gutterBottom;
2604
+ var halfGraphHeight = graphHeight / 2;
2605
+ var ymax = this.max;
2606
+ var ymin = prop['chart.ymin'];
2607
+ var coord = 0;
2608
+
2609
+ if (value > ymax || (prop['chart.xaxispos'] == 'bottom' && value < ymin) || (prop['chart.xaxispos'] == 'center' && ((value > 0 && value < ymin) || (value < 0 && value > (-1 * ymin))))) {
2610
+ return null;
2611
+ }
2612
+
2613
+ /**
2614
+ * This calculates scale values if the X axis is in the center
2615
+ */
2616
+ if (xaxispos == 'center') {
2617
+
2618
+ coord = ((Math.abs(value) - ymin) / (ymax - ymin)) * halfGraphHeight;
2619
+
2620
+ if (invert) {
2621
+ coord = halfGraphHeight - coord;
2622
+ }
2623
+
2624
+ if (value < 0) {
2625
+ coord += this.gutterTop;
2626
+ coord += halfGraphHeight;
2627
+ } else {
2628
+ coord = halfGraphHeight - coord;
2629
+ coord += this.gutterTop;
2630
+ }
2631
+
2632
+ /**
2633
+ * And this calculates scale values when the X axis is at the bottom
2634
+ */
2635
+ } else {
2636
+
2637
+ coord = ((value - ymin) / (ymax - ymin)) * graphHeight;
2638
+
2639
+ if (invert) {
2640
+ coord = graphHeight - coord;
2641
+ }
2642
+
2643
+ // Invert the coordinate because the Y scale starts at the top
2644
+ coord = graphHeight - coord;
2645
+
2646
+ // And add on the top gutter
2647
+ coord = this.gutterTop + coord;
2648
+ }
2649
+
2650
+ return coord;
2651
+ };
2652
+
2653
+
2654
+
2655
+
2656
+
2657
+
2658
+ /**
2659
+ * A helper class that helps facilitatesbubble charts
2660
+ */
2661
+ RG.Scatter.Bubble = function (scatter, min, max, width, data)
2662
+ {
2663
+ this.scatter = scatter;
2664
+ this.min = min;
2665
+ this.max = max;
2666
+ this.width = width;
2667
+ this.data = data;
2668
+ this.coords = [];
2669
+ this.type = 'scatter.bubble'
2670
+
2671
+
2672
+
2673
+ /**
2674
+ * A setter for the Bubble chart class - it just acts as a "passthru" to the Scatter object
2675
+ */
2676
+ this.set =
2677
+ this.Set = function (name, value)
2678
+ {
2679
+ this.scatter.set(name, value);
2680
+
2681
+ return this;
2682
+ };
2683
+
2684
+
2685
+
2686
+ /**
2687
+ * A getter for the Bubble chart class - it just acts as a "passthru" to the Scatter object
2688
+ */
2689
+ this.get =
2690
+ this.Get = function (name)
2691
+ {
2692
+ this.scatter.get(name);
2693
+ };
2694
+
2695
+
2696
+
2697
+
2698
+ /**
2699
+ * Tha Bubble chart draw function
2700
+ */
2701
+ this.draw =
2702
+ this.Draw = function ()
2703
+ {
2704
+ var bubble_min = this.min,
2705
+ bubble_max = this.max,
2706
+ bubble_data = this.data,
2707
+ bubble_max_width = this.width;
2708
+
2709
+ // Keep a record of the bubble chart object
2710
+ var obj_bubble = this,
2711
+ obj_scatter = this.scatter;
2712
+
2713
+ // This custom ondraw event listener draws the bubbles
2714
+ this.scatter.ondraw = function (obj)
2715
+ {
2716
+ // Loop through all the points (first dataset)
2717
+ for (var i=0; i<obj.coords[0].length; ++i) {
2718
+
2719
+ bubble_data[i] = ma.max(bubble_data[i], bubble_min);
2720
+ bubble_data[i] = ma.min(bubble_data[i], bubble_max);
2721
+
2722
+ var r = ((bubble_data[i] - bubble_min) / (bubble_max - bubble_min) ) * bubble_max_width,
2723
+ color = obj_scatter.data[0][i][2] ? obj_scatter.data[0][i][2] : obj_scatter.properties['chart.defaultcolor'];
2724
+
2725
+ co.beginPath();
2726
+ co.fillStyle = RG.radialGradient(obj,
2727
+ obj_scatter.coords[0][i][0] + (r / 2.5),
2728
+ obj_scatter.coords[0][i][1] - (r / 2.5),
2729
+ 0,
2730
+ obj_scatter.coords[0][i][0] + (r / 2.5),
2731
+ obj_scatter.coords[0][i][1] - (r / 2.5),
2732
+ r,
2733
+ prop['chart.colors.bubble.graduated'] ? 'white' : color,
2734
+ color
2735
+ );
2736
+
2737
+ // Draw the bubble
2738
+ co.arc(
2739
+ obj_scatter.coords[0][i][0],
2740
+ obj_scatter.coords[0][i][1],
2741
+ r,
2742
+ 0,
2743
+ RG.TWOPI,
2744
+ false
2745
+ );
2746
+
2747
+ co.fill();
2748
+
2749
+ obj_bubble.coords[i] = [
2750
+ obj_scatter.coords[0][i][0],
2751
+ obj_scatter.coords[0][i][1],
2752
+ r,
2753
+ co.fillStyle
2754
+ ];
2755
+ }
2756
+ }
2757
+
2758
+ this.scatter.Draw();
2759
+
2760
+ return this;
2761
+ };
2762
+ };
2763
+
2764
+
2765
+
2766
+
2767
+
2768
+ /**
2769
+ * This allows for easy specification of gradients
2770
+ */
2771
+ this.parseColors = function ()
2772
+ {
2773
+ // Save the original colors so that they can be restored when the canvas is reset
2774
+ if (this.original_colors.length === 0) {
2775
+ this.original_colors['data'] = RG.array_clone(this.data);
2776
+ this.original_colors['chart.background.vbars'] = RG.array_clone(prop['chart.background.vbars']);
2777
+ this.original_colors['chart.background.hbars'] = RG.array_clone(prop['chart.background.hbars']);
2778
+ this.original_colors['chart.line.colors'] = RG.array_clone(prop['chart.line.colors']);
2779
+ this.original_colors['chart.defaultcolor'] = RG.array_clone(prop['chart.defaultcolor']);
2780
+ this.original_colors['chart.crosshairs.color'] = RG.array_clone(prop['chart.crosshairs.color']);
2781
+ this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
2782
+ this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
2783
+ this.original_colors['chart.background.barcolor1'] = RG.array_clone(prop['chart.background.barcolor1']);
2784
+ this.original_colors['chart.background.barcolor2'] = RG.array_clone(prop['chart.background.barcolor2']);
2785
+ this.original_colors['chart.background.grid.color'] = RG.array_clone(prop['chart.background.grid.color']);
2786
+ this.original_colors['chart.background.color'] = RG.array_clone(prop['chart.background.color']);
2787
+ this.original_colors['chart.axis.color'] = RG.array_clone(prop['chart.axis.color']);
2788
+ }
2789
+
2790
+
2791
+
2792
+
2793
+
2794
+ // Colors
2795
+ var data = this.data;
2796
+ if (data) {
2797
+ for (var dataset=0; dataset<data.length; ++dataset) {
2798
+ for (var i=0; i<this.data[dataset].length; ++i) {
2799
+
2800
+ // Boxplots
2801
+ if (this.data[dataset][i] && typeof(this.data[dataset][i][1]) == 'object' && this.data[dataset][i][1]) {
2802
+
2803
+ if (typeof(this.data[dataset][i][1][5]) == 'string') this.data[dataset][i][1][5] = this.parseSingleColorForGradient(this.data[dataset][i][1][5]);
2804
+ if (typeof(this.data[dataset][i][1][6]) == 'string') this.data[dataset][i][1][6] = this.parseSingleColorForGradient(this.data[dataset][i][1][6]);
2805
+ }
2806
+
2807
+ if (!RG.isNull(this.data[dataset][i])) {
2808
+ this.data[dataset][i][2] = this.parseSingleColorForGradient(this.data[dataset][i][2]);
2809
+ }
2810
+ }
2811
+ }
2812
+ }
2813
+
2814
+ // Parse HBars
2815
+ var hbars = prop['chart.background.hbars'];
2816
+ if (hbars) {
2817
+ for (i=0; i<hbars.length; ++i) {
2818
+ hbars[i][2] = this.parseSingleColorForGradient(hbars[i][2]);
2819
+ }
2820
+ }
2821
+
2822
+ // Parse HBars
2823
+ var vbars = prop['chart.background.vbars'];
2824
+ if (vbars) {
2825
+ for (i=0; i<vbars.length; ++i) {
2826
+ vbars[i][2] = this.parseSingleColorForGradient(vbars[i][2]);
2827
+ }
2828
+ }
2829
+
2830
+ // Parse line colors
2831
+ var colors = prop['chart.line.colors'];
2832
+ if (colors) {
2833
+ for (i=0; i<colors.length; ++i) {
2834
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
2835
+ }
2836
+ }
2837
+
2838
+ prop['chart.defaultcolor'] = this.parseSingleColorForGradient(prop['chart.defaultcolor']);
2839
+ prop['chart.crosshairs.color'] = this.parseSingleColorForGradient(prop['chart.crosshairs.color']);
2840
+ prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
2841
+ prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
2842
+ prop['chart.background.barcolor1'] = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
2843
+ prop['chart.background.barcolor2'] = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
2844
+ prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
2845
+ prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
2846
+ prop['chart.axis.color'] = this.parseSingleColorForGradient(prop['chart.axis.color']);
2847
+ };
2848
+
2849
+
2850
+
2851
+
2852
+ /**
2853
+ * Use this function to reset the object to the post-constructor state. Eg reset colors if
2854
+ * need be etc
2855
+ */
2856
+ this.reset = function ()
2857
+ {
2858
+ };
2859
+
2860
+
2861
+
2862
+
2863
+ /**
2864
+ * This parses a single color value for a gradient
2865
+ */
2866
+ this.parseSingleColorForGradient = function (color)
2867
+ {
2868
+ if (!color || typeof(color) != 'string') {
2869
+ return color;
2870
+ }
2871
+
2872
+ if (color.match(/^gradient\((.*)\)$/i)) {
2873
+
2874
+ var parts = RegExp.$1.split(':');
2875
+
2876
+ // Create the gradient
2877
+ var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'], 0, prop['chart.gutter.top']);
2878
+
2879
+ var diff = 1 / (parts.length - 1);
2880
+
2881
+ grad.addColorStop(0, RG.trim(parts[0]));
2882
+
2883
+ for (var j=1; j<parts.length; ++j) {
2884
+ grad.addColorStop(j * diff, RG.trim(parts[j]));
2885
+ }
2886
+ }
2887
+
2888
+ return grad ? grad : color;
2889
+ };
2890
+
2891
+
2892
+
2893
+
2894
+ /**
2895
+ * This function handles highlighting an entire data-series for the interactive
2896
+ * key
2897
+ *
2898
+ * @param int index The index of the data series to be highlighted
2899
+ */
2900
+ this.interactiveKeyHighlight = function (index)
2901
+ {
2902
+ if (this.coords && this.coords[index] && this.coords[index].length) {
2903
+ this.coords[index].forEach(function (value, idx, arr)
2904
+ {
2905
+ co.beginPath();
2906
+ co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
2907
+ co.arc(value[0], value[1], prop['chart.ticksize'] + 3, 0, RG.TWOPI, false);
2908
+ co.fill();
2909
+ });
2910
+ }
2911
+ };
2912
+
2913
+
2914
+
2915
+
2916
+ /**
2917
+ * Using a function to add events makes it easier to facilitate method chaining
2918
+ *
2919
+ * @param string type The type of even to add
2920
+ * @param function func
2921
+ */
2922
+ this.on = function (type, func)
2923
+ {
2924
+ if (type.substr(0,2) !== 'on') {
2925
+ type = 'on' + type;
2926
+ }
2927
+
2928
+ if (typeof this[type] !== 'function') {
2929
+ this[type] = func;
2930
+ } else {
2931
+ RG.addCustomEventListener(this, type, func);
2932
+ }
2933
+
2934
+ return this;
2935
+ };
2936
+
2937
+
2938
+
2939
+
2940
+ /**
2941
+ * This function runs once only
2942
+ * (put at the end of the file (before any effects))
2943
+ */
2944
+ this.firstDrawFunc = function ()
2945
+ {
2946
+ };
2947
+
2948
+
2949
+
2950
+
2951
+ /**
2952
+ * Trace2
2953
+ *
2954
+ * This is a new version of the Trace effect which no longer requires jQuery and is more compatible
2955
+ * with other effects (eg Expand). This new effect is considerably simpler and less code.
2956
+ *
2957
+ * @param object Options for the effect. Currently only "frames" is available.
2958
+ * @param int A function that is called when the ffect is complete
2959
+ */
2960
+ this.trace =
2961
+ this.trace2 = function ()
2962
+ {
2963
+ var obj = this,
2964
+ callback = arguments[2],
2965
+ opt = arguments[0] || {},
2966
+ frames = opt.frames || 30,
2967
+ frame = 0,
2968
+ callback = arguments[1] || function () {}
2969
+
2970
+ obj.Set('animationTrace', true);
2971
+ obj.Set('animationTraceClip', 0);
2972
+
2973
+ function iterator ()
2974
+ {
2975
+ RG.clear(obj.canvas);
2976
+
2977
+ RG.redrawCanvas(obj.canvas);
2978
+
2979
+ if (frame++ < frames) {
2980
+ obj.set('animationTraceClip', frame / frames);
2981
+ RG.Effects.updateCanvas(iterator);
2982
+ } else {
2983
+ callback(obj);
2984
+ }
2985
+ }
2986
+
2987
+ iterator();
2988
+
2989
+ return this;
2990
+ };
2991
+
2992
+
2993
+
2994
+
2995
+ /**
2996
+ * This helps the Gantt reset colors when the reset function is called.
2997
+ * It handles going through the data and resetting the colors.
2998
+ */
2999
+ this.resetColorsToOriginalValues = function ()
3000
+ {
3001
+ /**
3002
+ * Copy the original colors over for single-event-per-line data
3003
+ */
3004
+ for (var i=0,len=this.original_colors['data'].length; i<len; ++i) {
3005
+ for (var j=0,len2=this.original_colors['data'][i].length; j<len2;++j) {
3006
+
3007
+ // The color for the point
3008
+ this.data[i][j][2] = RG.array_clone(this.original_colors['data'][i][j][2]);
3009
+
3010
+ // Handle boxplots
3011
+ if (typeof this.data[i][j][1] === 'object') {
3012
+ this.data[i][j][1][5] = RG.array_clone(this.original_colors['data'][i][j][1][5]);
3013
+ this.data[i][j][1][6] = RG.array_clone(this.original_colors['data'][i][j][1][6]);
3014
+ }
3015
+ }
3016
+ }
3017
+ };
3018
+
3019
+
3020
+
3021
+
3022
+ /**
3023
+ * Register the object
3024
+ */
3025
+ RG.register(this);
3026
+
3027
+
3028
+
3029
+
3030
+ /**
3031
+ * This is the 'end' of the constructor so if the first argument
3032
+ * contains configuration data - handle that.
3033
+ */
3034
+ if (parseConfObjectForOptions) {
3035
+ RG.parseObjectStyleConfig(this, conf.options);
3036
+ }
3037
+ };