rgraph-rails 5.00 → 6.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish-geml.yaml +46 -0
  3. data/.gitignore +1 -0
  4. data/README.md +4 -5
  5. data/lib/rgraph-rails/version.rb +1 -1
  6. data/rgraph-rails.gemspec +4 -4
  7. data/vendor/assets/javascripts/RGraph.activity.js +1691 -0
  8. data/vendor/assets/javascripts/RGraph.bar.js +4253 -236
  9. data/vendor/assets/javascripts/RGraph.bipolar.js +3958 -162
  10. data/vendor/assets/javascripts/RGraph.common.annotate.js +414 -35
  11. data/vendor/assets/javascripts/RGraph.common.context.js +635 -30
  12. data/vendor/assets/javascripts/RGraph.common.core.js +10485 -419
  13. data/vendor/assets/javascripts/RGraph.common.csv.js +508 -27
  14. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1693 -90
  15. data/vendor/assets/javascripts/RGraph.common.effects.js +1629 -89
  16. data/vendor/assets/javascripts/RGraph.common.key.js +1003 -53
  17. data/vendor/assets/javascripts/RGraph.common.moment.js +5670 -0
  18. data/vendor/assets/javascripts/RGraph.common.sheets.js +541 -31
  19. data/vendor/assets/javascripts/RGraph.common.sheets.php +351 -0
  20. data/vendor/assets/javascripts/RGraph.common.starburst.js +382 -0
  21. data/vendor/assets/javascripts/RGraph.common.table.js +386 -0
  22. data/vendor/assets/javascripts/RGraph.common.tooltips.js +1433 -32
  23. data/vendor/assets/javascripts/RGraph.drawing.background.js +660 -35
  24. data/vendor/assets/javascripts/RGraph.drawing.circle.js +618 -34
  25. data/vendor/assets/javascripts/RGraph.drawing.image.js +857 -52
  26. data/vendor/assets/javascripts/RGraph.drawing.line.js +712 -0
  27. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +760 -38
  28. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +740 -37
  29. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +573 -36
  30. data/vendor/assets/javascripts/RGraph.drawing.poly.js +667 -36
  31. data/vendor/assets/javascripts/RGraph.drawing.rect.js +638 -34
  32. data/vendor/assets/javascripts/RGraph.drawing.text.js +672 -37
  33. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +653 -52
  34. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +714 -51
  35. data/vendor/assets/javascripts/RGraph.fuel.js +1149 -59
  36. data/vendor/assets/javascripts/RGraph.funnel.js +1277 -56
  37. data/vendor/assets/javascripts/RGraph.gantt.js +1646 -82
  38. data/vendor/assets/javascripts/RGraph.gauge.js +1773 -89
  39. data/vendor/assets/javascripts/RGraph.hbar.js +3869 -159
  40. data/vendor/assets/javascripts/RGraph.horseshoe.js +970 -0
  41. data/vendor/assets/javascripts/RGraph.hprogress.js +1829 -81
  42. data/vendor/assets/javascripts/RGraph.line.js +5293 -244
  43. data/vendor/assets/javascripts/RGraph.meter.js +1570 -77
  44. data/vendor/assets/javascripts/RGraph.modaldialog.js +300 -19
  45. data/vendor/assets/javascripts/RGraph.odo.js +1553 -68
  46. data/vendor/assets/javascripts/RGraph.pie.js +3273 -129
  47. data/vendor/assets/javascripts/RGraph.radar.js +2333 -108
  48. data/vendor/assets/javascripts/RGraph.rose.js +2685 -114
  49. data/vendor/assets/javascripts/RGraph.rscatter.js +1920 -80
  50. data/vendor/assets/javascripts/RGraph.scatter.js +4215 -171
  51. data/vendor/assets/javascripts/RGraph.segmented.js +1006 -0
  52. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1980 -59
  53. data/vendor/assets/javascripts/RGraph.svg.activity.js +1696 -0
  54. data/vendor/assets/javascripts/RGraph.svg.bar.js +2575 -77
  55. data/vendor/assets/javascripts/RGraph.svg.bipolar.js +3533 -106
  56. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +240 -21
  57. data/vendor/assets/javascripts/RGraph.svg.common.core.js +7105 -299
  58. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +408 -28
  59. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1291 -68
  60. data/vendor/assets/javascripts/RGraph.svg.common.key.js +451 -20
  61. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +543 -31
  62. data/vendor/assets/javascripts/RGraph.svg.common.table.js +391 -0
  63. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +1072 -23
  64. data/vendor/assets/javascripts/RGraph.svg.funnel.js +1151 -32
  65. data/vendor/assets/javascripts/RGraph.svg.gauge.js +1429 -34
  66. data/vendor/assets/javascripts/RGraph.svg.hbar.js +2692 -65
  67. data/vendor/assets/javascripts/RGraph.svg.horseshoe.js +969 -0
  68. data/vendor/assets/javascripts/RGraph.svg.line.js +2855 -86
  69. data/vendor/assets/javascripts/RGraph.svg.pie.js +1630 -58
  70. data/vendor/assets/javascripts/RGraph.svg.radar.js +1772 -58
  71. data/vendor/assets/javascripts/RGraph.svg.rose.js +2419 -83
  72. data/vendor/assets/javascripts/RGraph.svg.scatter.js +2280 -65
  73. data/vendor/assets/javascripts/RGraph.svg.segmented.js +930 -0
  74. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +1612 -29
  75. data/vendor/assets/javascripts/RGraph.svg.waterfall.js +1525 -50
  76. data/vendor/assets/javascripts/RGraph.thermometer.js +1411 -64
  77. data/vendor/assets/javascripts/RGraph.vprogress.js +1915 -81
  78. data/vendor/assets/javascripts/RGraph.waterfall.js +1896 -89
  79. data/vendor/assets/javascripts/financial-data.js +1067 -0
  80. metadata +37 -16
  81. data/.travis.yml +0 -11
  82. data/vendor/assets/javascripts/RGraph.common.deprecated.js +0 -35
  83. data/vendor/assets/javascripts/RGraph.common.resizing.js +0 -38
  84. data/vendor/assets/javascripts/RGraph.common.zoom.js +0 -15
  85. data/vendor/assets/javascripts/RGraph.cornergauge.js +0 -71
@@ -1,160 +1,3870 @@
1
+ 'version:2023-09-16 (6.14)';
2
+ //
3
+ // o--------------------------------------------------------------------------------o
4
+ // o--------------------------------------------------------------------------------o
5
+ // | This file is part of the RGraph package - you can learn more at: |
6
+ // | |
7
+ // | https://www.rgraph.net |
8
+ // | |
9
+ // | RGraph is licensed under the Open Source MIT license. That means that it's |
10
+ // | totally free to use and there are no restrictions on what you can do with it! |
11
+ // o--------------------------------------------------------------------------------o
1
12
 
2
- RGraph=window.RGraph||{isRGraph:true};RGraph.HBar=function(conf)
3
- {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var id=conf.id
4
- var canvas=document.getElementById(id);var data=conf.data;var parseConfObjectForOptions=true;}else{var id=conf;var canvas=document.getElementById(id);var data=arguments[1];}
5
- this.id=id;this.canvas=canvas;this.context=this.canvas.getContext?this.canvas.getContext("2d",{alpha:(typeof id==='object'&&id.alpha===false)?false:true}):null;this.canvas.__object__=this;this.data=data;this.type='hbar';this.isRGraph=true;this.uid=RGraph.CreateUID();this.canvas.uid=this.canvas.uid?this.canvas.uid:RGraph.CreateUID();this.colorsParsed=false;this.coords=[];this.coords2=[];this.coordsText=[];this.original_colors=[];this.firstDraw=true;this.yaxisLabelsSize=0;this.yaxisTitleSize=0;this.propertyNameAliases={};this.max=0;this.stackedOrGrouped=false;this.properties={'chart.margin.left':25,'chart.margin.left.auto':true,'chart.margin.right':25,'chart.margin.right.auto':false,'chart.margin.top':25,'chart.margin.bottom':25,'chart.background.bars.count':null,'chart.background.bars.color1':'rgba(0,0,0,0)','chart.background.bars.color2':'rgba(0,0,0,0)','chart.background.grid':true,'chart.background.grid.color':'#ddd','chart.background.grid.linewidth':1,'chart.background.grid.hsize':25,'chart.background.grid.vsize':25,'chart.background.grid.hlines':true,'chart.background.grid.vlines':true,'chart.background.grid.border':true,'chart.background.grid.autofit':true,'chart.background.grid.autofit.align':true,'chart.background.grid.hlines.count':null,'chart.background.grid.vlines.count':5,'chart.background.grid.dashed':false,'chart.background.grid.dotted':false,'chart.background.color':null,'chart.linewidth':1,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.bold':null,'chart.title.italic':null,'chart.title.font':null,'chart.title.size':null,'chart.title.color':null,'chart.title.x':null,'chart.title.y':null,'chart.title.halign':null,'chart.title.valign':null,'chart.text.size':12,'chart.text.color':'black','chart.text.font':'Arial, Verdana, sans-serif','chart.text.bold':false,'chart.text.italic':false,'chart.text.angle':0,'chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':false,'chart.colors':['red','blue','green','pink','yellow','cyan','navy','gray','black'],'chart.colors.sequential':false,'chart.colors.stroke':'rgba(0,0,0,0)','chart.axes':true,'chart.axes.color':'black','chart.axes.linewidth':1,'chart.xaxis':true,'chart.xaxis.labels':true,'chart.xaxis.labels.count':5,'chart.xaxis.labels.bold':null,'chart.xaxis.labels.italic':null,'chart.xaxis.labels.font':null,'chart.xaxis.labels.size':null,'chart.xaxis.labels.color':null,'chart.xaxis.labels.specific':null,'chart.xaxis.labels.offsetx':0,'chart.xaxis.labels.offsety':0,'chart.xaxis.scale.units.pre':'','chart.xaxis.scale.units.post':'','chart.xaxis.scale.min':0,'chart.xaxis.scale.max':0,'chart.xaxis.scale.point':'.','chart.xaxis.scale.thousand':',','chart.xaxis.scale.decimals':null,'chart.xaxis.scale.zerostart':true,'chart.xaxis.title':'','chart.xaxis.title.bold':null,'chart.xaxis.title.italic':null,'chart.xaxis.title.size':null,'chart.xaxis.title.font':null,'chart.xaxis.title.color':null,'chart.xaxis.title.x':null,'chart.xaxis.title.y':null,'chart.xaxis.title.pos':null,'chart.xaxis.tickmarks':true,'chart.xaxis.tickmarks.count':10,'chart.yaxis':true,'chart.yaxis.labels.font':null,'chart.yaxis.labels.size':null,'chart.yaxis.labels.color':null,'chart.yaxis.labels.bold':null,'chart.yaxis.labels.italic':null,'chart.yaxis.labels.offsetx':0,'chart.yaxis.labels.offsety':0,'chart.yaxis.position':'left','chart.yaxis.title':'','chart.yaxis.title.bold':null,'chart.yaxis.title.italic':null,'chart.yaxis.title.size':null,'chart.yaxis.title.font':null,'chart.yaxis.title.color':null,'chart.yaxis.title.pos':null,'chart.yaxis.title.x':null,'chart.yaxis.title.y':null,'chart.yaxis.tickmarks.count':data.length,'chart.yaxis.tickmarks':true,'chart.labels.above':false,'chart.labels.above.decimals':0,'chart.labels.above.specific':null,'chart.labels.above.units.pre':'','chart.labels.above.units.post':'','chart.labels.above.color':null,'chart.labels.above.font':null,'chart.labels.above.size':null,'chart.labels.above.bold':null,'chart.labels.above.italic':null,'chart.contextmenu':null,'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.margin.boxed':false,'chart.key.position.x':null,'chart.key.position.y':null,'chart.key.color.shape':'square','chart.key.rounded':true,'chart.key.linewidth':1,'chart.key.colors':null,'chart.key.interactive':false,'chart.key.interactive.highlight.chart.stroke':'black','chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)','chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)','chart.key.labels.color':null,'chart.key.labels.font':null,'chart.key.labels.size':null,'chart.key.labels.bold':null,'chart.key.labels.italic':null,'chart.key.labels.offsetx':0,'chart.key.labels.offsety':0,'chart.units.ingraph':false,'chart.shadow':false,'chart.shadow.color':'#666','chart.shadow.blur':3,'chart.shadow.offsetx':3,'chart.shadow.offsety':3,'chart.vmargin':2,'chart.vmargin.grouped':2,'chart.grouping':'grouped','chart.tooltips':null,'chart.tooltips.event':'onclick','chart.tooltips.effect':'fade','chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.highlight':true,'chart.highlight.fill':'rgba(255,255,255,0.7)','chart.highlight.stroke':'rgba(0,0,0,0)','chart.highlight.style':null,'chart.annotatable':false,'chart.annotatable.color':'black','chart.resizable':false,'chart.resizable.handle.adjust':[0,0],'chart.resizable.handle.background':null,'chart.redraw':true,'chart.events.click':null,'chart.events.mousemove':null,'chart.variant':'hbar','chart.variant.threed.angle':0.1,'chart.variant.threed.offsetx':10,'chart.variant.threed.offsety':5,'chart.variant.threed.xaxis':true,'chart.variant.threed.yaxis':true,'chart.adjustable':false,'chart.adjustable.only':null,'chart.clearto':'rgba(0,0,0,0)'}
6
- if(!this.canvas){alert('[HBAR] No canvas support');return;}
7
- for(i=0,len=this.data.length;i<len;++i){if(typeof this.data[i]=='object'&&!RGraph.isNull(this.data[i])){this.stackedOrGrouped=true;for(var j=0,len2=this.data[i].length;j<len2;++j){if(typeof this.data[i][j]==='string'){this.data[i][j]=parseFloat(this.data[i][j]);}}}else if(typeof this.data[i]=='string'){this.data[i]=parseFloat(this.data[i])||0;}else if(typeof this.data[i]==='undefined'){this.data[i]=null;}}
8
- var linear_data=RGraph.arrayLinearize(data);for(var i=0,len=linear_data.length;i<len;++i){this['$'+i]={};}
9
- this.data_arr=RGraph.arrayLinearize(this.data);if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
10
- var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
11
- if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
12
- this.set=this.Set=function(name)
13
- {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
14
- if(name.substr(0,6)!='chart.'){name='chart.'+name;}
15
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
16
- prop[name]=value;return this;};this.get=this.Get=function(name)
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
- return prop[name];};this.draw=this.Draw=function()
20
- {RG.fireCustomEvent(this,'onbeforedraw');if(prop['chart.adjustable']&&prop['chart.grouping']==='stacked'){alert('[RGRAPH] The HBar does not support stacked charts with adjusting');}
21
- if(RG.isNull(prop['chart.background.grid.hlines.count'])){this.set('chart.background.grid.hlines.count',this.data.length);}
22
- if(prop['chart.variant']==='3d'){if(prop['chart.text.accessible']){}else{co.setTransform(1,prop['chart.variant.threed.angle'],0,1,0.5,0.5);}
23
- if(prop['chart.margin.bottom']===25){this.set('chart.margin.bottom',80);}}
24
- if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
25
- if(prop['chart.yaxis.labels']){var labels=prop['chart.yaxis.labels'],marginName=prop['chart.yaxis.position']==='right'?'chart.margin.right':'chart.margin.left';var textConf=RG.getTextConf({object:this,prefix:'chart.yaxis.labels'});for(var i=0,len=0;i<labels.length;i+=1){var length=RG.measureText(labels[i],textConf.bold,textConf.font,textConf.size)[0]||0;this.yaxisLabelsSize=ma.max(len,length);len=this.yaxisLabelsSize;}
26
- if(prop['chart.yaxis.title']){var textConf=RG.getTextConf({object:this,prefix:'chart.yaxis.title'});var titleSize=RG.measureText(prop['chart.yaxis.title'],textConf.bold,textConf.font,textConf.size)||[];this.yaxisTitleSize+=titleSize[1];prop[marginName]+=this.yaxisTitleSize;}}
27
- if(prop['chart.margin.left.auto']){var name=prop['chart.yaxis.position']==='right'?'chart.margin.right':'chart.margin.left';prop[name]=this.yaxisLabelsSize+this.yaxisTitleSize+10;}
28
- this.marginLeft=prop['chart.margin.left'];this.marginRight=prop['chart.margin.right'];this.marginTop=prop['chart.margin.top'];this.marginBottom=prop['chart.margin.bottom'];this.coords=[];this.coords2=[];this.coordsText=[];this.max=0;if(prop['chart.xaxis.scale.min']>0&&prop['chart.grouping']==='stacked'){alert('[HBAR] Using chart.xaxis.scale.min is not supported with stacked charts, resetting chart.xaxis.scale.min to zero');this.set('chart.xaxis.scale.min',0);}
29
- this.graphwidth=ca.width-this.marginLeft-this.marginRight;this.graphheight=ca.height-this.marginTop-this.marginBottom;this.halfgrapharea=this.grapharea/2;this.halfTextHeight=prop['chart.text.size']/2;this.halfway=ma.round((this.graphwidth/2)+this.marginLeft);RG.Background.draw(this);this.drawbars();this.drawAxes();this.drawLabels();if(prop['chart.key']&&prop['chart.key'].length){RG.drawKey(this,prop['chart.key'],prop['chart.colors']);}
30
- if(prop['chart.contextmenu']){RG.showContext(this);}
31
- RG.drawInGraphLabels(this);if(prop['chart.resizable']){RG.allowResizing(this);}
32
- RG.installEventListeners(this);if(this.firstDraw){this.firstDraw=false;RG.fireCustomEvent(this,'onfirstdraw');this.firstDrawFunc();}
33
- RG.fireCustomEvent(this,'ondraw');return this;};this.exec=function(func)
34
- {func(this);return this;};this.drawAxes=this.DrawAxes=function()
35
- {var halfway=this.halfway;co.beginPath();co.lineWidth=prop['chart.axes.linewidth']?prop['chart.axes.linewidth']+0.001:1.001;co.strokeStyle=prop['chart.axes.color'];if(prop['chart.yaxis']&&prop['chart.axes']){if(prop['chart.yaxis.position']=='center'){co.moveTo(halfway,this.marginTop);co.lineTo(halfway,ca.height-this.marginBottom);}else if(prop['chart.yaxis.position']=='right'){co.moveTo(ca.width-this.marginRight,this.marginTop);co.lineTo(ca.width-this.marginRight,ca.height-this.marginBottom);}else{co.moveTo(this.marginLeft,this.marginTop);co.lineTo(this.marginLeft,ca.height-this.marginBottom);}}
36
- if(prop['chart.xaxis']&&prop['chart.axes']){co.moveTo(this.marginLeft+0.001,ca.height-this.marginBottom+0.001);co.lineTo(ca.width-this.marginRight+0.001,ca.height-this.marginBottom+0.001);}
37
- if(prop['chart.yaxis.tickmarks']&&prop['chart.yaxis']&&prop['chart.yaxis.tickmarks.count']>0&&prop['chart.axes']){var yTickGap=(ca.height-this.marginTop-this.marginBottom)/(prop['chart.yaxis.tickmarks.count']>0?prop['chart.yaxis.tickmarks.count']:this.data.length);for(y=this.marginTop;y<(ca.height-this.marginBottom-1);y+=yTickGap){if(prop['chart.yaxis.position']=='center'){co.moveTo(halfway+3,ma.round(y));co.lineTo(halfway-3,ma.round(y));}else if(prop['chart.yaxis.position']=='right'){co.moveTo(ca.width-this.marginRight,ma.round(y));co.lineTo(ca.width-this.marginRight+3,ma.round(y));}else{co.moveTo(this.marginLeft,ma.round(y));co.lineTo(this.marginLeft-3,ma.round(y));}}
38
- if(!prop['chart.xaxis']){if(prop['chart.yaxis.position']==='center'){co.moveTo(halfway+3,ma.round(y));co.lineTo(halfway-3,ma.round(y));}else if(prop['chart.yaxis.position']==='right'){co.moveTo(ca.width-this.marginRight,ma.round(y));co.lineTo(ca.width-this.marginRight+3,ma.round(y));}else{co.moveTo(this.marginLeft,ma.round(y));co.lineTo(this.marginLeft-3,ma.round(y));}}}
39
- if(prop['chart.xaxis.tickmarks']&&prop['chart.xaxis']&&prop['chart.xaxis.tickmarks.count']>0&&prop['chart.axes']){xTickGap=(ca.width-this.marginLeft-this.marginRight)/prop['chart.xaxis.tickmarks.count'];yStart=ca.height-this.marginBottom;yEnd=(ca.height-this.marginBottom)+3;var cnt=prop['chart.xaxis.tickmarks.count'],interval=(this.canvas.width-prop['chart.margin.left']-prop['chart.margin.right'])/cnt,x=prop['chart.margin.left'];if(prop['chart.yaxis.position']==='center'){interval/=2;for(var i=0;i<cnt;++i,x+=interval){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}
40
- x=(this.graphwidth/2)+this.marginLeft+interval;for(var i=0;i<cnt;++i,x+=interval){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}}else if(prop['chart.yaxis.position']==='right'){for(var i=0;i<cnt;++i,x+=interval){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}}else{x+=interval;for(var i=0;i<cnt;++i,x+=interval){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}}
41
- if(!prop['chart.yaxis']){x=this.marginLeft;if(prop['chart.yaxis.position']==='right'){x=ca.width-this.marginRight;}
42
- co.moveTo(x,ma.round(yStart));co.lineTo(x,ma.round(yEnd));}}
43
- co.stroke();co.lineWidth=1;};this.drawLabels=this.DrawLabels=function()
44
- {var units_pre=prop['chart.xaxis.scale.units.pre'],units_post=prop['chart.xaxis.scale.units.post'],text_size=prop['chart.text.size'],font=prop['chart.text.font'],offsetx=prop['chart.xaxis.labels.offsetx'],offsety=prop['chart.xaxis.labels.offsety']
45
- if(prop['chart.units.ingraph']){units_pre='';units_post='';}
46
- if(prop['chart.xaxis.labels']){if(RG.isArray(prop['chart.xaxis.labels.specific'])){if(prop['chart.yaxis.position']=='center'){var halfGraphWidth=this.graphwidth/2;var labels=prop['chart.xaxis.labels.specific'];var interval=(this.graphwidth/2)/(labels.length-1);co.fillStyle=prop['chart.xaxis.labels'];var textConf=RG.getTextConf({object:this,prefix:'chart.xaxis.labels'});for(var i=0;i<labels.length;i+=1){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+halfGraphWidth+(interval*i)+offsetx,y:ca.height-this.marginBottom+offsetx,text:labels[i],valign:'top',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}
47
- for(var i=(labels.length-1);i>0;i-=1){RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+(interval*(labels.length-i-1))+offsetx,y:ca.height-this.marginBottom+offsety,text:labels[i],valign:'top',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}}else if(prop['chart.yaxis.position']=='right'){var labels=prop['chart.xaxis.labels.specific'];var interval=this.graphwidth/(labels.length-1);co.fillStyle=prop['chart.text.color'];var textConf=RG.getTextConf({object:this,prefix:'chart.xaxis.labels'});for(var i=0;i<labels.length;i+=1){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+(interval*i)+offsetx,y:ca.height-this.marginBottom+offsety,text:labels[labels.length-i-1],valign:'top',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}}else{var labels=prop['chart.xaxis.labels.specific'];var interval=this.graphwidth/(labels.length-1);co.fillStyle=prop['chart.text.color'];var textConf=RG.getTextConf({object:this,prefix:'chart.xaxis.labels'});for(var i=0;i<labels.length;i+=1){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+(interval*i)+offsetx,y:ca.height-this.marginBottom+offsety,text:labels[i],valign:'top',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}}}else{var gap=7;co.beginPath();co.fillStyle=prop['chart.text.color'];var textConf=RG.getTextConf({object:this,prefix:'chart.xaxis.labels'});if(prop['chart.yaxis.position']=='center'){for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+(this.graphwidth/2)-((this.graphwidth/2)*((i+1)/this.scale2.labels.length))+offsetx,y:this.marginTop+this.halfTextHeight+this.graphheight+gap+offsety,text:'-'+this.scale2.labels[i],valign:'center',halign:(typeof prop['chart.text.angle']==='number'&&prop['chart.text.angle']!==0)?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}
48
- for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+((this.graphwidth/2)*((i+1)/this.scale2.labels.length))+(this.graphwidth/2)+offsetx,y:this.marginTop+this.halfTextHeight+this.graphheight+gap+offsety,text:this.scale2.labels[i],valign:'center',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}}else if(prop['chart.yaxis.position']=='right'){for(var i=0,len=this.scale2.labels.length;i<len;++i){RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+(i*(this.graphwidth/len))+offsetx,y:this.marginTop+this.halfTextHeight+this.graphheight+gap+offsety,text:'-'+this.scale2.labels[len-1-i],valign:'center',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}}else{for(var i=0,len=this.scale2.labels.length;i<len;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:this.marginLeft+(this.graphwidth*((i+1)/len))+offsetx,y:this.marginTop+this.halfTextHeight+this.graphheight+gap+offsety,text:this.scale2.labels[i],valign:'center',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}}
49
- if(prop['chart.xaxis.scale.min']>0||!prop['chart.yaxis']||prop['chart.xaxis.scale.zerostart']||!prop['chart.axes']){var x=prop['chart.yaxis.position']==='center'?this.marginLeft+(this.graphwidth/2):this.marginLeft;if(prop['chart.yaxis.position']==='right'){var x=ca.width-this.marginRight;}
50
- RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x+offsetx,y:this.marginTop+this.halfTextHeight+this.graphheight+gap+offsety,text:RG.numberFormat({object:this,number:prop['chart.xaxis.scale.min'].toFixed(prop['chart.xaxis.scale.min']===0?0:prop['chart.xaxis.scale.decimals']),unitspre:units_pre,unitspost:units_post,point:prop['chart.xaxis.scale.point'],thousand:prop['chart.xaxis.scale.thousand']}),valign:'center',halign:prop['chart.text.angle']!==0?'right':'center',tag:'scale',angle:-1*prop['chart.text.angle']});}
51
- co.fill();co.stroke();}}
52
- if(typeof prop['chart.yaxis.labels']==='object'){var xOffset=prop['chart.variant']==='3d'&&prop['chart.yaxis.position']==='right'?15:5,offsetx=prop['chart.yaxis.labels.offsetx'],offsety=prop['chart.yaxis.labels.offsety']
53
- var barHeight=(ca.height-this.marginTop-this.marginBottom)/prop['chart.yaxis.labels'].length;yTickGap=(ca.height-this.marginTop-this.marginBottom)/prop['chart.yaxis.labels'].length
54
- if(prop['chart.yaxis.position']==='right'){var x=ca.width-this.marginRight+xOffset;var halign='left'}else{var x=this.marginLeft-xOffset;var halign='right'}
55
- var textConf=RG.getTextConf({object:this,prefix:'chart.yaxis.labels'});var i=0;for(y=this.marginTop+(yTickGap/2);y<=ca.height-this.marginBottom;y+=yTickGap){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x+offsetx,y:y+offsety,text:String(prop['chart.yaxis.labels'][i++]),halign:halign,valign:'center',tag:'labels'});}}};this.drawbars=this.Drawbars=function()
56
- {co.lineWidth=prop['chart.linewidth'];co.strokeStyle=prop['chart.colors.stroke'];co.fillStyle=prop['chart.colors'][0];var prevX=0,prevY=0;if(prop['chart.xaxis.scale.max']){this.scale2=RG.getScale2(this,{'scale.max':prop['chart.xaxis.scale.max'],'scale.min':prop['chart.xaxis.scale.min'],'scale.decimals':Number(prop['chart.xaxis.scale.decimals']),'scale.point':prop['chart.xaxis.scale.point'],'scale.thousand':prop['chart.xaxis.scale.thousand'],'scale.round':prop['chart.xaxis.scale.round'],'scale.units.pre':prop['chart.xaxis.scale.units.pre'],'scale.units.post':prop['chart.xaxis.scale.units.post'],'scale.labels.count':prop['chart.xaxis.labels.count'],'scale.strict':true});this.max=this.scale2.max;}else{var grouping=prop['chart.grouping'];for(i=0;i<this.data.length;++i){if(typeof(this.data[i])=='object'){var value=grouping=='grouped'?Number(RG.arrayMax(this.data[i],true)):Number(RG.arraySum(this.data[i]));}else{var value=Number(ma.abs(this.data[i]));}
57
- this.max=ma.max(Math.abs(this.max),Math.abs(value));}
58
- this.scale2=RG.getScale2(this,{'scale.max':this.max,'scale.min':prop['chart.xaxis.scale.min'],'scale.decimals':Number(prop['chart.xaxis.scale.decimals']),'scale.point':prop['chart.xaxis.scale.point'],'scale.thousand':prop['chart.xaxis.scale.thousand'],'scale.round':prop['chart.xaxis.scale.round'],'scale.units.pre':prop['chart.xaxis.scale.units.pre'],'scale.units.post':prop['chart.xaxis.scale.units.post'],'scale.labels.count':prop['chart.xaxis.labels.count']});this.max=this.scale2.max;this.min=this.scale2.min;}
59
- if(prop['chart.xaxis.scale.decimals']==null&&Number(this.max)==1){this.Set('chart.xaxis.scale.decimals',1);}
60
- var colorIdx=0;this.numbars=RG.arrayLinearize(this.data).length;if(prop['chart.adjustable']&&!prop['chart.xaxis.scale.max']){this.set('chart.xaxis.scale.max',this.scale2.max);}
61
- if(prop['chart.variant']==='3d'){RG.draw3DAxes(this);}
62
- var graphwidth=(ca.width-this.marginLeft-this.marginRight);var halfwidth=graphwidth/2;for(i=(len=this.data.length-1);i>=0;--i){var width=ma.abs((this.data[i]/this.max)*graphwidth);var height=this.graphheight/this.data.length;var orig_height=height;var x=this.marginLeft;var y=this.marginTop+(i*height);var vmargin=prop['chart.vmargin'];if(prop['chart.yaxis.position']==='right'){x=ca.width-this.marginRight-ma.abs(width);}
63
- if(width<0){x-=width;width=ma.abs(width);}
64
- if(prop['chart.shadow']){co.shadowColor=prop['chart.shadow.color'];co.shadowBlur=prop['chart.shadow.blur'];co.shadowOffsetX=prop['chart.shadow.offsetx'];co.shadowOffsetY=prop['chart.shadow.offsety'];}
65
- co.beginPath();if(typeof this.data[i]=='number'||RG.isNull(this.data[i])){var barHeight=height-(2*vmargin),barWidth=((this.data[i]-prop['chart.xaxis.scale.min'])/(this.max-prop['chart.xaxis.scale.min']))*this.graphwidth,barX=this.marginLeft;if(prop['chart.yaxis.position']=='center'){barWidth/=2;barX+=halfwidth;if(this.data[i]<0){barWidth=(ma.abs(this.data[i])-prop['chart.xaxis.scale.min'])/(this.max-prop['chart.xaxis.scale.min']);barWidth=barWidth*(this.graphwidth/2);barX=((this.graphwidth/2)+this.marginLeft)-barWidth;}}else if(prop['chart.yaxis.position']=='right'){barWidth=ma.abs(barWidth);barX=ca.width-this.marginRight-barWidth;}
66
- co.strokeStyle=prop['chart.colors.stroke'];co.fillStyle=prop['chart.colors'][0];++colorIdx;if(prop['chart.colors.sequential']&&typeof colorIdx==='number'){if(prop['chart.colors'][this.numbars-colorIdx]){co.fillStyle=prop['chart.colors'][this.numbars-colorIdx];}else{co.fillStyle=prop['chart.colors'][prop['chart.colors'].length-1];}}
67
- co.strokeRect(barX,this.marginTop+(i*height)+prop['chart.vmargin'],barWidth,barHeight);co.fillRect(barX,this.marginTop+(i*height)+prop['chart.vmargin'],barWidth,barHeight);this.coords.push([barX,y+vmargin,barWidth,height-(2*vmargin),co.fillStyle,this.data[i],true]);if(prop['chart.variant']==='3d'&&typeof this.data[i]=='number'){var prevStrokeStyle=co.strokeStyle,prevFillStyle=co.fillStyle;RG.noShadow(this);var barX=barX,barY=y+vmargin,barW=barWidth,barH=height-(2*vmargin),offsetX=prop['chart.variant.threed.offsetx'],offsetY=prop['chart.variant.threed.offsety'],value=this.data[i];pa2(co,['b','m',barX,barY,'l',barX+offsetX-(prop['chart.yaxis.position']=='left'&&value<0?offsetX:0),barY-offsetY,'l',barX+barW+offsetX-(prop['chart.yaxis.position']=='center'&&value<0?offsetX:0),barY-offsetY,'l',barX+barW,barY,'c','s',co.strokeStyle,'f',co.fillStyle,'f','rgba(255,255,255,0.6)']);if(prop['chart.yaxis.position']!=='right'&&!(prop['chart.yaxis.position']==='center'&&value<0)&&value>=0&&!RG.isNull(value)){pa2(co,['b','fs',prevFillStyle,'m',barX+barW,barY,'l',barX+barW+offsetX,barY-offsetY,'l',barX+barW+offsetX,barY-offsetY+barH,'l',barX+barW,barY+barH,'c','s',co.strokeStyle,'f',prevFillStyle,'f','rgba(0,0,0,0.25)']);}}}else if(typeof(this.data[i])=='object'&&prop['chart.grouping']=='stacked'){if(prop['chart.yaxis.position']=='center'){alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped');}else if(prop['chart.yaxis.position']=='right'){var x=ca.width-this.marginRight}
68
- var barHeight=height-(2*vmargin);if(typeof this.coords2[i]=='undefined'){this.coords2[i]=[];}
69
- for(j=0;j<this.data[i].length;++j){if(prop['chart.shadow']&&prop['chart.variant']==='3d'){co.shadowColor=prop['chart.shadow.color'];co.shadowBlur=prop['chart.shadow.blur'];co.shadowOffsetX=prop['chart.shadow.offsetx'];co.shadowOffsetY=prop['chart.shadow.offsety'];}
70
- if(!RG.isNull(this.data[i][j]))this.data[i][j]=ma.abs(this.data[i][j]);var last=(j===(this.data[i].length-1));co.strokeStyle=prop['chart.colors.stroke'];++colorIdx;if(prop['chart.colors.sequential']&&typeof colorIdx==='number'){if(prop['chart.colors'][this.numbars-colorIdx]){co.fillStyle=prop['chart.colors'][this.numbars-colorIdx];}else{co.fillStyle=prop['chart.colors'][prop['chart.colors'].length-1];}}else if(prop['chart.colors'][j]){co.fillStyle=prop['chart.colors'][j];}
71
- var width=(((this.data[i][j])/(this.max)))*this.graphwidth;var totalWidth=(RG.arraySum(this.data[i])/this.max)*this.graphwidth;if(prop['chart.yaxis.position']==='right'){x-=width;}
72
- co.strokeRect(x,this.marginTop+prop['chart.vmargin']+(this.graphheight/this.data.length)*i,width,height-(2*vmargin));co.fillRect(x,this.marginTop+prop['chart.vmargin']+(this.graphheight/this.data.length)*i,width,height-(2*vmargin));this.coords.push([x,y+vmargin,width,height-(2*vmargin),co.fillStyle,RG.array_sum(this.data[i]),j==(this.data[i].length-1)]);this.coords2[i].push([x,y+vmargin,width,height-(2*vmargin),co.fillStyle,RG.array_sum(this.data[i]),j==(this.data[i].length-1)]);if(prop['chart.variant']==='3d'){RG.noShadow(this);var prevStrokeStyle=co.strokeStyle,prevFillStyle=co.fillStyle;var barX=x,barY=y+vmargin,barW=width,barH=height-(2*vmargin),offsetX=prop['chart.variant.threed.offsetx'],offsetY=prop['chart.variant.threed.offsety'],value=this.data[i][j];if(!RG.isNull(value)){pa2(co,['b','m',barX,barY,'l',barX+offsetX,barY-offsetY,'l',barX+barW+offsetX,barY-offsetY,'l',barX+barW,barY,'c','s',co.strokeStyle,'f',co.fillStyle,'f','rgba(255,255,255,0.6)']);}
73
- if(prop['chart.yaxis.position']!=='right'&&!(prop['chart.yaxis.position']==='center'&&value<0)&&!RG.isNull(value)){pa2(co,['fs',prevFillStyle,'b','m',barX+barW,barY,'l',barX+barW+offsetX,barY-offsetY,'l',barX+barW+offsetX,barY-offsetY+barH,'l',barX+barW,barY+barH,'c','s',co.strokeStyle,'f',prevFillStyle,'f','rgba(0,0,0,0.25)']);}
74
- co.beginPath();co.strokeStyle=prevStrokeStyle;co.fillStyle=prevFillStyle;}
75
- if(prop['chart.yaxis.position']!=='right'){x+=width;}}}else if(typeof(this.data[i])=='object'&&prop['chart.grouping']=='grouped'){var vmarginGrouped=prop['chart.vmargin.grouped'];var individualBarHeight=((height-(2*vmargin)-((this.data[i].length-1)*vmarginGrouped))/this.data[i].length)
76
- if(typeof this.coords2[i]=='undefined'){this.coords2[i]=[];}
77
- for(j=(this.data[i].length-1);j>=0;--j){if(prop['chart.shadow']){RG.setShadow(this,prop['chart.shadow.color'],prop['chart.shadow.offsetx'],prop['chart.shadow.offsety'],prop['chart.shadow.blur']);}
78
- co.strokeStyle=prop['chart.colors.stroke'];++colorIdx;if(prop['chart.colors.sequential']&&typeof colorIdx==='number'){if(prop['chart.colors'][this.numbars-colorIdx]){co.fillStyle=prop['chart.colors'][this.numbars-colorIdx];}else{co.fillStyle=prop['chart.colors'][prop['chart.colors'].length-1];}}else if(prop['chart.colors'][j]){co.fillStyle=prop['chart.colors'][j];}
79
- var startY=this.marginTop+(height*i)+(individualBarHeight*j)+vmargin+(vmarginGrouped*j);var width=((this.data[i][j]-prop['chart.xaxis.scale.min'])/(this.max-prop['chart.xaxis.scale.min']))*(ca.width-this.marginLeft-this.marginRight);var startX=this.marginLeft;if(prop['chart.yaxis.position']=='center'){width/=2;startX+=halfwidth;}else if(prop['chart.yaxis.position']=='right'){width=ma.abs(width);startX=ca.width-this.marginRight-ma.abs(width);}
80
- if(width<0){startX+=width;width*=-1;}
81
- co.strokeRect(startX,startY,width,individualBarHeight);co.fillRect(startX,startY,width,individualBarHeight);this.coords.push([startX,startY,width,individualBarHeight,co.fillStyle,this.data[i][j],true]);this.coords2[i].push([startX,startY,width,individualBarHeight,co.fillStyle,this.data[i][j],true]);if(prop['chart.variant']==='3d'){RG.noShadow(this);var prevStrokeStyle=co.strokeStyle,prevFillStyle=co.fillStyle;var barX=startX,barY=startY,barW=width,barH=individualBarHeight,offsetX=prop['chart.variant.threed.offsetx'],offsetY=prop['chart.variant.threed.offsety'],value=this.data[i][j];pa2(co,['b','m',barX,barY,'l',barX+offsetX,barY-offsetY,'l',barX+barW+offsetX-(value<0?offsetX:0),barY-offsetY,'l',barX+barW,barY,'c','s',co.strokeStyle,'f',co.fillStyle,'f','rgba(255,255,255,0.6)']);if(prop['chart.yaxis.position']!=='right'&&!(prop['chart.yaxis.position']==='center'&&value<0)&&value>=0&&!RG.isNull(value)){pa2(co,['fs',prevFillStyle,'b','m',barX+barW,barY,'l',barX+barW+offsetX,barY-offsetY,'l',barX+barW+offsetX,barY-offsetY+barH,'l',barX+barW,barY+barH,'c','s',co.strokeStyle,'f',prevFillStyle,'f','rgba(0,0,0,0.25)']);}
82
- co.beginPath();co.strokeStyle=prevStrokeStyle;co.fillStyle=prevFillStyle;}}
83
- startY+=vmargin;}
84
- co.closePath();}
85
- co.stroke();co.fill();if(prop['chart,yaxis.position']==='right'){pa2(co,'cr % % % %',ca.width-this.marginRight+prop['chart.variant.threed.offsetx'],'0',this.marginRight,ca.height);}
86
- if(prop['chart.yaxis.position']==='right'&&prop['chart.variant']==='3d'){RG.draw3DYAxis(this);}
87
- RG.noShadow(this);this.coords=RG.arrayReverse(this.coords);if(prop['chart.grouping']==='grouped'){for(var i=0;i<this.coords2.length;++i){this.coords2[i]=RG.arrayReverse(this.coords2[i]);}}
88
- this.redrawBars();};this.redrawBars=this.RedrawBars=function()
89
- {if(!prop['chart.redraw']){return;}
90
- var coords=this.coords;var font=prop['chart.text.font'],size=prop['chart.text.size'],color=prop['chart.text.color'];RG.noShadow(this);co.strokeStyle=prop['chart.colors.stroke'];for(var i=0;i<coords.length;++i){if(prop['chart.shadow']){pa2(co,'b lw % r % % % % s % f %',prop['chart.linewidth'],coords[i][0],coords[i][1],coords[i][2],coords[i][3],prop['chart.colors.stroke'],coords[i][4]);}
91
- var halign='left';if(prop['chart.labels.above']&&coords[i][6]){var border=(coords[i][0]+coords[i][2]+7+co.measureText(prop['chart.labels.above.units.pre']+this.coords[i][5]+prop['chart.labels.above.units.post']).width)>ca.width?true:false,text=RG.numberFormat({object:this,number:(this.coords[i][5]).toFixed(prop['chart.labels.above.decimals']),unitspre:prop['chart.labels.above.units.pre'],unitspost:prop['chart.labels.above.units.post'],point:prop['chart.labels.above.point'],thousand:prop['chart.labels.above.thousand']});RG.noShadow(this);if(typeof prop['chart.labels.above.specific']==='object'&&prop['chart.labels.above.specific']&&prop['chart.labels.above.specific'][i]){text=prop['chart.labels.above.specific'][i];}
92
- var x=coords[i][0]+coords[i][2]+5;var y=coords[i][1]+(coords[i][3]/2);if(prop['chart.yaxis.position']==='right'){x=coords[i][0]-5;halign='right';}else if(prop['chart.yaxis.position']==='center'&&this.data_arr[i]<0){x=coords[i][0]-5;halign='right';}
93
- var textConf=RG.getTextConf({object:this,prefix:'chart.labels.above'});RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x,y:y,text:text,valign:'center',halign:halign,tag:'labels.above'});}}};this.getShape=this.getBar=function(e)
94
- {var mouseXY=RG.getMouseXY(e);for(var i=0,len=this.coords.length;i<len;i++){var mouseX=mouseXY[0],mouseY=mouseXY[1],left=this.coords[i][0],top=this.coords[i][1],width=this.coords[i][2],height=this.coords[i][3],idx=i;pa2(co,['b','r',left,top,width,height]);if(co.isPointInPath(mouseX,mouseY)){var tooltip=RG.parseTooltipText(prop['chart.tooltips'],i);return{0:this,'object':this,1:left,'x':left,2:top,'y':top,3:width,'width':width,4:height,'height':height,5:idx,'index':idx,'tooltip':tooltip};}}};this.getValue=function(arg)
95
- {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];}
96
- if(mouseY<this.marginTop||mouseY>(ca.height-this.marginBottom)||mouseX<this.marginLeft||mouseX>(ca.width-this.marginRight)){return null;}
97
- if(prop['chart.yaxis.position']=='center'){var value=((mouseX-this.marginLeft)/(this.graphwidth/2))*(this.max-prop['chart.xaxis.scale.min']);value=value-this.max
98
- if(prop['chart.xaxis.scale.min']>0){value=((mouseX-this.marginLeft-(this.graphwidth/2))/(this.graphwidth/2))*(this.max-prop['chart.xaxis.scale.min']);value+=prop['chart.xaxis.scale.min'];if(mouseX<(this.marginLeft+(this.graphwidth/2))){value-=(2*prop['chart.xaxis.scale.min']);}}}else if(prop['chart.yaxis.position']=='right'){var value=((mouseX-this.marginLeft)/this.graphwidth)*(this.scale2.max-prop['chart.xaxis.scale.min']);value=this.scale2.max-value;}else{var value=((mouseX-this.marginLeft)/this.graphwidth)*(this.scale2.max-prop['chart.xaxis.scale.min']);value+=prop['chart.xaxis.scale.min'];}
99
- return value;};this.highlight=this.Highlight=function(shape)
100
- {if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else{RG.Highlight.Rect(this,shape);}};this.getObjectByXY=function(e)
101
- {var mouseXY=RG.getMouseXY(e);if(prop['chart.variant']==='3d'){var adjustment=prop['chart.variant.threed.angle']*mouseXY[0];mouseXY[1]-=adjustment;}
102
- if(mouseXY[0]>=this.marginLeft&&mouseXY[0]<=(ca.width-this.marginRight)&&mouseXY[1]>=this.marginTop&&mouseXY[1]<=(ca.height-this.marginBottom)){return this;}};this.getXCoord=function(value)
103
- {if(prop['chart.yaxis.position']=='center'){if(value>this.max||value<(-1*this.max)){return null;}
104
- var width=(ca.width-prop['chart.margin.left']-prop['chart.margin.right'])/2;var coord=(((value-prop['chart.xaxis.scale.min'])/(this.max-prop['chart.xaxis.scale.min']))*width)+width;coord=prop['chart.margin.left']+coord;}else{if(value>this.max||value<0){return null;}
105
- var width=ca.width-prop['chart.margin.left']-prop['chart.margin.right'];var coord=((value-prop['chart.xaxis.scale.min'])/(this.max-prop['chart.xaxis.scale.min']))*width;coord=prop['chart.margin.left']+coord;}
106
- return coord;};this.parseColors=function()
107
- {if(this.original_colors.length===0){this.original_colors['chart.colors']=RG.arrayClone(prop['chart.colors']);this.original_colors['chart.background.grid.color']=RG.arrayClone(prop['chart.background.grid.color']);this.original_colors['chart.background.color']=RG.arrayClone(prop['chart.background.color']);this.original_colors['chart.background.bars.color1']=RG.arrayClone(prop['chart.background.bars.color1']);this.original_colors['chart.background.bars.color2']=RG.arrayClone(prop['chart.background.bars.color2']);this.original_colors['chart.text.color']=RG.arrayClone(prop['chart.text.color']);this.original_colors['chart.yaxis.labels.color']=RG.arrayClone(prop['chart.yaxis.labels.color']);this.original_colors['chart.colors.stroke']=RG.arrayClone(prop['chart.colors.stroke']);this.original_colors['chart.axes.color']=RG.arrayClone(prop['chart.axes.color']);this.original_colors['chart.highlight.fill']=RG.arrayClone(prop['chart.highlight.fill']);this.original_colors['chart.highlight.stroke']=RG.arrayClone(prop['chart.highlight.stroke']);}
108
- var colors=prop['chart.colors'];for(var i=0;i<colors.length;++i){colors[i]=this.parseSingleColorForGradient(colors[i]);}
109
- prop['chart.background.grid.color']=this.parseSingleColorForGradient(prop['chart.background.grid.color']);prop['chart.background.color']=this.parseSingleColorForGradient(prop['chart.background.color']);prop['chart.background.bars.color1']=this.parseSingleColorForGradient(prop['chart.background.bars.color1']);prop['chart.background.bars.color2']=this.parseSingleColorForGradient(prop['chart.background.bars.color2']);prop['chart.text.color']=this.parseSingleColorForGradient(prop['chart.text.color']);prop['chart.yaxis.labels.color']=this.parseSingleColorForGradient(prop['chart.yaxis.labels.color']);prop['chart.colors.stroke']=this.parseSingleColorForGradient(prop['chart.colors.stroke']);prop['chart.axes.color']=this.parseSingleColorForGradient(prop['chart.axes.color']);prop['chart.highlight.fill']=this.parseSingleColorForGradient(prop['chart.highlight.fill']);prop['chart.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.highlight.stroke']);};this.reset=function()
110
- {};this.parseSingleColorForGradient=function(color)
111
- {if(!color||typeof(color)!='string'){return color;}
112
- if(color.match(/^gradient\((.*)\)$/i)){if(color.match(/^gradient\(({.*})\)$/i)){return RGraph.parseJSONGradient({object:this,def:RegExp.$1});}
113
- var parts=RegExp.$1.split(':');if(prop['chart.yaxis.position']==='right'){parts=RG.arrayReverse(parts);}
114
- var grad=co.createLinearGradient(prop['chart.margin.left'],0,ca.width-prop['chart.margin.right'],0);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]));}}
115
- return grad?grad:color;};this.interactiveKeyHighlight=function(index)
116
- {var obj=this;this.coords2.forEach(function(value,idx,arr)
117
- {var shape=obj.coords2[idx][index]
118
- var pre_linewidth=co.lineWidth;co.lineWidth=2;co.fillStyle=prop['chart.key.interactive.highlight.chart.fill'];co.strokeStyle=prop['chart.key.interactive.highlight.chart.stroke'];co.fillRect(shape[0],shape[1],shape[2],shape[3]);co.strokeRect(shape[0],shape[1],shape[2],shape[3]);co.lineWidth=pre_linewidth;});};this.on=function(type,func)
119
- {if(type.substr(0,2)!=='on'){type='on'+type;}
120
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
121
- return this;};this.firstDrawFunc=function()
122
- {};this.getShapeByY=function(e)
123
- {var mouseXY=RG.getMouseXY(e);var obj=arguments[1]?arguments[1]:this;for(var i=0,len=obj.coords.length;i<len;i++){if(obj.coords[i].length==0){continue;}
124
- var mouseX=mouseXY[0],mouseY=mouseXY[1],left=obj.coords[i][0],top=obj.coords[i][1],width=obj.coords[i][2],height=obj.coords[i][3];if(mouseY>=top&&mouseY<=(top+height)){if(prop['chart.tooltips']){var tooltip=RG.parseTooltipText?RG.parseTooltipText(prop['chart.tooltips'],i):prop['chart.tooltips'][i];}
125
- return{0:obj,object:obj,1:left,x:left,2:top,y:top,3:width,width:width,4:height,height:height,5:i,index:i,tooltip:tooltip};}}
126
- return null;};this.adjusting_mousemove=this.Adjusting_mousemove=function(e)
127
- {if(prop['chart.adjustable']&&RG.Registry.get('chart.adjusting')&&RG.Registry.get('chart.adjusting').uid==this.uid){var value=Number(this.getValue(e)),shape=RG.Registry.get('chart.adjusting.shape');if(shape){RG.Registry.Set('chart.adjusting.shape',shape);if(this.stackedOrGrouped&&prop['chart.grouping']=='grouped'){var indexes=RG.sequentialIndexToGrouped(shape['index'],this.data);if(typeof this.data[indexes[0]]=='number'){this.data[indexes[0]]=Number(value);}else if(!RG.is_null(this.data[indexes[0]])){this.data[indexes[0]][indexes[1]]=Number(value);}}else if(typeof this.data[shape['index']]=='number'){this.data[shape['index']]=Number(value);}
128
- RG.redrawCanvas(e.target);RG.fireCustomEvent(this,'onadjust');}}};this.grow=function()
129
- {var obj=this,opt=arguments[0]||{},frames=opt.frames||30,frame=0,callback=arguments[1]||function(){},labelsAbove=prop['chart.labels.above'];this.set('chart.labels.above',false);obj.original_data=RG.arrayClone(obj.data);if(prop['chart.xaxis.scale.max']==0){var xmax=0;for(var i=0;i<obj.data.length;++i){if(RG.isArray(obj.data[i])&&prop['chart.grouping']=='stacked'){xmax=ma.max(xmax,RG.arraySum(obj.data[i]));}else if(RG.isArray(obj.data[i])&&prop['chart.grouping']=='grouped'){xmax=ma.max(xmax,RG.arrayMax(obj.data[i]));}else{xmax=ma.max(xmax,ma.abs(RG.arrayMax(obj.data[i])));}}
130
- var scale2=RG.getScale2(obj,{'scale.max':xmax});obj.set('chart.xaxis.scale.max',scale2.max);}
131
- function iterator()
132
- {for(var j=0,len=obj.original_data.length;j<len;++j){var easingFactor=RG.Effects.getEasingMultiplier(frames,frame);if(typeof obj.data[j]==='object'&&obj.data[j]){for(var k=0,len2=obj.data[j].length;k<len2;++k){obj.data[j][k]=RG.isNull(obj.data[j][k])?null:obj.original_data[j][k]*easingFactor;}}else{obj.data[j]=RG.isNull(obj.data[j])?null:obj.original_data[j]*easingFactor;}}
133
- RG.redrawCanvas(obj.canvas);if(frame<frames){frame+=1;RG.Effects.updateCanvas(iterator);}else{if(labelsAbove){obj.set('chart.labels.above',true);RG.redraw();}
134
- callback(obj);}}
135
- iterator();return this;};this.grow=function()
136
- {var opt=arguments[0]||{},frames=opt.frames||30,frame=0,callback=arguments[1]||function(){},obj=this,labelsAbove=this.get('chart.labels.above')
137
- this.original_data=RG.arrayClone(this.data);if(prop['chart.xaxis.scale.max']==0){var xmax=0;for(var i=0;i<obj.data.length;++i){if(RG.isArray(obj.data[i])&&prop['chart.grouping']=='stacked'){xmax=ma.max(xmax,RG.arraySum(obj.data[i]));}else if(RG.isArray(obj.data[i])&&prop['chart.grouping']=='grouped'){xmax=ma.max(xmax,RG.arrayMax(obj.data[i]));}else{xmax=ma.max(xmax,ma.abs(RG.arrayMax(obj.data[i])));}}
138
- var scale2=RG.getScale2(obj,{'scale.max':xmax});obj.set('chart.xaxis.scale.max',scale2.max);}
139
- if(RG.isArray(opt.data)){var xmax=0;for(var i=0;i<opt.data.length;++i){if(typeof opt.data[i]==='object'){for(var j=0;j<opt.data[i].length;++j){if(typeof opt.data[i][j]==='string'&&opt.data[i][j].match(/(\+|\-)([0-9]+)/)){if(RegExp.$1==='+'){opt.data[i][j]=this.original_data[i][j]+parseInt(RegExp.$2);}else{opt.data[i][j]=this.original_data[i][j]-parseInt(RegExp.$2);}}
140
- xmax=ma.max(xmax,opt.data[i][j]);}}else if(typeof opt.data[i]==='string'&&opt.data[i].match(/(\+|\-)([0-9]+)/)){if(RegExp.$1==='+'){opt.data[i]=this.original_data[i]+parseFloat(RegExp.$2);}else{opt.data[i]=this.original_data[i]-parseFloat(RegExp.$2);}
141
- xmax=ma.max(xmax,opt.data[i]);}else{xmax=ma.max(xmax,opt.data[i]);}}
142
- var scale=RG.getScale2(this,{'scale.max':xmax});if(RG.isNull(this.get('chart.xaxis.scale.max'))){this.set('chart.xaxis.scale.max',scale.max);}}
143
- this.set('chart.labels.above',false);if(RG.isNull(prop['chart.xaxis.scale.max'])){var xmax=0;for(var i=0;i<obj.data.length;++i){if(RG.isArray(this.data[i])&&prop['chart.grouping']==='stacked'){xmax=ma.max(xmax,ma.abs(RG.arraySum(this.data[i])));}else if(RG.isArray(this.data[i])&&prop['chart.grouping']==='grouped'){for(var j=0,group=[];j<this.data[i].length;j++){group.push(ma.abs(this.data[i][j]));}
144
- xmax=ma.max(xmax,ma.abs(RG.arrayMax(group)));}else{xmax=ma.max(xmax,ma.abs(this.data[i]));}}
145
- var scale=RG.getScale2(this,{'scale.max':xmax});this.set('chart.xaxis.scale.max',scale.max);}
146
- if(typeof opt.xmax==='number'){obj.set('chart.xaxis.scale.max',opt.xmax);}
147
- var iterator=function()
148
- {var easingMultiplier=RG.Effects.getEasingMultiplier(frames,frame);for(var j=0,len=obj.original_data.length;j<len;++j){if(typeof obj.data[j]==='object'&&!RG.isNull(obj.data[j])){for(var k=0,len2=obj.data[j].length;k<len2;++k){if(obj.firstDraw||!opt.data){obj.data[j][k]=easingMultiplier*obj.original_data[j][k];}else if(opt.data&&opt.data.length===obj.original_data.length){var diff=opt.data[j][k]-obj.original_data[j][k];obj.data[j][k]=(easingMultiplier*diff)+obj.original_data[j][k];}}}else{if(obj.firstDraw||!opt.data){obj.data[j]=easingMultiplier*obj.original_data[j];}else if(opt.data&&opt.data.length===obj.original_data.length){var diff=opt.data[j]-obj.original_data[j];obj.data[j]=(easingMultiplier*diff)+obj.original_data[j];}}}
149
- RG.redrawCanvas(obj.canvas);if(frame<frames){frame+=1;RG.Effects.updateCanvas(iterator);}else{if(RG.isArray(opt.data)){var linear_data=RG.arrayLinearize(data);for(var i=0;i<linear_data.length;++i){if(!obj['$'+i]){obj['$'+i]={};}}}
150
- obj.data=data;obj.original_data=RG.arrayClone(data);if(labelsAbove){obj.set('chart.labels.above',true);RG.redraw();}
151
- callback(obj);}};iterator();return this;};this.wave=function()
152
- {var obj=this,opt=arguments[0]||{};opt.frames=opt.frames||60;opt.startFrames=[];opt.counters=[];var framesperbar=opt.frames/3,frame=-1,callback=arguments[1]||function(){},original=RG.arrayClone(obj.data),labelsAbove=prop['chart.labels.above'];this.set('chart.labels.above',false);for(var i=0,len=obj.data.length;i<len;i+=1){opt.startFrames[i]=((opt.frames/2)/(obj.data.length-1))*i;if(typeof obj.data[i]==='object'&&obj.data[i]){opt.counters[i]=[];for(var j=0;j<obj.data[i].length;j++){opt.counters[i][j]=0;}}else{opt.counters[i]=0;}}
153
- obj.draw();obj.Set('chart.xaxis.scale.max',obj.scale2.max);RG.clear(obj.canvas);function iterator()
154
- {++frame;for(var i=0,len=obj.data.length;i<len;i+=1){if(frame>opt.startFrames[i]){if(typeof obj.data[i]==='number'){obj.data[i]=ma.min(ma.abs(original[i]),ma.abs(original[i]*((opt.counters[i]++)/framesperbar)));if(original[i]<0){obj.data[i]*=-1;}}else if(!RG.isNull(obj.data[i])){for(var j=0,len2=obj.data[i].length;j<len2;j+=1){obj.data[i][j]=ma.min(ma.abs(original[i][j]),ma.abs(original[i][j]*((opt.counters[i][j]++)/framesperbar)));if(original[i][j]<0){obj.data[i][j]*=-1;}}}}else{obj.data[i]=typeof obj.data[i]==='object'&&obj.data[i]?RG.arrayPad([],obj.data[i].length,0):(RG.isNull(obj.data[i])?null:0);}}
155
- if(frame>=opt.frames){if(labelsAbove){obj.set('chart.labels.above',true);RG.redrawCanvas(obj.canvas);}
156
- callback(obj);}else{RG.redrawCanvas(obj.canvas);RG.Effects.updateCanvas(iterator);}}
157
- iterator();return this;};this.isAdjustable=function(shape)
158
- {if(RG.isNull(prop['chart.adjustable.only'])){return true;}
159
- if(RG.isArray(prop['chart.adjustable.only'])&&prop['chart.adjustable.only'][shape.index]){return true;}
160
- return false;};RG.Register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};
13
+ RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
14
+
15
+ //
16
+ // The horizontal bar chart constructor. The horizontal bar is a minor variant
17
+ // on the bar chart. If you have big labels, this may be useful as there is usually
18
+ // more space available for them.
19
+ //
20
+ RGraph.HBar = function (conf)
21
+ {
22
+ //
23
+ // Allow for object config style
24
+ //
25
+ var id = conf.id,
26
+ canvas = document.getElementById(id),
27
+ data = conf.data;
28
+
29
+
30
+ this.id = id;
31
+ this.canvas = canvas;
32
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null;
33
+ this.canvas.__object__ = this;
34
+ this.data = data;
35
+ this.type = 'hbar';
36
+ this.isRGraph = true;
37
+ this.isrgraph = true;
38
+ this.rgraph = true;
39
+ this.uid = RGraph.createUID();
40
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID();
41
+ this.colorsParsed = false;
42
+ this.coords = [];
43
+ this.coords2 = [];
44
+ this.coordsText = [];
45
+ this.coordsSpline = [];
46
+ this.original_colors = [];
47
+ this.firstDraw = true; // After the first draw this will be false
48
+ this.stopAnimationRequested = false;// Used to control the animations
49
+ this.yaxisLabelsSize = 0; // Used later when the margin is auto calculated
50
+ this.yaxisTitleSize = 0; // Used later when the margin is auto calculated
51
+
52
+
53
+
54
+ this.max = 0;
55
+ this.stackedOrGrouped = false;
56
+
57
+ // Default properties
58
+ this.properties =
59
+ {
60
+ marginLeft: 75,
61
+ marginLeftAuto: true,
62
+ marginRight: 35,
63
+ marginTop: 35,
64
+ marginBottom: 35,
65
+ marginInner: 2,
66
+ marginInnerGrouped: 2,
67
+
68
+ backgroundBarsCount: null,
69
+ backgroundBarsColor1: 'rgba(0,0,0,0)',
70
+ backgroundBarsColor2: 'rgba(0,0,0,0)',
71
+ backgroundGrid: true,
72
+ backgroundGridColor: '#ddd',
73
+ backgroundGridLinewidth: 1,
74
+ backgroundGridHsize: 25,
75
+ backgroundGridVsize: 25,
76
+ backgroundGridHlines: true,
77
+ backgroundGridVlines: true,
78
+ backgroundGridBorder: true,
79
+ backgroundGridAutofit: true,
80
+ backgroundGridAutofitAlign:true,
81
+ backgroundGridHlinesCount: null,
82
+ backgroundGridVlinesCount: 5,
83
+ backgroundGridDashed: false,
84
+ backgroundGridDotted: false,
85
+ backgroundColor: null,
86
+
87
+ linewidth: 1,
88
+
89
+ title: '',
90
+ titleBold: null,
91
+ titleItalic: null,
92
+ titleFont: null,
93
+ titleSize: null,
94
+ titleColor: null,
95
+ titleX: null,
96
+ titleY: null,
97
+ titleHalign: null,
98
+ titleValign: null,
99
+ titleOffsetx: 0,
100
+ titleOffsety: 0,
101
+ titleSubtitle: '',
102
+ titleSubtitleSize: null,
103
+ titleSubtitleColor: '#aaa',
104
+ titleSubtitleFont: null,
105
+ titleSubtitleBold: null,
106
+ titleSubtitleItalic: null,
107
+ titleSubtitleOffsetx: 0,
108
+ titleSubtitleOffsety: 0,
109
+
110
+ textSize: 12,
111
+ textColor: 'black',
112
+ textFont: 'Arial, Verdana, sans-serif',
113
+ textBold: false,
114
+ textItalic: false,
115
+ textAngle: 0,
116
+ textAccessible: false,
117
+ textAccessibleOverflow: 'visible',
118
+ textAccessiblePointerevents: false,
119
+ text: null,
120
+
121
+ colors: ['red', 'blue', 'green', 'pink', 'yellow', 'cyan', 'navy', 'gray', 'black'],
122
+ colorsSequential: false,
123
+ colorsStroke: 'rgba(0,0,0,0)',
124
+
125
+ xaxis: true,
126
+ xaxisLinewidth: 1,
127
+ xaxisColor: 'black',
128
+ xaxisPosition: 'bottom',
129
+ xaxisTickmarks: true,
130
+ xaxisTickmarksLength: 3,
131
+ xaxisTickmarksLastLeft: null,
132
+ xaxisTickmarksLastRight: null,
133
+ xaxisTickmarksCount: null,
134
+ xaxisLabels: true,
135
+ xaxisLabelsCount: 5,
136
+ xaxisLabelsBold: null,
137
+ xaxisLabelsItalic: null,
138
+ xaxisLabelsFont: null,
139
+ xaxisLabelsSize: null,
140
+ xaxisLabelsColor: null,
141
+ xaxisLabelsSpecific: null,
142
+ xaxisLabelsAngle: 0,
143
+ xaxisLabelsOffsetx: 0,
144
+ xaxisLabelsOffsety: 0,
145
+ xaxisLabelsHalign: null,
146
+ xaxisLabelsValign: null,
147
+ xaxisLabelsPosition: 'edge',
148
+ xaxisLabelsSpecificAlign:'left',
149
+ xaxisScale: true,
150
+ xaxisScaleUnitsPre: '',
151
+ xaxisScaleUnitsPost: '',
152
+ xaxisScaleMin: 0,
153
+ xaxisScaleMax: 0,
154
+ xaxisScalePoint: '.',
155
+ xaxisScaleThousand: ',',
156
+ xaxisScaleDecimals: null,
157
+ xaxisScaleZerostart: true,
158
+ xaxisTitle: '',
159
+ xaxisTitleBold: null,
160
+ xaxisTitleItalic: null,
161
+ xaxisTitleSize: null,
162
+ xaxisTitleFont: null,
163
+ xaxisTitleColor: null,
164
+ xaxisTitleX: null,
165
+ xaxisTitleY: null,
166
+ xaxisTitleOffsetx: null,
167
+ xaxisTitleOffsety: null,
168
+ xaxisTitlePos: null,
169
+ xaxisTitleHalign: null,
170
+ xaxisTitleValign: null,
171
+
172
+ yaxis: true,
173
+ yaxisLinewidth: 1,
174
+ yaxisColor: 'black',
175
+ yaxisTickmarks: true,
176
+ yaxisTickmarksCount: null,
177
+ yaxisTickmarksLastTop: null,
178
+ yaxisTickmarksLastBottom: null,
179
+ yaxisTickmarksLength: 3,
180
+ yaxisScale: false,
181
+ yaxisLabels: null,
182
+ yaxisLabelsCount: null, // Not used by the HBar
183
+ yaxisLabelsOffsetx: 0,
184
+ yaxisLabelsOffsety: 0,
185
+ yaxisLabelsHalign: null,
186
+ yaxisLabelsValign: null,
187
+ yaxisLabelsFont: null,
188
+ yaxisLabelsSize: null,
189
+ yaxisLabelsColor: null,
190
+ yaxisLabelsBold: null,
191
+ yaxisLabelsItalic: null,
192
+ yaxisLabelsPosition: 'section',
193
+ yaxisLabelsFormattedDecimals: 0,
194
+ yaxisLabelsFormattedPoint: '.',
195
+ yaxisLabelsFormattedThousand: ',',
196
+ yaxisLabelsFormattedUnitsPre: '',
197
+ yaxisLabelsFormattedUnitsPost: '',
198
+ yaxisPosition: 'left',
199
+ yaxisTitle: null,
200
+ yaxisTitleBold: null,
201
+ yaxisTitleSize: null,
202
+ yaxisTitleFont: null,
203
+ yaxisTitleColor: null,
204
+ yaxisTitleItalic: null,
205
+ yaxisTitlePos: null,
206
+ yaxisTitleX: null,
207
+ yaxisTitleY: null,
208
+ yaxisTitleOffsetx: 0,
209
+ yaxisTitleOffsety: 0,
210
+ yaxisTitleHalign: null,
211
+ yaxisTitleValign: null,
212
+ yaxisTitleAccessible: null,
213
+
214
+ labelsAbove: false,
215
+ labelsAboveDecimals: 0,
216
+ labelsAboveSpecific: null,
217
+ labelsAboveUnitsPre: '',
218
+ labelsAboveUnitsPost: '',
219
+ labelsAboveColor: null,
220
+ labelsAboveFont: null,
221
+ labelsAboveSize: null,
222
+ labelsAboveBold: null,
223
+ labelsAboveItalic: null,
224
+ labelsAboveOffsetx: 0,
225
+ labelsAboveOffsety: 0,
226
+
227
+ labelsInbar: false,
228
+ labelsInbarHalign: 'center',
229
+ labelsInbarValign: 'center',
230
+ labelsInbarFont: null,
231
+ labelsInbarSize: null,
232
+ labelsInbarBold: null,
233
+ labelsInbarItalic: null,
234
+ labelsInbarColor: null,
235
+ labelsInbarBackground: null,
236
+ labelsInbarBackgroundPadding: 0,
237
+ labelsInbarUnitsPre: null,
238
+ labelsInbarUnitsPost: null,
239
+ labelsInbarPoint: null,
240
+ labelsInbarThousand: null,
241
+ labelsInbarFormatter: null,
242
+ labelsInbarDecimals: null,
243
+ labelsInbarOffsetx: 0,
244
+ labelsInbarOffsety: 0,
245
+ labelsInbarSpecific: null,
246
+ labelsInbarFormatter: null,
247
+
248
+ contextmenu: null,
249
+
250
+ key: null,
251
+ keyBackground: 'white',
252
+ keyPosition: 'graph',
253
+ keyHalign: 'right',
254
+ keyShadow: false,
255
+ keyShadowColor: '#666',
256
+ keyShadowBlur: 3,
257
+ keyShadowOffsetx: 2,
258
+ keyShadowOffsety: 2,
259
+ keyPositionMarginBoxed: false,
260
+ keyPositionMarginHSpace: 0,
261
+ keyPositionX: null,
262
+ keyPositionY: null,
263
+ keyColorShape: 'square',
264
+ keyRounded: true,
265
+ keyLinewidth: 1,
266
+ keyColors: null,
267
+ keyInteractive: false,
268
+ keyInteractiveHighlightChartStroke: 'black',
269
+ keyInteractiveHighlightChartFill:'rgba(255,255,255,0.7)',
270
+ keyInteractiveHighlightLabel:'rgba(255,0,0,0.2)',
271
+ keyLabelsColor: null,
272
+ keyLabelsFont: null,
273
+ keyLabelsSize: null,
274
+ keyLabelsBold: null,
275
+ keyLabelsItalic: null,
276
+ keyLabelsOffsetx: 0,
277
+ keyLabelsOffsety: 0,
278
+ keyFormattedDecimals: 0,
279
+ keyFormattedPoint: '.',
280
+ keyFormattedThousand: ',',
281
+ keyFormattedUnitsPre: '',
282
+ keyFormattedUnitsPost: '',
283
+ keyFormattedValueSpecific: null,
284
+ keyFormattedItemsCount: null,
285
+
286
+ unitsIngraph: false,
287
+
288
+ shadow: false,
289
+ shadowColor: '#666',
290
+ shadowBlur: 3,
291
+ shadowOffsetx: 3,
292
+ shadowOffsety: 3,
293
+
294
+ grouping: 'grouped',
295
+
296
+ tooltips: null,
297
+ tooltipsEvent: 'onclick',
298
+ tooltipsEffect: 'slide',
299
+ tooltipsCssClass: 'RGraph_tooltip',
300
+ tooltipsCss: null,
301
+ tooltipsHighlight: true,
302
+ tooltipsFormattedThousand: ',',
303
+ tooltipsFormattedPoint: '.',
304
+ tooltipsFormattedDecimals: 0,
305
+ tooltipsFormattedUnitsPre: '',
306
+ tooltipsFormattedUnitsPost: '',
307
+ tooltipsFormattedKeyColors: null,
308
+ tooltipsFormattedKeyColorsShape: 'square',
309
+ tooltipsFormattedKeyLabels: [],
310
+ tooltipsFormattedListType: 'ul',
311
+ tooltipsFormattedListItems: null,
312
+ tooltipsFormattedTableHeaders: null,
313
+ tooltipsFormattedTableData: null,
314
+ tooltipsPointer: true,
315
+ tooltipsPointerOffsetx: 0,
316
+ tooltipsPointerOffsety: 0,
317
+ tooltipsPositionStatic: true,
318
+ tooltipsHotspotYonly: false,
319
+ tooltipsHotspotIgnore: null,
320
+
321
+ highlightFill: 'rgba(255,255,255,0.7)',
322
+ highlightStroke: 'rgba(0,0,0,0)',
323
+ highlightStyle: null,
324
+
325
+ annotatable: false,
326
+ annotatableColor: 'black',
327
+ annotatableLinewidth: 1,
328
+
329
+ redraw: true,
330
+
331
+ variant: 'hbar',
332
+ variantThreedAngle: 0.1,
333
+ variantThreedOffsetx: 10,
334
+ variantThreedOffsety: 5,
335
+ variantThreedXaxis: true,
336
+ variantThreedYaxis: true,
337
+ variantThreedXaxisColor: '#ddd',
338
+ variantThreedYaxisColor: '#ddd',
339
+
340
+ adjustable: false,
341
+ adjustableOnly: null,
342
+
343
+ corners: 'square',
344
+ cornersRoundRadius: 10,
345
+ cornersRoundTop: true,
346
+ cornersRoundBottom: true,
347
+ cornersRoundTopRadius: null,
348
+ cornersRoundBottomRadius: null,
349
+
350
+ line: false,
351
+ lineColor: 'black',
352
+ lineLinejoin: 'round',
353
+ lineLinecap: 'round',
354
+ lineLinewidth: 1,
355
+ lineShadow: true,
356
+ lineShadowColor: '#666',
357
+ lineShadowBlur: 2,
358
+ lineShadowOffsetx: 2,
359
+ lineShadowOffsety: 2,
360
+ lineSpline: false,
361
+ lineTickmarksStyle: null,
362
+ lineTickmarksSize: 5,
363
+ lineTickmarksDrawNull: false,
364
+ lineTickmarksDrawNonNull: false,
365
+
366
+
367
+ animationTraceClip: 1,
368
+
369
+ clearto: 'rgba(0,0,0,0)'
370
+ }
371
+
372
+ // Check for support
373
+ if (!this.canvas) {
374
+ alert('[HBAR] No canvas support');
375
+ return;
376
+ }
377
+
378
+ //
379
+ // Allow the data to be given as a string
380
+ //
381
+ this.data = RGraph.stringsToNumbers(this.data);
382
+
383
+
384
+ // This loop is used to check for stacked or grouped charts and now
385
+ // also to convert strings to numbers. And now also undefined values
386
+ // (29/07/2016
387
+ for (i=0,len=this.data.length; i<len; ++i) {
388
+ if (typeof this.data[i] === 'object' && !RGraph.isNull(this.data[i])) {
389
+ this.stackedOrGrouped = true;
390
+ }
391
+ }
392
+
393
+
394
+ //
395
+ // Create the dollar objects so that functions can be added to them
396
+ //
397
+ var linear_data = RGraph.arrayLinearize(data);
398
+ for (var i=0,len=linear_data.length; i<len; ++i) {
399
+ this['$' + i] = {};
400
+ }
401
+
402
+
403
+
404
+ //
405
+ // Create the linear data array
406
+ //
407
+ this.data_arr = RGraph.arrayLinearize(this.data);
408
+
409
+
410
+
411
+
412
+ // Easy access to properties and the path function
413
+ var properties = this.properties;
414
+ this.path = RGraph.pathObjectFunction;
415
+
416
+ //
417
+ // "Decorate" the object with the generic effects if the effects library has been included
418
+ //
419
+ if (RGraph.Effects && typeof RGraph.Effects.decorate === 'function') {
420
+ RGraph.Effects.decorate(this);
421
+ }
422
+
423
+
424
+
425
+ // Add the responsive method. This method resides in the common file.
426
+ this.responsive = RGraph.responsive;
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+ //
436
+ // A setter
437
+ //
438
+ // @param name string The name of the property to set
439
+ // @param value mixed The value of the property
440
+ //
441
+ this.set = function (name)
442
+ {
443
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
444
+
445
+ // the number of arguments is only one and it's an
446
+ // object - parse it for configuration data and return.
447
+ if (arguments.length === 1 && typeof arguments[0] === 'object') {
448
+ for (i in arguments[0]) {
449
+ if (typeof i === 'string') {
450
+ this.set(i, arguments[0][i]);
451
+ }
452
+ }
453
+
454
+ return this;
455
+ }
456
+
457
+ properties[name] = value;
458
+
459
+ return this;
460
+ };
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+ //
470
+ // A getter
471
+ //
472
+ // @param name string The name of the property to get
473
+ //
474
+ this.get = function (name)
475
+ {
476
+ return properties[name];
477
+ };
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+ //
487
+ // The function you call to draw the bar chart
488
+ //
489
+ this.draw = function ()
490
+ {
491
+ //
492
+ // Fire the onbeforedraw event
493
+ //
494
+ RGraph.fireCustomEvent(this, 'onbeforedraw');
495
+
496
+
497
+
498
+ // Translate half a pixel for antialiasing purposes - but only if it hasn't been
499
+ // done already
500
+ //
501
+ // MUST be the first thing done!
502
+ //
503
+ if (!this.canvas.__rgraph_aa_translated__) {
504
+ this.context.translate(0.5,0.5);
505
+
506
+ this.canvas.__rgraph_aa_translated__ = true;
507
+ }
508
+
509
+ //
510
+ // Do the yaxis label substitution. This has to be called
511
+ // before the left (or right) margin size is calculated.
512
+ //
513
+ this.yaxisLabelSubstitution();
514
+
515
+
516
+ //
517
+ // Check that the bHBar isn't stacked with adjusting enabled
518
+ //
519
+ if (properties.adjustable && properties.grouping === 'stacked') {
520
+ alert('[RGRAPH] The HBar does not support stacked charts with adjusting');
521
+ }
522
+
523
+ //
524
+ // Set the correct number of horizontal grid lines if
525
+ // it hasn't been set already
526
+ //
527
+ if (RGraph.isNull(properties.backgroundGridHlines.count)) {
528
+ this.set('backgroundGridHlinesCount', this.data.length);
529
+ }
530
+
531
+ //
532
+ // If the chart is 3d then angle it
533
+ //
534
+
535
+ if (properties.variant === '3d') {
536
+
537
+ if (properties.textAccessible) {
538
+ // Nada
539
+ } else {
540
+ this.context.setTransform(1,properties.variantThreedAngle,0,1,0.5,0.5);
541
+ }
542
+
543
+ // Enlarge the margin if its 25
544
+ if (properties.marginBottom === 25) {
545
+ this.set('marginBottom', 80);
546
+ }
547
+ }
548
+
549
+
550
+
551
+
552
+ //
553
+ // Parse the colors. This allows for simple gradient syntax
554
+ //
555
+ if (!this.colorsParsed) {
556
+ this.parseColors();
557
+
558
+ // Don't want to do this again
559
+ this.colorsParsed = true;
560
+ }
561
+
562
+
563
+
564
+ // Reset this so that it doesn't grow uncontrollably
565
+ this.yaxisTitleSize = 0;
566
+
567
+
568
+ // Calculate the size of the labels regardless of anything else
569
+ if ( properties.yaxisLabels) {
570
+
571
+ var labels = properties.yaxisLabels,
572
+ marginName = properties.yaxisPosition === 'right' ? 'marginRight' : 'marginLeft';
573
+
574
+ var textConf = RGraph.getTextConf({
575
+ object: this,
576
+ prefix: 'yaxisLabels'
577
+ });
578
+
579
+ for (var i=0,len=0; i<labels.length; i+=1) {
580
+
581
+ var length = RGraph.measureText(
582
+ labels[i],
583
+ textConf.bold,
584
+ textConf.font,
585
+ textConf.size
586
+ )[0] || 0;
587
+
588
+ this.yaxisLabelsSize = Math.max(len, length);
589
+ len = this.yaxisLabelsSize;
590
+ }
591
+
592
+ // Is a title Specified? If so accommodate that
593
+ if ( properties.yaxisTitle) {
594
+
595
+ var textConf = RGraph.getTextConf({
596
+ object: this,
597
+ prefix: 'yaxisTitle'
598
+ });
599
+
600
+ var titleSize = RGraph.measureText(
601
+ properties.yaxisTitle,
602
+ textConf.bold,
603
+ textConf.font,
604
+ textConf.size
605
+ ) || [];
606
+
607
+
608
+ this.yaxisTitleSize += titleSize[1];
609
+ properties[marginName] += this.yaxisTitleSize;
610
+ }
611
+ }
612
+
613
+
614
+
615
+ //
616
+ // Accomodate autosizing the left/right margin
617
+ //
618
+ if (properties.marginLeftAuto) {
619
+ var name = properties.yaxisPosition === 'right' ? 'marginRight' : 'marginLeft';
620
+
621
+ this.set(
622
+ name,
623
+ this.yaxisLabelsSize + this.yaxisTitleSize + 10
624
+ );
625
+ }
626
+
627
+
628
+
629
+ //
630
+ // Make the margins easy to access
631
+ //
632
+ this.marginLeft = properties.marginLeft;
633
+ this.marginRight = properties.marginRight;
634
+ this.marginTop = properties.marginTop;
635
+ this.marginBottom = properties.marginBottom;
636
+
637
+
638
+
639
+
640
+ //
641
+ // Stop the coords array from growing uncontrollably
642
+ //
643
+ this.coords = [];
644
+ this.coords2 = [];
645
+ this.coordsText = [];
646
+ this.max = 0;
647
+
648
+ //
649
+ // Check for xaxisScaleMin in stacked charts
650
+ //
651
+ if ( properties.xaxisScaleMin > 0 && properties.grouping === 'stacked') {
652
+ alert('[HBAR] Using xaxisScaleMin is not supported with stacked charts, resetting xaxisScaleMin to zero');
653
+ this.set('xaxisScaleMin', 0);
654
+ }
655
+
656
+ //
657
+ // Work out a few things. They need to be here because they depend on things you can change before you
658
+ // call Draw() but after you instantiate the object
659
+ //
660
+ this.graphwidth = this.canvas.width - this.marginLeft - this.marginRight;
661
+ this.graphheight = this.canvas.height - this.marginTop - this.marginBottom;
662
+ this.halfgrapharea = this.grapharea / 2;
663
+ this.halfTextHeight = properties.textSize / 2;
664
+ this.halfway = Math.round((this.graphwidth / 2) + this.marginLeft);
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+
673
+
674
+
675
+ // Progressively Draw the chart
676
+ RGraph.Background.draw(this);
677
+
678
+ this.drawbars();
679
+ this.drawAxes();
680
+ this.drawLabels();
681
+
682
+
683
+ // Draw the labelsInbar
684
+ this.drawLabelsInbar();
685
+
686
+
687
+ // Draw the key if necessary
688
+ if (properties.key && properties.key.length) {
689
+ RGraph.drawKey(this, properties.key, properties.colors);
690
+ }
691
+
692
+ //
693
+ // Draw a line on the chart if necessary
694
+ //
695
+ if (this.properties.line) {
696
+ this.drawLine();
697
+ }
698
+
699
+
700
+
701
+ //
702
+ // Setup the context menu if required
703
+ //
704
+ if (properties.contextmenu) {
705
+ RGraph.showContext(this);
706
+ }
707
+
708
+
709
+
710
+ //
711
+ // Draw "in graph" labels
712
+ //
713
+ RGraph.drawInGraphLabels(this);
714
+
715
+
716
+
717
+
718
+ //
719
+ // Add custom text thats specified
720
+ //
721
+ RGraph.addCustomText(this);
722
+
723
+
724
+
725
+
726
+
727
+
728
+
729
+
730
+
731
+ //
732
+ // This installs the event listeners
733
+ //
734
+ RGraph.installEventListeners(this);
735
+
736
+
737
+ //
738
+ // Fire the onfirstdraw event
739
+ //
740
+ if (this.firstDraw) {
741
+ this.firstDraw = false;
742
+ RGraph.fireCustomEvent(this, 'onfirstdraw');
743
+ this.firstDrawFunc();
744
+ }
745
+
746
+
747
+
748
+ //
749
+ // Fire the RGraph draw event
750
+ //
751
+ RGraph.fireCustomEvent(this, 'ondraw');
752
+
753
+
754
+
755
+
756
+
757
+
758
+
759
+
760
+
761
+
762
+
763
+ //
764
+ // Install any inline responsive configuration. This
765
+ // should be last in the draw function - even after
766
+ // the draw events.
767
+ //
768
+ RGraph.installInlineResponsive(this);
769
+
770
+
771
+
772
+
773
+
774
+
775
+
776
+
777
+
778
+
779
+
780
+
781
+ return this;
782
+ };
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+ //
792
+ // Used in chaining. Runs a function there and then - not waiting for
793
+ // the events to fire (eg the onbeforedraw event)
794
+ //
795
+ // @param function func The function to execute
796
+ //
797
+ this.exec = function (func)
798
+ {
799
+ func(this);
800
+
801
+ return this;
802
+ };
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+ //
812
+ // This draws the axes
813
+ //
814
+ this.drawAxes = function ()
815
+ {
816
+ // Draw the X axis
817
+ RGraph.drawXAxis(this);
818
+
819
+ // Draw the Y axis
820
+ RGraph.drawYAxis(this);
821
+ };
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+ //
831
+ // Do the label substitution. This is called from the top of the
832
+ // draw function
833
+ //
834
+ this.yaxisLabelSubstitution = function ()
835
+ {
836
+ if (properties.yaxisLabels && properties.yaxisLabels.length) {
837
+ //
838
+ // If the yaxisLabels option is a string then turn it
839
+ // into an array.
840
+ //
841
+ if (typeof properties.yaxisLabels === 'string') {
842
+ properties.yaxisLabels = RGraph.arrayPad({
843
+ array: [],
844
+ length: this.data.length,
845
+ value: properties.yaxisLabels
846
+ });
847
+ }
848
+
849
+ //
850
+ // Label substitution
851
+ //
852
+ for (var i=0; i<properties.yaxisLabels.length; ++i) {
853
+ properties.yaxisLabels[i] = RGraph.labelSubstitution({
854
+ object: this,
855
+ text: properties.yaxisLabels[i],
856
+ index: i,
857
+ value: this.data[i],
858
+ decimals: properties.yaxisLabelsFormattedDecimals || 0,
859
+ unitsPre: properties.yaxisLabelsFormattedUnitsPre || '',
860
+ unitsPost: properties.yaxisLabelsFormattedUnitsPost || '',
861
+ thousand: properties.yaxisLabelsFormattedThousand || ',',
862
+ point: properties.yaxisLabelsFormattedPoint || '.'
863
+ });
864
+ }
865
+ }
866
+ };
867
+
868
+
869
+
870
+
871
+
872
+
873
+
874
+
875
+ //
876
+ // This draws the labels for the graph
877
+ //
878
+ this.drawLabels = function ()
879
+ {
880
+ // Labels are now drawn by the RGraph.drawYaxis() function
881
+ };
882
+
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+ //
891
+ // This function draws the bars. It also draw 3D axes as the axes drawing bit
892
+ // is don AFTER the bars are drawn
893
+ //
894
+ this.drawbars = function ()
895
+ {
896
+ this.context.lineWidth = properties.linewidth;
897
+ this.context.strokeStyle = properties.colorsStroke;
898
+ this.context.fillStyle = properties.colors[0];
899
+
900
+ var prevX = 0,
901
+ prevY = 0;
902
+
903
+
904
+ //
905
+ // Work out the max value
906
+ //
907
+
908
+ if ( properties.xaxisScaleMax) {
909
+
910
+ this.scale2 = RGraph.getScale({object: this, options: {
911
+ 'scale.max': properties.xaxisScaleMax,
912
+ 'scale.min': properties.xaxisScaleMin,
913
+ 'scale.decimals': Number( properties.xaxisScaleDecimals),
914
+ 'scale.point': properties.xaxisScalePoint,
915
+ 'scale.thousand': properties.xaxisScaleThousand,
916
+ 'scale.round': properties.xaxisScaleRound,
917
+ 'scale.units.pre': properties.xaxisScaleUnitsPre,
918
+ 'scale.units.post': properties.xaxisScaleUnitsPost,
919
+ 'scale.labels.count': properties.xaxisLabelsCount,
920
+ 'scale.strict': true
921
+ }});
922
+
923
+ this.max = this.scale2.max;
924
+
925
+ } else {
926
+
927
+ var grouping = properties.grouping;
928
+
929
+ for (i=0; i<this.data.length; ++i) {
930
+ if (typeof this.data[i] == 'object') {
931
+ var value = grouping == 'grouped' ? Number(RGraph.arrayMax(this.data[i], true)) : Number(RGraph.arraySum(this.data[i]));
932
+ } else {
933
+ var value = Number(Math.abs(this.data[i]));
934
+ }
935
+
936
+ this.max = Math.max(Math.abs(this.max), Math.abs(value));
937
+ }
938
+
939
+ this.scale2 = RGraph.getScale({object: this, options: {
940
+ 'scale.max': this.max,
941
+ 'scale.min': properties.xaxisScaleMin,
942
+ 'scale.decimals': Number( properties.xaxisScaleDecimals),
943
+ 'scale.point': properties.xaxisScalePoint,
944
+ 'scale.thousand': properties.xaxisScaleThousand,
945
+ 'scale.round': properties.xaxisScaleRound,
946
+ 'scale.units.pre': properties.xaxisScaleUnitsPre,
947
+ 'scale.units.post': properties.xaxisScaleUnitsPost,
948
+ 'scale.labels.count': properties.xaxisLabelsCount
949
+ }});
950
+
951
+
952
+ this.max = this.scale2.max;
953
+ this.min = this.scale2.min;
954
+ }
955
+
956
+ if ( properties.xaxisScaleDecimals == null && Number(this.max) == 1) {
957
+ this.set('xaxisScaleDecimals', 1);
958
+ }
959
+
960
+ //
961
+ // This is here to facilitate sequential colors
962
+ //
963
+ var colorIdx = 0;
964
+
965
+ //
966
+ // For grouped bars we need to calculate the number of bars
967
+ //
968
+ this.numbars = RGraph.arrayLinearize(this.data).length;
969
+
970
+
971
+
972
+
973
+ //
974
+ // if the chart is adjustable fix the scale so that it doesn't change.
975
+ //
976
+ // It's here (after the scale generation) so that the max value can be
977
+ // set to the maximum scale value)
978
+ //
979
+ if (properties.adjustable && ! properties.xaxisScaleMax) {
980
+ this.set('xaxisScaleMax', this.scale2.max);
981
+ }
982
+
983
+
984
+
985
+ // Draw the 3d axes if necessary
986
+ if (properties.variant === '3d') {
987
+ RGraph.draw3DAxes(this);
988
+ }
989
+
990
+
991
+
992
+
993
+
994
+
995
+ //
996
+ // The bars are drawn HERE
997
+ //
998
+ var graphwidth = (this.canvas.width - this.marginLeft - this.marginRight);
999
+ var halfwidth = graphwidth / 2;
1000
+
1001
+ for (i=(len=this.data.length-1); i>=0; --i) {
1002
+
1003
+ // Work out the width and height
1004
+ var width = Math.abs((this.data[i] / this.max) * graphwidth);
1005
+ var height = this.graphheight / this.data.length;
1006
+
1007
+ var orig_height = height;
1008
+
1009
+ var x = this.getXCoord(0);
1010
+ var y = this.marginTop + (i * height);
1011
+ var vmargin = properties.marginInner;
1012
+
1013
+ //
1014
+ // Edge case: When X axis min is greater than 0
1015
+ // eg min=1 and max=2.5
1016
+ //
1017
+ if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) {
1018
+ x = this.getXCoord(properties.xaxisScaleMin);
1019
+ }
1020
+
1021
+ // Account for the Y axis being on the right hand side
1022
+ if ( properties.yaxisPosition === 'right') {
1023
+ x = this.canvas.width - this.marginRight - Math.abs(width);
1024
+ }
1025
+
1026
+ // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
1027
+ if (width < 0) {
1028
+ x -= width;
1029
+ width = Math.abs(width);
1030
+ }
1031
+
1032
+ //
1033
+ // Turn on the shadow if need be
1034
+ //
1035
+ if (properties.shadow) {
1036
+ this.context.shadowColor = properties.shadowColor;
1037
+ this.context.shadowBlur = properties.shadowBlur;
1038
+ this.context.shadowOffsetX = properties.shadowOffsetx;
1039
+ this.context.shadowOffsetY = properties.shadowOffsety;
1040
+ }
1041
+
1042
+ //
1043
+ // Draw the bar
1044
+ //
1045
+ this.context.beginPath();
1046
+
1047
+ // Standard (non-grouped and non-stacked) bars here
1048
+ if (typeof this.data[i] === 'number' || RGraph.isNull(this.data[i])) {
1049
+
1050
+ var barHeight = height - (2 * vmargin),
1051
+ barWidth = ((this.data[i] - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * this.graphwidth,
1052
+ barX = x;
1053
+
1054
+ // Accommodate an offset Y axis
1055
+ if (this.scale2.min < 0 && this.scale2.max > 0 && properties.yaxisPosition === 'left') {
1056
+ barWidth = (this.data[i] / (this.max - properties.xaxisScaleMin)) * this.graphwidth;
1057
+ }
1058
+
1059
+ // Account for Y axis pos
1060
+ if ( properties.yaxisPosition == 'center') {
1061
+ barWidth /= 2;
1062
+ barX += halfwidth;
1063
+
1064
+ if (this.data[i] < 0) {
1065
+ barWidth = (Math.abs(this.data[i]) - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin);
1066
+ barWidth = barWidth * (this.graphwidth / 2);
1067
+ barX = ((this.graphwidth / 2) + this.marginLeft) - barWidth;
1068
+ } else if (this.data[i] > 0) {
1069
+ barX = (this.graphwidth / 2) + this.marginLeft;
1070
+ }
1071
+
1072
+
1073
+ } else if ( properties.yaxisPosition == 'right') {
1074
+
1075
+ barWidth = Math.abs(barWidth);
1076
+ barX = this.canvas.width - this.marginRight - barWidth;
1077
+
1078
+ }
1079
+
1080
+ // Set the fill color
1081
+ this.context.strokeStyle = properties.colorsStroke;
1082
+ this.context.fillStyle = properties.colors[0];
1083
+
1084
+ // Sequential colors
1085
+ ++colorIdx;
1086
+ if (properties.colorsSequential && typeof colorIdx === 'number') {
1087
+ if (properties.colors[this.numbars - colorIdx]) {
1088
+ this.context.fillStyle = properties.colors[this.numbars - colorIdx];
1089
+ } else {
1090
+ this.context.fillStyle = properties.colors[properties.colors.length - 1];
1091
+ }
1092
+ }
1093
+
1094
+
1095
+ if (properties.corners === 'round') {
1096
+ this.context.rectOld = this.context.rect;
1097
+ this.context.rect = this.roundedCornersRect;
1098
+ }
1099
+
1100
+ this.context.beginPath();
1101
+ this.context.lineJoin = 'miter';
1102
+ this.context.lineCap = 'square';
1103
+
1104
+ // Draw the rounded corners rect positive or negative
1105
+ if (properties.corners === 'square' || (this.data[i] > 0 && this.properties.yaxisPosition !== 'right') ) {
1106
+ this.context.rect(
1107
+ barX,
1108
+ this.marginTop + (i * height) + properties.marginInner,
1109
+ barWidth,
1110
+ barHeight
1111
+ );
1112
+ } else {
1113
+ this.roundedCornersRectNegative(
1114
+ barX,
1115
+ this.marginTop + (i * height) + properties.marginInner,
1116
+ barWidth,
1117
+ barHeight
1118
+ );
1119
+ }
1120
+
1121
+ this.context.stroke();
1122
+ this.context.fill();
1123
+
1124
+ // Put the rect function back to what it was
1125
+ if (properties.corners === 'round' ) {
1126
+ this.context.rect = this.context.rectOld;
1127
+ this.context.rectOld = null;
1128
+ }
1129
+
1130
+
1131
+ // This skirts an annoying "extra fill bug"
1132
+ // by getting rid of the last path which
1133
+ //was drawm - which is usually the last
1134
+ //bar to be drawn (the bars are drawn
1135
+ //from bottom to top). Woo.
1136
+ this.path('b');
1137
+
1138
+
1139
+ this.coords.push([
1140
+ barX,
1141
+ y + vmargin,
1142
+ barWidth,
1143
+ height - (2 * vmargin),
1144
+ this.context.fillStyle,
1145
+ this.data[i],
1146
+ true
1147
+ ]);
1148
+
1149
+
1150
+
1151
+
1152
+
1153
+
1154
+ // Draw the 3D effect using the coords that have just been stored
1155
+ if (properties.variant === '3d' && typeof this.data[i] == 'number') {
1156
+
1157
+
1158
+ var prevStrokeStyle = this.context.strokeStyle,
1159
+ prevFillStyle = this.context.fillStyle;
1160
+
1161
+ //
1162
+ // Turn off the shadow for the 3D bits
1163
+ //
1164
+ RGraph.noShadow(this);
1165
+
1166
+ // DRAW THE 3D BITS HERE
1167
+ var barX = barX,
1168
+ barY = y + vmargin,
1169
+ barW = barWidth,
1170
+ barH = height - (2 * vmargin),
1171
+ offsetX = properties.variantThreedOffsetx,
1172
+ offsetY = properties.variantThreedOffsety,
1173
+ value = this.data[i];
1174
+
1175
+
1176
+ this.path(
1177
+ 'b m % % l % % l % % l % % c s % f % f rgba(255,255,255,0.6)',
1178
+ barX, barY,
1179
+ barX + offsetX - ( properties.yaxisPosition == 'left' && value < 0 ? offsetX : 0), barY - offsetY,
1180
+ barX + barW + offsetX - ( properties.yaxisPosition == 'center' && value < 0 ? offsetX : 0), barY - offsetY,
1181
+ barX + barW, barY,
1182
+ this.context.strokeStyle,this.context.fillStyle
1183
+ );
1184
+
1185
+ if ( properties.yaxisPosition !== 'right'
1186
+ && !( properties.yaxisPosition === 'center' && value < 0)
1187
+ && value >= 0
1188
+ && !RGraph.isNull(value)
1189
+ ) {
1190
+
1191
+ this.path(
1192
+ 'b fs % m % % l % % l % % l % % c s % f % f rgba(0,0,0,0.25)',
1193
+ prevFillStyle,
1194
+ barX + barW, barY,
1195
+ barX + barW + offsetX, barY - offsetY,
1196
+ barX + barW + offsetX, barY - offsetY + barH,
1197
+ barX + barW, barY + barH,
1198
+ this.context.strokeStyle,prevFillStyle
1199
+ );
1200
+ }
1201
+ }
1202
+
1203
+
1204
+
1205
+
1206
+
1207
+
1208
+ //
1209
+ // Stacked bar chart
1210
+ //
1211
+ } else if (typeof this.data[i] == 'object' && properties.grouping === 'stacked') {
1212
+
1213
+ if ( properties.yaxisPosition == 'center') {
1214
+ alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped');
1215
+ } else if ( properties.yaxisPosition == 'right') {
1216
+ var x = this.canvas.width - this.marginRight
1217
+ }
1218
+
1219
+ var barHeight = height - (2 * vmargin);
1220
+
1221
+ if (typeof this.coords2[i] == 'undefined') {
1222
+ this.coords2[i] = [];
1223
+ }
1224
+
1225
+ for (j=0; j<this.data[i].length; ++j) {
1226
+
1227
+ // The previous 3D segments would have turned the shadow off - so turn it back on
1228
+ if (properties.shadow && properties.variant === '3d') {
1229
+ this.context.shadowColor = properties.shadowColor;
1230
+ this.context.shadowBlur = properties.shadowBlur;
1231
+ this.context.shadowOffsetX = properties.shadowOffsetx;
1232
+ this.context.shadowOffsetY = properties.shadowOffsety;
1233
+ }
1234
+
1235
+ //
1236
+ // Ensure the number is positive
1237
+ //(even though having the X axis on the right implies a
1238
+ //negative value)
1239
+ //
1240
+ if (!RGraph.isNull(this.data[i][j])) this.data[i][j] = Math.abs(this.data[i][j]);
1241
+
1242
+
1243
+ var last = (j === (this.data[i].length - 1) );
1244
+
1245
+ // Set the fill/stroke colors
1246
+ this.context.strokeStyle = properties.colorsStroke;
1247
+
1248
+ // Sequential colors
1249
+ ++colorIdx;
1250
+ if (properties.colorsSequential && typeof colorIdx === 'number') {
1251
+ if (properties.colors[this.numbars - colorIdx]) {
1252
+ this.context.fillStyle = properties.colors[this.numbars - colorIdx];
1253
+ } else {
1254
+ this.context.fillStyle = properties.colors[properties.colors.length - 1];
1255
+ }
1256
+ } else if (properties.colors[j]) {
1257
+ this.context.fillStyle = properties.colors[j];
1258
+ }
1259
+
1260
+
1261
+ var width = (((this.data[i][j]) / (this.max))) * this.graphwidth;
1262
+ var totalWidth = (RGraph.arraySum(this.data[i]) / this.max) * this.graphwidth;
1263
+
1264
+ if ( properties.yaxisPosition === 'right') {
1265
+ x -= width;
1266
+ }
1267
+
1268
+
1269
+
1270
+
1271
+
1272
+
1273
+
1274
+
1275
+
1276
+
1277
+
1278
+
1279
+ if (properties.corners === 'round' && j === (this.data[i].length - 1) ) {
1280
+ this.context.rectOld = this.context.rect;
1281
+ this.context.rect = this.roundedCornersRect;
1282
+ }
1283
+
1284
+ this.context.beginPath();
1285
+ this.context.lineJoin = 'miter';
1286
+ this.context.lineCap = 'square';
1287
+
1288
+ // Draw the rounded corners rect positive or negative
1289
+ if (properties.corners === 'square' || (j < (this.data[i].length - 1) && this.data[i][j] > 0) ) {
1290
+ this.context.rect(x, this.marginTop + properties.marginInner + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
1291
+ } else {
1292
+ if (this.properties.yaxisPosition === 'left') {
1293
+ this.context.rect(x, this.marginTop + properties.marginInner + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
1294
+ } else {
1295
+ this.roundedCornersRectNegative(x, this.marginTop + properties.marginInner + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
1296
+ }
1297
+ }
1298
+
1299
+ this.context.stroke();
1300
+ this.context.fill();
1301
+
1302
+ // This avoids a "double fill"bug by resetting
1303
+ // the path
1304
+ this.context.beginPath();
1305
+
1306
+ // Put the rect function back to what it was
1307
+ if (properties.corners === 'round' && j === (this.data[i].length - 1) ) {
1308
+ this.context.rect = this.context.rectOld;
1309
+ this.context.rectOld = null;
1310
+ }
1311
+
1312
+ //this.context.strokeRect(x, this.marginTop + properties.marginInner + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
1313
+ //this.context.fillRect(x, this.marginTop + properties.marginInner + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
1314
+
1315
+
1316
+ //
1317
+ // Store the coords for tooltips
1318
+ //
1319
+
1320
+ // The last property of this array is a boolean which tells you whether the value is the last or not
1321
+ this.coords.push([
1322
+ x,
1323
+ y + vmargin,
1324
+ width,
1325
+ height - (2 * vmargin),
1326
+ this.context.fillStyle,
1327
+ RGraph.arraySum(this.data[i]),
1328
+ j == (this.data[i].length - 1)
1329
+ ]);
1330
+
1331
+ this.coords2[i].push([
1332
+ x,
1333
+ y + vmargin,
1334
+ width,
1335
+ height - (2 * vmargin),
1336
+ this.context.fillStyle,
1337
+ RGraph.arraySum(this.data[i]),
1338
+ j == (this.data[i].length - 1)
1339
+ ]);
1340
+
1341
+
1342
+
1343
+
1344
+
1345
+
1346
+ // 3D effect
1347
+ if (properties.variant === '3d') {
1348
+
1349
+ //
1350
+ // Turn off the shadow for the 3D bits
1351
+ //
1352
+ RGraph.noShadow(this);
1353
+
1354
+ var prevStrokeStyle = this.context.strokeStyle,
1355
+ prevFillStyle = this.context.fillStyle;
1356
+
1357
+ // DRAW THE 3D BITS HERE
1358
+ var barX = x,
1359
+ barY = y + vmargin,
1360
+ barW = width,
1361
+ barH = height - (2 * vmargin),
1362
+ offsetX = properties.variantThreedOffsetx,
1363
+ offsetY = properties.variantThreedOffsety,
1364
+ value = this.data[i][j];
1365
+
1366
+ if (!RGraph.isNull(value)) {
1367
+ this.path(
1368
+ 'b m % % l % % l % % l % % c s % f % f rgba(255,255,255,0.6)',
1369
+ barX, barY,
1370
+ barX + offsetX, barY - offsetY,
1371
+ barX + barW + offsetX, barY - offsetY,
1372
+ barX + barW, barY,
1373
+ this.context.strokeStyle,this.context.fillStyle
1374
+ );
1375
+ }
1376
+
1377
+ if ( properties.yaxisPosition !== 'right'
1378
+ && !( properties.yaxisPosition === 'center' && value < 0)
1379
+ && !RGraph.isNull(value)
1380
+ ) {
1381
+
1382
+ this.path(
1383
+ 'fs % b m % % l % % l % % l % % c s % f % f rgba(0,0,0,0.25)',
1384
+ prevFillStyle,
1385
+ barX + barW, barY,
1386
+ barX + barW + offsetX, barY - offsetY,
1387
+ barX + barW + offsetX, barY - offsetY + barH,
1388
+ barX + barW, barY + barH,
1389
+ this.context.strokeStyle,prevFillStyle
1390
+ );
1391
+ }
1392
+
1393
+ this.context.beginPath();
1394
+ this.context.strokeStyle = prevStrokeStyle;
1395
+ this.context.fillStyle = prevFillStyle;
1396
+ }
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+ if ( properties.yaxisPosition !== 'right') {
1404
+ x += width;
1405
+ }
1406
+ }
1407
+
1408
+
1409
+
1410
+
1411
+
1412
+
1413
+
1414
+
1415
+ //
1416
+ // A grouped bar chart
1417
+ //
1418
+ } else if (typeof this.data[i] == 'object' && properties.grouping == 'grouped') {
1419
+
1420
+ var vmarginGrouped = properties.marginInnerGrouped;
1421
+ var individualBarHeight = ((height - (2 * vmargin) - ((this.data[i].length - 1) * vmarginGrouped)) / this.data[i].length)
1422
+
1423
+ if (typeof this.coords2[i] == 'undefined') {
1424
+ this.coords2[i] = [];
1425
+ }
1426
+
1427
+ for (j=(this.data[i].length - 1); j>=0; --j) {
1428
+
1429
+ //
1430
+ // Turn on the shadow if need be
1431
+ //
1432
+ if (properties.shadow) {
1433
+ RGraph.setShadow(
1434
+ this,
1435
+ properties.shadowColor,
1436
+ properties.shadowOffsetx,
1437
+ properties.shadowOffsety,
1438
+ properties.shadowBlur
1439
+ );
1440
+ }
1441
+
1442
+ // Set the fill/stroke colors
1443
+ this.context.strokeStyle = properties.colorsStroke;
1444
+
1445
+ // Sequential colors
1446
+ ++colorIdx;
1447
+ if (properties.colorsSequential && typeof colorIdx === 'number') {
1448
+ if (properties.colors[this.numbars - colorIdx]) {
1449
+ this.context.fillStyle = properties.colors[this.numbars - colorIdx];
1450
+ } else {
1451
+ this.context.fillStyle = properties.colors[properties.colors.length - 1];
1452
+ }
1453
+ } else if (properties.colors[j]) {
1454
+ this.context.fillStyle = properties.colors[j];
1455
+ }
1456
+
1457
+
1458
+
1459
+ var startY = this.marginTop + (height * i) + (individualBarHeight * j) + vmargin + (vmarginGrouped * j);
1460
+
1461
+ if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) {
1462
+ var width = ((this.data[i][j] - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * (this.canvas.width - this.marginLeft - this.marginRight );
1463
+ var startX = this.getXCoord((properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) ? properties.xaxisScaleMin : 0);//this.marginLeft;
1464
+ } else {
1465
+ var width = (this.data[i][j] / (this.max - properties.xaxisScaleMin)) * (this.canvas.width - this.marginLeft - this.marginRight);
1466
+ var startX = this.getXCoord(0);
1467
+ }
1468
+
1469
+
1470
+
1471
+ // Account for the Y axis being in the middle
1472
+ if ( properties.yaxisPosition == 'center') {
1473
+ width /= 2;
1474
+
1475
+ // Account for the Y axis being on the right
1476
+ } else if ( properties.yaxisPosition == 'right') {
1477
+ width = Math.abs(width);
1478
+ startX = this.canvas.width - this.marginRight - Math.abs(width);
1479
+ }
1480
+
1481
+ if (width < 0) {
1482
+ startX += width;
1483
+ width *= -1;
1484
+ }
1485
+
1486
+ if (properties.corners === 'round') {
1487
+ this.context.rectOld = this.context.rect;
1488
+ this.context.rect = this.roundedCornersRect;
1489
+ }
1490
+
1491
+ this.context.beginPath();
1492
+ this.context.lineJoin = 'miter';
1493
+ this.context.lineCap = 'square';
1494
+
1495
+ // Draw the rounded corners rect positive or negative
1496
+ if (properties.corners === 'square' || (this.properties.yaxisPosition === 'left' || this.properties.yaxisPosition === 'center')) {
1497
+ if (properties.corners === 'square' || this.data[i][j] > 0) {
1498
+ this.context.rect(startX, startY, width, individualBarHeight);
1499
+ } else {
1500
+ this.roundedCornersRectNegative(startX, startY, width, individualBarHeight);
1501
+ }
1502
+ } else {
1503
+ if (this.data[i][j] > 0 && properties.corners === 'round') {
1504
+ this.roundedCornersRectNegative(startX, startY, width, individualBarHeight);
1505
+ } else {
1506
+ this.context.rect(startX + width, startY, width, individualBarHeight);
1507
+ }
1508
+ }
1509
+
1510
+ this.context.stroke();
1511
+ this.context.fill();
1512
+
1513
+ // Put the rect function back to what it was
1514
+ if (properties.corners === 'round') {
1515
+ this.context.rect = this.context.rectOld;
1516
+ this.context.rectOld = null;
1517
+ }
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+ this.coords.push([
1525
+ startX,
1526
+ startY,
1527
+ width,
1528
+ individualBarHeight,
1529
+ this.context.fillStyle,
1530
+ this.data[i][j],
1531
+ true
1532
+ ]);
1533
+
1534
+ this.coords2[i].push([
1535
+ startX,
1536
+ startY,
1537
+ width,
1538
+ individualBarHeight,
1539
+ this.context.fillStyle,
1540
+ this.data[i][j],
1541
+ true
1542
+ ]);
1543
+
1544
+
1545
+
1546
+
1547
+
1548
+
1549
+
1550
+
1551
+
1552
+
1553
+
1554
+
1555
+ // 3D effect
1556
+ if (properties.variant === '3d') {
1557
+
1558
+ //
1559
+ // Turn off the shadow for the 3D bits
1560
+ //
1561
+ RGraph.noShadow(this);
1562
+
1563
+ var prevStrokeStyle = this.context.strokeStyle,
1564
+ prevFillStyle = this.context.fillStyle;
1565
+
1566
+ // DRAW THE 3D BITS HERE
1567
+ var barX = startX,
1568
+ barY = startY,
1569
+ barW = width,
1570
+ barH = individualBarHeight,
1571
+ offsetX = properties.variantThreedOffsetx,
1572
+ offsetY = properties.variantThreedOffsety,
1573
+ value = this.data[i][j];
1574
+
1575
+ this.path(
1576
+ 'b m % % l % % l % % l % % c s % f % f rgba(255,255,255,0.6)',
1577
+ barX, barY,
1578
+ barX + offsetX, barY - offsetY,
1579
+ barX + barW + offsetX - (value < 0 ? offsetX : 0), barY - offsetY,
1580
+ barX + barW, barY,
1581
+ this.context.strokeStyle,this.context.fillStyle
1582
+ );
1583
+
1584
+ if ( properties.yaxisPosition !== 'right'
1585
+ && !( properties.yaxisPosition === 'center' && value < 0)
1586
+ && value >= 0
1587
+ && !RGraph.isNull(value)
1588
+ ) {
1589
+
1590
+ this.path(
1591
+ 'fs % b m % % l % % l % % l % % c s % f % f rgba(0,0,0,0.25)',
1592
+ prevFillStyle,
1593
+ barX + barW, barY,
1594
+ barX + barW + offsetX, barY - offsetY,
1595
+ barX + barW + offsetX, barY - offsetY + barH,
1596
+ barX + barW, barY + barH,
1597
+ this.context.strokeStyle,prevFillStyle
1598
+ );
1599
+ }
1600
+
1601
+
1602
+
1603
+
1604
+
1605
+ this.context.beginPath();
1606
+ this.context.strokeStyle = prevStrokeStyle;
1607
+ this.context.fillStyle = prevFillStyle;
1608
+ }
1609
+ }
1610
+
1611
+ startY += vmargin;
1612
+
1613
+ // This skirts an annoying "extra fill bug"
1614
+ // by getting rid of the last path which
1615
+ // was drawn - which is usually the last
1616
+ // bar to be drawn (the bars are drawn
1617
+ // from bottom to top). Woo.
1618
+ this.path('b');
1619
+ }
1620
+
1621
+ this.context.closePath();
1622
+ }
1623
+
1624
+ this.context.stroke();
1625
+ this.context.fill();
1626
+
1627
+ // Sunday 30th April 2023:
1628
+ // Why is this necessary? It causes the title
1629
+ // (if it's big enough) to be cut off so it's
1630
+ // been commented out.
1631
+ //
1632
+ // Under certain circumstances we can cover the shadow
1633
+ // overspill with a white rectangle
1634
+ //
1635
+ //if ( properties.yaxisPosition === 'right') {
1636
+ // this.path(
1637
+ // 'cr % % % %',
1638
+ // this.canvas.width - this.marginRight + properties.variantThreedOffsetx,'0',this.marginRight,this.canvas.height
1639
+ // );
1640
+ //}
1641
+
1642
+
1643
+
1644
+
1645
+
1646
+
1647
+ // Draw the 3d axes AGAIN if the Y axis is on the right
1648
+ if ( properties.yaxisPosition === 'right'
1649
+ && properties.variant === '3d'
1650
+ ) {
1651
+ RGraph.draw3DYAxis(this);
1652
+ }
1653
+
1654
+ //
1655
+ // Now the bars are stroke()ed, turn off the shadow
1656
+ //
1657
+ RGraph.noShadow(this);
1658
+
1659
+
1660
+ //
1661
+ // Reverse the coords arrays as the bars are drawn from the borrom up now
1662
+ //
1663
+ this.coords = RGraph.arrayReverse(this.coords);
1664
+
1665
+ if (properties.grouping === 'grouped') {
1666
+ for (var i=0; i<this.coords2.length; ++i) {
1667
+ this.coords2[i] = RGraph.arrayReverse(this.coords2[i]);
1668
+ }
1669
+ }
1670
+
1671
+
1672
+ this.redrawBars();
1673
+ };
1674
+
1675
+
1676
+
1677
+
1678
+
1679
+
1680
+
1681
+
1682
+ //
1683
+ // This function goes over the bars after they been drawn, so that upwards shadows are underneath the bars
1684
+ //
1685
+ this.redrawBars = function ()
1686
+ {
1687
+ if (!properties.redraw) {
1688
+ return;
1689
+ }
1690
+
1691
+ var coords = this.coords;
1692
+
1693
+ var font = properties.textFont,
1694
+ size = properties.textSize,
1695
+ color = properties.textColor;
1696
+
1697
+ RGraph.noShadow(this);
1698
+ this.context.strokeStyle = properties.colorsStroke;
1699
+
1700
+ for (var i=0; i<coords.length; ++i) {
1701
+
1702
+ if (properties.shadow) {
1703
+
1704
+ this.path(
1705
+ 'b lw % r % % % % s % f %',
1706
+ properties.linewidth,
1707
+ coords[i][0],coords[i][1],coords[i][2],coords[i][3],
1708
+ properties.colorsStroke,coords[i][4]
1709
+ );
1710
+ }
1711
+
1712
+
1713
+
1714
+
1715
+
1716
+ // TODO Maybe put a function here to check the
1717
+ // various conditions as to whether a labelsAbove
1718
+ // label should be shown or not
1719
+
1720
+ // Draw labels "above" the bar
1721
+ var halign = 'left';
1722
+ if (
1723
+ properties.labelsAbove
1724
+ && coords[i][6]
1725
+ && !RGraph.isNull(this.data[i])
1726
+ ) {
1727
+
1728
+ var border = (coords[i][0] + coords[i][2] + 7 + this.context.measureText(properties.labelsAboveUnitsPre + this.coords[i][5] + properties.labelsAboveUnitsPost).width) > this.canvas.width ? true : false,
1729
+ text = RGraph.numberFormat({
1730
+ object: this,
1731
+ number: (this.coords[i][5]).toFixed(properties.labelsAboveDecimals),
1732
+ unitspre: properties.labelsAboveUnitsPre,
1733
+ unitspost: properties.labelsAboveUnitsPost,
1734
+ point: properties.labelsAbovePoint,
1735
+ thousand: properties.labelsAboveThousand
1736
+ });
1737
+
1738
+ RGraph.noShadow(this);
1739
+
1740
+ // Check for specific labels
1741
+ if (typeof properties.labelsAboveSpecific === 'object' && properties.labelsAboveSpecific && properties.labelsAboveSpecific[i]) {
1742
+ text = properties.labelsAboveSpecific[i];
1743
+ }
1744
+
1745
+ var x = coords[i][0] + coords[i][2] + 5;
1746
+ var y = coords[i][1] + (coords[i][3] / 2);
1747
+
1748
+ if ( properties.yaxisPosition === 'right') {
1749
+ x = coords[i][0] - 5;
1750
+ halign = 'right';
1751
+ } else if ( properties.yaxisPosition === 'center' && this.data_arr[i] < 0) {
1752
+ x = coords[i][0] - 5;
1753
+ halign = 'right';
1754
+ }
1755
+
1756
+ var textConf = RGraph.getTextConf({
1757
+ object: this,
1758
+ prefix: 'labelsAbove'
1759
+ });
1760
+
1761
+
1762
+
1763
+
1764
+ RGraph.text({
1765
+
1766
+ object: this,
1767
+
1768
+ font: textConf.font,
1769
+ size: textConf.size,
1770
+ color: textConf.color,
1771
+ bold: textConf.bold,
1772
+ italic: textConf.italic,
1773
+
1774
+ x: x + properties.labelsAboveOffsetx,
1775
+ y: y + properties.labelsAboveOffsety,
1776
+ text: text,
1777
+ valign: 'center',
1778
+ halign: halign,
1779
+ tag: 'labels.above'
1780
+ });
1781
+ }
1782
+ }
1783
+ };
1784
+
1785
+
1786
+
1787
+
1788
+
1789
+
1790
+
1791
+
1792
+ //
1793
+ // This function can be used to get the appropriate bar information (if any)
1794
+ //
1795
+ // @param e Event object
1796
+ // @return Appriate bar information (if any)
1797
+ //
1798
+ this.getShape = function (e)
1799
+ {
1800
+ var mouseXY = RGraph.getMouseXY(e);
1801
+
1802
+ //
1803
+ // Loop through the bars determining if the mouse is over a bar
1804
+ //
1805
+ for (var i=0,len=this.coords.length; i<len; i++) {
1806
+
1807
+ if (RGraph.tooltipsHotspotIgnore(this, i)) {
1808
+ continue;
1809
+ }
1810
+
1811
+ var mouseX = mouseXY[0], // In relation to the canvas
1812
+ mouseY = mouseXY[1], // In relation to the canvas
1813
+ left = this.coords[i][0],
1814
+ top = this.coords[i][1],
1815
+ width = this.coords[i][2],
1816
+ height = this.coords[i][3],
1817
+ idx = i,
1818
+ indexes = RGraph.sequentialIndexToGrouped(i, this.data);
1819
+
1820
+ //
1821
+ // If the corners are rounded then change the rect function
1822
+ //
1823
+ if (properties.corners === 'round') {
1824
+
1825
+ var isLast = indexes[1] === 0;
1826
+
1827
+ if (properties.grouping === 'stacked' && isLast) {
1828
+ this.context.rectOld = this.context.rect;
1829
+ this.context.rect = this.roundedCornersRect;
1830
+
1831
+ var revert = true;
1832
+
1833
+ } else if (properties.grouping === 'stacked') {
1834
+ // Do nothing for the main body bits of
1835
+ // stacked bars
1836
+
1837
+ } else {
1838
+
1839
+ this.context.rectOld = this.context.rect;
1840
+ this.context.rect = this.roundedCornersRect;
1841
+
1842
+ var revert = true;
1843
+ }
1844
+ }
1845
+
1846
+ // Recreate the path/rectangle so that it can be tested
1847
+ // ** DO NOT STROKE OR FILL IT **
1848
+ if (properties.tooltipsHotspotYonly) {
1849
+ this.path(
1850
+ 'b r % % % % ',
1851
+ this.marginLeft, top, this.canvas.width - this.marginRight - this.marginLeft, height
1852
+ );
1853
+ } else {
1854
+ this.path(
1855
+ 'b r % % % %',
1856
+ left,top,width,height
1857
+ );
1858
+ }
1859
+
1860
+ //
1861
+ // Put the rect function back to what it was
1862
+ //
1863
+ if (revert) {
1864
+ this.context.rect = this.context.rectOld;
1865
+ this.context.rectOld = null;
1866
+ revert = null;
1867
+ }
1868
+
1869
+
1870
+
1871
+
1872
+
1873
+
1874
+
1875
+
1876
+
1877
+
1878
+ if (this.context.isPointInPath(mouseX, mouseY)) {
1879
+
1880
+ if (RGraph.parseTooltipText) {
1881
+ var tooltip = RGraph.parseTooltipText(properties.tooltips, i);
1882
+ }
1883
+
1884
+ var indexes = RGraph.sequentialIndexToGrouped(idx, this.data);
1885
+ var group = indexes[0];
1886
+ var index = indexes[1];
1887
+
1888
+ return {
1889
+ object: this,
1890
+ x: left,
1891
+ y: top,
1892
+ width: width,
1893
+ height: height,
1894
+ sequentialIndex: idx,
1895
+ dataset: group,
1896
+ index: index,
1897
+ label: properties.yaxisLabels && typeof properties.yaxisLabels[group] === 'string' ? properties.yaxisLabels[group] : null,
1898
+ tooltip: typeof tooltip === 'string' ? tooltip : null
1899
+ };
1900
+ }
1901
+ }
1902
+ };
1903
+
1904
+
1905
+
1906
+
1907
+
1908
+
1909
+
1910
+
1911
+ //
1912
+ // When you click on the chart, this method can return the X value at that point. It works for any point on the
1913
+ // chart (that is inside the margins) - not just points within the Bars.
1914
+ //
1915
+ // @param object e The event object
1916
+ //
1917
+ this.getValue = function (arg)
1918
+ {
1919
+ if (arg.length == 2) {
1920
+ var mouseX = arg[0];
1921
+ var mouseY = arg[1];
1922
+ } else {
1923
+ var mouseCoords = RGraph.getMouseXY(arg);
1924
+ var mouseX = mouseCoords[0];
1925
+ var mouseY = mouseCoords[1];
1926
+ }
1927
+
1928
+ if ( mouseY < this.marginTop
1929
+ || mouseY > (this.canvas.height - this.marginBottom)
1930
+ || mouseX < this.marginLeft
1931
+ || mouseX > (this.canvas.width - this.marginRight)
1932
+ ) {
1933
+ return null;
1934
+ }
1935
+
1936
+
1937
+
1938
+
1939
+
1940
+ if ( properties.yaxisPosition == 'center') {
1941
+ var value = ((mouseX - this.marginLeft) / (this.graphwidth / 2)) * (this.max - properties.xaxisScaleMin);
1942
+ value = value - this.max
1943
+
1944
+ // Special case if xmin is defined
1945
+ if ( properties.xaxisScaleMin > 0) {
1946
+ value = ((mouseX - this.marginLeft - (this.graphwidth / 2)) / (this.graphwidth / 2)) * (this.max - properties.xaxisScaleMin);
1947
+ value += properties.xaxisScaleMin;
1948
+
1949
+ if (mouseX < (this.marginLeft + (this.graphwidth / 2))) {
1950
+ value -= (2 * properties.xaxisScaleMin);
1951
+ }
1952
+ }
1953
+
1954
+
1955
+ // TODO This needs fixing
1956
+ } else if ( properties.yaxisPosition == 'right') {
1957
+ var value = ((mouseX - this.marginLeft) / this.graphwidth) * (this.scale2.max - properties.xaxisScaleMin);
1958
+ value = this.scale2.max - value;
1959
+
1960
+ } else {
1961
+ var value = ((mouseX - this.marginLeft) / this.graphwidth) * (this.scale2.max - properties.xaxisScaleMin);
1962
+ value += properties.xaxisScaleMin;
1963
+ }
1964
+
1965
+ return value;
1966
+ };
1967
+
1968
+
1969
+
1970
+
1971
+
1972
+
1973
+
1974
+
1975
+ //
1976
+ // Each object type has its own Highlight() function which highlights the appropriate shape
1977
+ //
1978
+ // @param object shape The shape to highlight
1979
+ //
1980
+ this.highlight = function (shape)
1981
+ {
1982
+ // highlightStyle is a function - user defined highlighting
1983
+ if (typeof properties.highlightStyle === 'function') {
1984
+ (properties.highlightStyle)(shape);
1985
+
1986
+ // Highlight all of the rects except this one - essentially an inverted highlight
1987
+ } else if (typeof properties.highlightStyle === 'string' && properties.highlightStyle === 'invert') {
1988
+ for (var i=0; i<this.coords.length; ++i) {
1989
+ if (i !== shape.sequentialIndex) {
1990
+ this.path(
1991
+ 'b r % % % % s % f %',
1992
+ this.coords[i][0] - 0.5, this.coords[i][1] - 0.5, this.coords[i][2] + 1, this.coords[i][3] + 1,
1993
+ properties.highlightStroke,
1994
+ properties.highlightFill
1995
+ );
1996
+ }
1997
+ }
1998
+
1999
+ // Standard higlight
2000
+ } else {
2001
+ RGraph.Highlight.rect(this, shape);
2002
+ }
2003
+ };
2004
+
2005
+
2006
+
2007
+
2008
+
2009
+
2010
+
2011
+
2012
+ //
2013
+ // The getObjectByXY() worker method. Don't call this call:
2014
+ //
2015
+ // RGraph.ObjectRegistry.getObjectByXY(e)
2016
+ //
2017
+ // @param object e The event object
2018
+ //
2019
+ this.getObjectByXY = function (e)
2020
+ {
2021
+ var mouseXY = RGraph.getMouseXY(e);
2022
+
2023
+ // Adjust the mouse Y coordinate for when the bar chart is
2024
+ // a 3D variant
2025
+ if (properties.variant === '3d' && !properties.textAccessible) {
2026
+ var adjustment = properties.variantThreedAngle * mouseXY[0];
2027
+ mouseXY[1] -= adjustment;
2028
+ }
2029
+
2030
+
2031
+ if (
2032
+ mouseXY[0] >= this.marginLeft
2033
+ && mouseXY[0] <= (this.canvas.width - this.marginRight)
2034
+ && mouseXY[1] >= this.marginTop
2035
+ && mouseXY[1] <= (this.canvas.height - this.marginBottom)
2036
+ ) {
2037
+
2038
+ return this;
2039
+ }
2040
+ };
2041
+
2042
+
2043
+
2044
+
2045
+
2046
+
2047
+
2048
+
2049
+ //
2050
+ // Returns the appropriate X coord for the given value
2051
+ //
2052
+ // @param number value The value to get the coord for
2053
+ //
2054
+ this.getXCoord = function (value)
2055
+ {
2056
+ if ( properties.yaxisPosition == 'center') {
2057
+
2058
+ // Range checking
2059
+ if (value > this.max || value < (-1 * this.max)) {
2060
+ return null;
2061
+ }
2062
+
2063
+ var width = (this.canvas.width - properties.marginLeft - properties.marginRight) / 2;
2064
+ var coord = (((value - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * width) + width;
2065
+
2066
+ coord = properties.marginLeft + coord;
2067
+ } else {
2068
+
2069
+ // Range checking
2070
+ if (value > this.max || value < 0) {
2071
+ return null;
2072
+ }
2073
+
2074
+ var width = this.canvas.width - properties.marginLeft - properties.marginRight;
2075
+ var coord = ((value - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * width;
2076
+
2077
+ coord = properties.marginLeft + coord;
2078
+ }
2079
+
2080
+ return coord;
2081
+ };
2082
+
2083
+
2084
+
2085
+
2086
+
2087
+
2088
+
2089
+
2090
+ //
2091
+ //
2092
+ //
2093
+ this.parseColors = function ()
2094
+ {
2095
+ // Save the original colors so that they can be restored when the canvas is reset
2096
+ if (this.original_colors.length === 0) {
2097
+ //this.original_colors['chart.'] = RGraph.arrayClone(properties.);
2098
+ this.original_colors.colors = RGraph.arrayClone(properties.colors);
2099
+ this.original_colors.backgroundGridColor = RGraph.arrayClone(properties.backgroundGridColor);
2100
+ this.original_colors.backgroundColor = RGraph.arrayClone(properties.backgroundColor);
2101
+ this.original_colors.backgroundBarsColor1 = RGraph.arrayClone(properties.backgroundBarsColor1);
2102
+ this.original_colors.backgroundBarsColor2 = RGraph.arrayClone(properties.backgroundBarsColor2);
2103
+ this.original_colors.textColor = RGraph.arrayClone(properties.textColor);
2104
+ this.original_colors.yaxisLabelsColor = RGraph.arrayClone(properties.yaxisLabelsColor);
2105
+ this.original_colors.colorsStroke = RGraph.arrayClone(properties.colorsStroke);
2106
+ this.original_colors.axesColor = RGraph.arrayClone(properties.axesColor);
2107
+ this.original_colors.highlightFill = RGraph.arrayClone(properties.highlightFill);
2108
+ this.original_colors.highlightStroke = RGraph.arrayClone(properties.highlightStroke);
2109
+ this.original_colors.annotatableColor = RGraph.arrayClone(properties.annotatableColor);
2110
+
2111
+ }
2112
+
2113
+ var colors = properties.colors;
2114
+
2115
+ for (var i=0; i<colors.length; ++i) {
2116
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
2117
+ }
2118
+
2119
+ properties.backgroundGridColor = this.parseSingleColorForGradient(properties.backgroundGridColor);
2120
+ properties.backgroundColor = this.parseSingleColorForGradient(properties.backgroundColor);
2121
+ properties.backgroundBarsColor1 = this.parseSingleColorForGradient(properties.backgroundBarsColor1);
2122
+ properties.backgroundBarsColor2 = this.parseSingleColorForGradient(properties.backgroundBarsColor2);
2123
+ properties.textColor = this.parseSingleColorForGradient(properties.textColor);
2124
+ properties.yaxisLabelsColor = this.parseSingleColorForGradient(properties.yaxisLabelsColor);
2125
+ properties.colorsStroke = this.parseSingleColorForGradient(properties.colorsStroke);
2126
+ properties.axesColor = this.parseSingleColorForGradient(properties.axesColor);
2127
+ properties.highlightFill = this.parseSingleColorForGradient(properties.highlightFill);
2128
+ properties.highlightStroke = this.parseSingleColorForGradient(properties.highlightStroke);
2129
+ properties.annotatableColor = this.parseSingleColorForGradient(properties.annotatableColor);
2130
+ };
2131
+
2132
+
2133
+
2134
+
2135
+
2136
+
2137
+
2138
+
2139
+ //
2140
+ // Use this function to reset the object to the post-constructor state. Eg reset colors if
2141
+ // need be etc
2142
+ //
2143
+ this.reset = function ()
2144
+ {
2145
+ };
2146
+
2147
+
2148
+
2149
+
2150
+
2151
+
2152
+
2153
+ //
2154
+ // This parses a single color value
2155
+ //
2156
+ this.parseSingleColorForGradient = function (color)
2157
+ {
2158
+ if (!color || typeof color != 'string') {
2159
+ return color;
2160
+ }
2161
+
2162
+ if (color.match(/^gradient\((.*)\)$/i)) {
2163
+
2164
+ // Allow for JSON gradients
2165
+ if (color.match(/^gradient\(({.*})\)$/i)) {
2166
+ return RGraph.parseJSONGradient({object: this, def: RegExp.$1});
2167
+ }
2168
+
2169
+ var parts = RegExp.$1.split(':');
2170
+
2171
+ if ( properties.yaxisPosition === 'right') {
2172
+ parts = RGraph.arrayReverse(parts);
2173
+ }
2174
+
2175
+ // Create the gradient
2176
+ var grad = this.context.createLinearGradient(properties.marginLeft,0,this.canvas.width - properties.marginRight,0);
2177
+
2178
+ var diff = 1 / (parts.length - 1);
2179
+
2180
+ grad.addColorStop(0, RGraph.trim(parts[0]));
2181
+
2182
+ for (var j=1; j<parts.length; ++j) {
2183
+ grad.addColorStop(j * diff, RGraph.trim(parts[j]));
2184
+ }
2185
+ }
2186
+
2187
+ return grad ? grad : color;
2188
+ };
2189
+
2190
+
2191
+
2192
+
2193
+
2194
+
2195
+
2196
+
2197
+ //
2198
+ // This function handles highlighting an entire data-series for the interactive
2199
+ // key
2200
+ //
2201
+ // @param int index The index of the data series to be highlighted
2202
+ //
2203
+ this.interactiveKeyHighlight = function (index)
2204
+ {
2205
+ var obj = this;
2206
+
2207
+ this.coords2.forEach(function (value, idx, arr)
2208
+ {
2209
+ var coords = obj.coords2[idx][index],
2210
+ pre_linewidth = obj.context.lineWidth;
2211
+
2212
+ obj.path(
2213
+ 'b lw 2 r % % % % f % s % lw %',
2214
+ coords[0]- 0.5, coords[1] - 0.5, coords[2] + 1, coords[3] + 1,
2215
+ properties.keyInteractiveHighlightChartFill,
2216
+ properties.keyInteractiveHighlightChartStroke,
2217
+ pre_linewidth
2218
+ );
2219
+ });
2220
+ };
2221
+
2222
+
2223
+
2224
+
2225
+
2226
+
2227
+
2228
+
2229
+ //
2230
+ // Using a function to add events makes it easier to facilitate method chaining
2231
+ //
2232
+ // @param string type The type of even to add
2233
+ // @param function func
2234
+ //
2235
+ this.on = function (type, func)
2236
+ {
2237
+ if (type.substr(0,2) !== 'on') {
2238
+ type = 'on' + type;
2239
+ }
2240
+
2241
+ if (typeof this[type] !== 'function') {
2242
+ this[type] = func;
2243
+ } else {
2244
+ RGraph.addCustomEventListener(this, type, func);
2245
+ }
2246
+
2247
+ return this;
2248
+ };
2249
+
2250
+
2251
+
2252
+
2253
+
2254
+
2255
+
2256
+
2257
+ //
2258
+ // This function runs once only
2259
+ // (put at the end of the file (before any effects))
2260
+ //
2261
+ this.firstDrawFunc = function ()
2262
+ {
2263
+ };
2264
+
2265
+
2266
+
2267
+
2268
+
2269
+
2270
+
2271
+
2272
+ //
2273
+ // This retrives the bar based on the X coordinate only.
2274
+ //
2275
+ // @param object e The event object
2276
+ // @param object OPTIONAL You can pass in the bar object instead of the
2277
+ // function using "this"
2278
+ //
2279
+ this.getShapeByY = function (e)
2280
+ {
2281
+ var mouseXY = RGraph.getMouseXY(e);
2282
+
2283
+
2284
+ // This facilitates you being able to pass in the bar object as a parameter instead of
2285
+ // the function getting it from itself
2286
+ var obj = arguments[1] ? arguments[1] : this;
2287
+
2288
+
2289
+ //
2290
+ // Loop through the bars determining if the mouse is over a bar
2291
+ //
2292
+ for (var i=0,len=obj.coords.length; i<len; i++) {
2293
+
2294
+ if (obj.coords[i].length == 0) {
2295
+ continue;
2296
+ }
2297
+
2298
+ var mouseX = mouseXY[0],
2299
+ mouseY = mouseXY[1],
2300
+ left = obj.coords[i][0],
2301
+ top = obj.coords[i][1],
2302
+ width = obj.coords[i][2],
2303
+ height = obj.coords[i][3];
2304
+
2305
+ if (mouseY >= top && mouseY <= (top + height)) {
2306
+
2307
+ var indexes = RGraph.sequentialIndexToGrouped(i, this.data);
2308
+ var group = indexes[0];
2309
+ var index = indexes[1];
2310
+
2311
+ if (properties.tooltips) {
2312
+ var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(properties.tooltips, i) : properties.tooltips[i];
2313
+ }
2314
+
2315
+ return {
2316
+ object: obj,
2317
+ x: left,
2318
+ y: top,
2319
+ width: width,
2320
+ height: height,
2321
+ dataset: group,
2322
+ index: index,
2323
+ sequentialIndex: i,
2324
+ tooltip: tooltip || null
2325
+ };
2326
+ }
2327
+ }
2328
+
2329
+ return null;
2330
+ };
2331
+
2332
+
2333
+
2334
+
2335
+
2336
+
2337
+
2338
+
2339
+ //
2340
+ // This method handles the adjusting calculation for when the mouse is moved
2341
+ //
2342
+ // @param object e The event object
2343
+ //
2344
+ this.adjusting_mousemove = function (e)
2345
+ {
2346
+ //
2347
+ // Handle adjusting for the Bar
2348
+ //
2349
+ if (properties.adjustable && RGraph.Registry.get('adjusting') && RGraph.Registry.get('adjusting').uid == this.uid) {
2350
+
2351
+ // Rounding the value to the given number of decimals make the chart step
2352
+ var value = Number(this.getValue(e)),
2353
+ shape = RGraph.Registry.get('adjusting.shape');
2354
+
2355
+ if (shape) {
2356
+
2357
+ RGraph.Registry.set('adjusting.shape', shape);
2358
+
2359
+ if (this.stackedOrGrouped && properties.grouping == 'grouped') {
2360
+
2361
+ var indexes = RGraph.sequentialIndexToGrouped(shape.sequentialIndex, this.data);
2362
+
2363
+ if (typeof this.data[indexes[0]] == 'number') {
2364
+ this.data[indexes[0]] = Number(value);
2365
+ } else if (!RGraph.isNull(this.data[indexes[0]])) {
2366
+ this.data[indexes[0]][indexes[1]] = Number(value);
2367
+ }
2368
+ } else if (typeof this.data[shape.dataset] == 'number') {
2369
+
2370
+ this.data[shape.dataset] = Number(value);
2371
+ }
2372
+
2373
+ RGraph.redrawCanvas(e.target);
2374
+ RGraph.fireCustomEvent(this, 'onadjust');
2375
+ }
2376
+ }
2377
+ };
2378
+
2379
+
2380
+
2381
+
2382
+
2383
+
2384
+
2385
+
2386
+ //
2387
+ // Draws the labelsInbar
2388
+ //
2389
+ this.drawLabelsInbar = function ()
2390
+ {
2391
+ // Go through the above labels
2392
+ if (properties.labelsInbar) {
2393
+
2394
+ // Default alignment
2395
+ var valign = properties.labelsInbarValign,
2396
+ halign = properties.labelsInbarHalign;
2397
+
2398
+ // Get the text configuration for the labels
2399
+ var textConf = RGraph.getTextConf({
2400
+ object: this,
2401
+ prefix: 'labelsInbar'
2402
+ });
2403
+
2404
+ // Linearize the data using a custom function because the coords are
2405
+ // stored in the wrong order
2406
+ var linearize = function (data)
2407
+ {
2408
+ var ret = [];
2409
+
2410
+ for (var i=0; i<data.length; ++i) {
2411
+ if (typeof data[i] === 'number') {
2412
+ ret.push(data[i]);
2413
+ } else if (typeof data[i] === 'object' && !RGraph.isNull(data[i])) {
2414
+ for (var j=data[i].length-1; j>=0; j--) {
2415
+ ret.push(data[i][j]);
2416
+ }
2417
+ }
2418
+ }
2419
+
2420
+ return ret;
2421
+ };
2422
+
2423
+ // Linearize the data using the custom linearize function if stacked,
2424
+ // if not stacked use the API function
2425
+ if (properties.grouping === 'stacked') {
2426
+ data = linearize(this.data);
2427
+ } else {
2428
+ data = RGraph.arrayLinearize(this.data);
2429
+ }
2430
+
2431
+ for (var i=0; i<data.length; ++i) {
2432
+ if (RGraph.isNumber(data[i]) && !isNaN(data[i]) && this.coords[i][2] > 0) {
2433
+
2434
+ var value = data[i].toFixed(properties.labelsInbarDecimals);
2435
+ var indexes = RGraph.sequentialIndexToGrouped(i, this.data);
2436
+
2437
+ var str = RGraph.numberFormat({
2438
+ object: this,
2439
+ number: Number(value).toFixed(properties.labelsInbarDecimals),
2440
+ unitspre: properties.labelsInbarUnitsPre,
2441
+ unitspost: properties.labelsInbarUnitsPost,
2442
+ point: properties.labelsInbarPoint,
2443
+ thousand: properties.labelsInbarThousand,
2444
+ formatter: properties.labelsInbarFormatter
2445
+ });
2446
+
2447
+ var dimensions = RGraph.measureText({
2448
+ text: str,
2449
+ bold: textConf.bold,
2450
+ font: textConf.font,
2451
+ size: textConf.size
2452
+ });
2453
+
2454
+ var x = this.coords[i][0] + (this.coords[i][2] / 2) + properties.labelsInbarOffsetx,
2455
+ y = this.coords[i][1] + (this.coords[i][3] / 2) + properties.labelsInbarOffsety,
2456
+ width = dimensions[0],
2457
+ height = dimensions[1];
2458
+
2459
+ //
2460
+ // Horizontal alignment
2461
+ //
2462
+ if (properties.labelsInbarHalign === 'left') {
2463
+ halign = 'left';
2464
+ x = this.coords[i][0] + 5 + properties.labelsInbarOffsetx;
2465
+ } else if (properties.labelsInbarHalign === 'right') {
2466
+ halign = 'right';
2467
+ x = this.coords[i][0] + this.coords[i][2] - 5 + properties.labelsInbarOffsetx;
2468
+ }
2469
+
2470
+ //
2471
+ // Vertical alignment
2472
+ //
2473
+ if (properties.labelsInbarValign === 'bottom') {
2474
+ valign = 'bottom';
2475
+ y = this.coords[i][1] - 5 + this.coords[i][3] + properties.labelsInbarOffsety;
2476
+ } else if (properties.labelsInbarValign === 'top') {
2477
+ valign = 'top';
2478
+ y = this.coords[i][1] + 5 + properties.labelsInbarOffsety;
2479
+ }
2480
+
2481
+
2482
+
2483
+
2484
+ // Specific label given
2485
+ if (RGraph.isArray(properties.labelsInbarSpecific) && (RGraph.isString(properties.labelsInbarSpecific[i]) || RGraph.isNumber(properties.labelsInbarSpecific[i]))) {
2486
+ str = properties.labelsInbarSpecific[i];
2487
+ }
2488
+
2489
+ RGraph.text({
2490
+ object: this,
2491
+ font: textConf.font,
2492
+ size: textConf.size,
2493
+ color: textConf.color,
2494
+ bold: textConf.bold,
2495
+ italic: textConf.italic,
2496
+ x: x,
2497
+ y: y,
2498
+ text: str,
2499
+ valign: valign,
2500
+ halign: halign,
2501
+ tag: 'labels.above',
2502
+ bounding: RGraph.isString(properties.labelsInbarBackground),
2503
+ boundingFill: properties.labelsInbarBackground,
2504
+ boundingStroke: 'transparent'
2505
+ });
2506
+ }
2507
+ }
2508
+ }
2509
+ };
2510
+
2511
+
2512
+
2513
+
2514
+
2515
+
2516
+
2517
+
2518
+ //
2519
+ // Grow
2520
+ //
2521
+ // The HBar chart Grow effect gradually increases the values of the bars
2522
+ //
2523
+ // @param object OPTIONAL Options for the effect. You can pass frames here
2524
+ // @param function OPTIONAL A callback function
2525
+ //
2526
+ //this.grow = function ()
2527
+ //{
2528
+ // var obj = this,
2529
+ // opt = arguments[0] || {},
2530
+ // frames = opt.frames || 30,
2531
+ // frame = 0,
2532
+ // callback = arguments[1] || function () {},
2533
+ // labelsAbove = properties.labelsAbove;
2534
+ //
2535
+ // this.set('labelsAbove', false);
2536
+
2537
+
2538
+ // // Save the data
2539
+ // obj.original_data = RGraph.arrayClone(obj.data);
2540
+
2541
+
2542
+ // // Stop the scale from changing by setting xaxisScaleMax (if it's not already set)
2543
+
2544
+ // if ( properties.xaxisScaleMax == 0) {
2545
+
2546
+ // var xmax = 0;
2547
+
2548
+ // for (var i=0; i<obj.data.length; ++i) {
2549
+ // if (RGraph.isArray(obj.data[i]) && properties.grouping == 'stacked') {
2550
+ // xmax = Math.max(xmax, RGraph.arraySum(obj.data[i]));
2551
+ // } else if (RGraph.isArray(obj.data[i]) && properties.grouping == 'grouped') {
2552
+ // xmax = Math.max(xmax, RGraph.arrayMax(obj.data[i]));
2553
+ // } else {
2554
+ // xmax = Math.max(xmax, Math.abs(RGraph.arrayMax(obj.data[i])));
2555
+ // }
2556
+ // }
2557
+
2558
+ // var scale2 = RGraph.getScale({object: obj, options: {'scale.max': xmax,'scale.round': obj.properties.xaxisScaleRound}});
2559
+ // obj.set('xaxisScaleMax', scale2.max);
2560
+ // }
2561
+
2562
+ // function iterator ()
2563
+ // {
2564
+ // // Alter the Bar chart data depending on the frame
2565
+ // for (var j=0,len=obj.original_data.length; j<len; ++j) {
2566
+ //
2567
+ // // This stops the animation from being completely linear
2568
+ // var easingFactor = RGraph.Effects.getEasingMultiplier(frames, frame);
2569
+
2570
+ // if (typeof obj.data[j] === 'object' && obj.data[j]) {
2571
+ // for (var k=0,len2=obj.data[j].length; k<len2; ++k) {
2572
+ // obj.data[j][k] = RGraph.isNull(obj.data[j][k]) ? null : obj.original_data[j][k] * easingFactor;
2573
+ // }
2574
+ // } else {
2575
+ // obj.data[j] = RGraph.isNull(obj.data[j]) ? null : obj.original_data[j] * easingFactor;
2576
+ // }
2577
+ // }
2578
+
2579
+
2580
+
2581
+ // RGraph.redrawCanvas(obj.canvas);
2582
+
2583
+ // if (frame < frames) {
2584
+ // frame += 1;
2585
+ // RGraph.Effects.updateCanvas(iterator);
2586
+ // } else {
2587
+
2588
+ // if (labelsAbove) {
2589
+ // obj.set('labelsAbove', true);
2590
+ // RGraph.redraw();
2591
+ // }
2592
+
2593
+ // callback(obj);
2594
+ // }
2595
+ // }
2596
+
2597
+ // iterator();
2598
+
2599
+ // return this;
2600
+ //};
2601
+
2602
+
2603
+
2604
+
2605
+
2606
+
2607
+
2608
+
2609
+ //
2610
+ // Grow
2611
+ //
2612
+ // The HBar chart Grow effect gradually increases the values of the bars
2613
+ //
2614
+ // @param object An object of options - eg: {frames: 30}
2615
+ // @param function A function to call when the effect is complete
2616
+ //
2617
+ this.grow = function ()
2618
+ {
2619
+ // Cancel any stop request if one is pending
2620
+ this.cancelStopAnimation();
2621
+
2622
+ // Callback
2623
+ var opt = arguments[0] || {},
2624
+ frames = opt.frames || 120,
2625
+ frame = 0,
2626
+ callback = arguments[1] || function () {},
2627
+ obj = this,
2628
+ labelsAbove = this.get('labelsAbove')
2629
+
2630
+
2631
+
2632
+
2633
+ this.original_data = RGraph.arrayClone(this.data);
2634
+
2635
+
2636
+
2637
+ // Stop the scale from changing by setting xaxisScalemax (if it's not already set)
2638
+ if ( properties.xaxisScaleMax == 0) {
2639
+
2640
+ var xmax = 0;
2641
+
2642
+ for (var i=0; i<obj.data.length; ++i) {
2643
+ if (RGraph.isArray(obj.data[i]) && properties.grouping == 'stacked') {
2644
+ xmax = Math.max(xmax, RGraph.arraySum(obj.data[i]));
2645
+ } else if (RGraph.isArray(obj.data[i]) && properties.grouping == 'grouped') {
2646
+ xmax = Math.max(xmax, RGraph.arrayMax(obj.data[i]));
2647
+ } else {
2648
+ xmax = Math.max(xmax, Math.abs(RGraph.arrayMax(obj.data[i])));
2649
+ }
2650
+ }
2651
+
2652
+ var scale2 = RGraph.getScale({object: obj, options: {'scale.max':xmax, 'scale.round': obj.properties.xaxisScaleRound}});
2653
+ obj.set('xaxisScaleMax', scale2.max);
2654
+ }
2655
+
2656
+
2657
+ // Go through the data and change string arguments of
2658
+ // the format +/-[0-9] to absolute numbers
2659
+ if (RGraph.isArray(opt.data)) {
2660
+
2661
+ var xmax = 0;
2662
+
2663
+ for (var i=0; i<opt.data.length; ++i) {
2664
+ if (typeof opt.data[i] === 'object') {
2665
+ for (var j=0; j<opt.data[i].length; ++j) {
2666
+ if (typeof opt.data[i][j] === 'string'&& opt.data[i][j].match(/(\+|\-)([0-9]+)/)) {
2667
+ if (RegExp.$1 === '+') {
2668
+ opt.data[i][j] = this.original_data[i][j] + parseInt(RegExp.$2);
2669
+ } else {
2670
+ opt.data[i][j] = this.original_data[i][j] - parseInt(RegExp.$2);
2671
+ }
2672
+ }
2673
+
2674
+ xmax = Math.max(xmax, opt.data[i][j]);
2675
+ }
2676
+ } else if (typeof opt.data[i] === 'string' && opt.data[i].match(/(\+|\-)([0-9]+)/)) {
2677
+ if (RegExp.$1 === '+') {
2678
+ opt.data[i] = this.original_data[i] + parseFloat(RegExp.$2);
2679
+ } else {
2680
+ opt.data[i] = this.original_data[i] - parseFloat(RegExp.$2);
2681
+ }
2682
+
2683
+ xmax = Math.max(xmax, opt.data[i]);
2684
+ } else {
2685
+ xmax = Math.max(xmax, opt.data[i]);
2686
+ }
2687
+ }
2688
+
2689
+
2690
+ var scale = RGraph.getScale({object: this, options: {'scale.max':xmax}});
2691
+ if (RGraph.isNull(this.get('xaxisScaleMax'))) {
2692
+ this.set('xaxisScaleMax', scale.max);
2693
+ }
2694
+ }
2695
+
2696
+
2697
+
2698
+
2699
+
2700
+
2701
+
2702
+
2703
+ //
2704
+ // turn off the labelsAbove option whilst animating
2705
+ //
2706
+ this.set('labelsAbove', false);
2707
+
2708
+
2709
+
2710
+
2711
+
2712
+
2713
+
2714
+
2715
+ // Stop the scale from changing by setting xaxisScaleMax (if it's not already set)
2716
+ if (RGraph.isNull( properties.xaxisScaleMax)) {
2717
+
2718
+ var xmax = 0;
2719
+
2720
+ for (var i=0; i<obj.data.length; ++i) {
2721
+ if (RGraph.isArray(this.data[i]) && properties.grouping === 'stacked') {
2722
+ xmax = Math.max(xmax, Math.abs(RGraph.arraySum(this.data[i])));
2723
+
2724
+ } else if (RGraph.isArray(this.data[i]) && properties.grouping === 'grouped') {
2725
+
2726
+ for (var j=0,group=[]; j<this.data[i].length; j++) {
2727
+ group.push(Math.abs(this.data[i][j]));
2728
+ }
2729
+
2730
+ xmax = Math.max(xmax, Math.abs(RGraph.arrayMax(group)));
2731
+
2732
+ } else {
2733
+ xmax = Math.max(xmax, Math.abs(this.data[i]));
2734
+ }
2735
+ }
2736
+
2737
+ var scale = RGraph.getScale({object: this, options: {'scale.max':xmax}});
2738
+ this.set('xaxisScaleMax', scale.max);
2739
+ }
2740
+
2741
+ // You can give an xmax to the grow function
2742
+ if (typeof opt.xmax === 'number') {
2743
+ obj.set('xaxisScaleMax', opt.xmax);
2744
+ }
2745
+
2746
+ //
2747
+ // Need a copy of the original border radiuses
2748
+ //
2749
+ if (typeof obj.properties.cornersRoundTopRadius === 'number') {orig_cornersRoundTopRadius = obj.properties.cornersRoundTopRadius;}
2750
+ if (typeof obj.properties.cornersRoundBottomRadius === 'number') {orig_cornersRoundBottomRadius = obj.properties.cornersRoundBottomRadius;}
2751
+
2752
+
2753
+
2754
+ var iterator = function ()
2755
+ {
2756
+ if (obj.stopAnimationRequested) {
2757
+
2758
+ // Reset the flag
2759
+ obj.stopAnimationRequested = false;
2760
+
2761
+ return;
2762
+ }
2763
+
2764
+
2765
+
2766
+ var easingMultiplier = RGraph.Effects.getEasingMultiplier(frames, frame);
2767
+
2768
+ if (typeof obj.properties.cornersRoundTopRadius === 'number') {
2769
+ obj.properties.cornersRoundTopRadius = easingMultiplier * orig_cornersRoundTopRadius;
2770
+ }
2771
+
2772
+ if (typeof obj.properties.cornersRoundBottomRadius === 'number') {
2773
+ obj.properties.cornersRoundBottomRadius = easingMultiplier * orig_cornersRoundBottomRadius;
2774
+ }
2775
+
2776
+ // Alter the Bar chart data depending on the frame
2777
+ for (var j=0,len=obj.original_data.length; j<len; ++j) {
2778
+
2779
+ if (typeof obj.data[j] === 'object' && !RGraph.isNull(obj.data[j])) {
2780
+ for (var k=0,len2=obj.data[j].length; k<len2; ++k) {
2781
+ if (obj.firstDraw || !opt.data) {
2782
+ if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) {
2783
+ obj.data[j][k] = (easingMultiplier * (obj.original_data[j][k] - properties.xaxisScaleMin)) + properties.xaxisScaleMin;
2784
+ } else {
2785
+ obj.data[j][k] = easingMultiplier * obj.original_data[j][k];
2786
+ }
2787
+
2788
+ } else if (opt.data && opt.data.length === obj.original_data.length) {
2789
+ var diff = opt.data[j][k] - obj.original_data[j][k];
2790
+ obj.data[j][k] = (easingMultiplier * diff) + obj.original_data[j][k];
2791
+ }
2792
+ }
2793
+ } else {
2794
+
2795
+ if (obj.firstDraw || !opt.data) {
2796
+ if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) {
2797
+ obj.data[j] = (easingMultiplier * (obj.original_data[j] - properties.xaxisScaleMin)) + properties.xaxisScaleMin;
2798
+ } else {
2799
+ obj.data[j] = easingMultiplier * obj.original_data[j];
2800
+ }
2801
+
2802
+ } else if (opt.data && opt.data.length === obj.original_data.length) {
2803
+ var diff = opt.data[j] - obj.original_data[j];
2804
+ obj.data[j] = (easingMultiplier * diff) + obj.original_data[j];
2805
+ }
2806
+ }
2807
+ }
2808
+
2809
+
2810
+
2811
+
2812
+ //RGraph.clear(obj.canvas);
2813
+ RGraph.redrawCanvas(obj.canvas);
2814
+
2815
+
2816
+
2817
+
2818
+ if (frame < frames) {
2819
+ frame += 1;
2820
+
2821
+ RGraph.Effects.updateCanvas(iterator);
2822
+
2823
+ // Call the callback function
2824
+ } else {
2825
+
2826
+
2827
+
2828
+
2829
+
2830
+ // Do some housekeeping if new data was specified thats done in
2831
+ // the constructor - but needs to be redone because new data
2832
+ // has been specified
2833
+ if (RGraph.isArray(opt.data)) {
2834
+
2835
+ var linear_data = RGraph.arrayLinearize(data);
2836
+
2837
+ for (var i=0; i<linear_data.length; ++i) {
2838
+ if (!obj['$' + i]) {
2839
+ obj['$' + i] = {};
2840
+ }
2841
+ }
2842
+ }
2843
+
2844
+
2845
+
2846
+ obj.data = data;
2847
+ obj.original_data = RGraph.arrayClone(data);
2848
+
2849
+
2850
+
2851
+
2852
+
2853
+ if (labelsAbove) {
2854
+ obj.set('labelsAbove', true);
2855
+ RGraph.redraw();
2856
+ }
2857
+ callback(obj);
2858
+ }
2859
+ };
2860
+
2861
+ iterator();
2862
+
2863
+ return this;
2864
+ };
2865
+
2866
+
2867
+
2868
+
2869
+
2870
+
2871
+
2872
+
2873
+ //
2874
+ // (new) Bar chart Wave effect. This is a rewrite that should be smoother
2875
+ // because it just uses a single loop and not setTimeout
2876
+ //
2877
+ // @param object OPTIONAL An object map of options. You specify 'frames' here to give the number of frames in the effect
2878
+ // @param function OPTIONAL A function that will be called when the effect is complete
2879
+ //
2880
+ this.wave = function ()
2881
+ {
2882
+ // Cancel any stop request if one is pending
2883
+ this.cancelStopAnimation();
2884
+
2885
+ var obj = this,
2886
+ opt = arguments[0] || {};
2887
+ opt.frames = opt.frames || 120;
2888
+ opt.startFrames = [];
2889
+ opt.counters = [];
2890
+
2891
+ var framesperbar = opt.frames / 3,
2892
+ frame = -1,
2893
+ callback = arguments[1] || function () {},
2894
+ original = RGraph.arrayClone(obj.data),
2895
+ labelsAbove = properties.labelsAbove;
2896
+
2897
+ this.isWave = true;
2898
+
2899
+ //
2900
+ // If corners are set to be rounded disable them
2901
+ // whilst growing the bars
2902
+ //
2903
+ var orig_cornersRoundRadius = this.properties.cornersRoundRadius;
2904
+ var orig_cornersRoundTopRadius = this.properties.cornersRoundTopRadius;
2905
+ var orig_cornersRoundBottomRadius = this.properties.cornersRoundBottomRadius;
2906
+
2907
+ this.properties.cornersRoundRadius = null;
2908
+ this.properties.cornersRoundTopRadius = null;
2909
+ this.properties.cornersRoundBottomRadius = null;
2910
+
2911
+ this.set('labelsAbove', false);
2912
+
2913
+ for (var i=0,len=obj.data.length; i<len; i+=1) {
2914
+ opt.startFrames[i] = ((opt.frames / 2) / (obj.data.length - 1)) * i;
2915
+
2916
+ if (typeof obj.data[i] === 'object' && obj.data[i]) {
2917
+ opt.counters[i] = [];
2918
+ for (var j=0; j<obj.data[i].length; j++) {
2919
+ opt.counters[i][j] = 0;
2920
+ }
2921
+ } else {
2922
+ opt.counters[i] = 0;
2923
+ }
2924
+ }
2925
+
2926
+ //
2927
+ // This stops the chart from jumping
2928
+ //
2929
+ obj.draw();
2930
+ obj.set('xaxisScaleMax', obj.scale2.max);
2931
+ RGraph.clear(obj.canvas);
2932
+
2933
+
2934
+ function iterator ()
2935
+ {
2936
+ if (obj.stopAnimationRequested) {
2937
+
2938
+ // Reset the flag
2939
+ obj.stopAnimationRequested = false;
2940
+
2941
+ return;
2942
+ }
2943
+
2944
+ ++frame;
2945
+
2946
+ for (var i=0,len=obj.data.length; i<len; i+=1) {
2947
+ if (frame > opt.startFrames[i]) {
2948
+ if (typeof obj.data[i] === 'number') {
2949
+
2950
+ obj.data[i] = Math.min(Math.abs(original[i]),Math.abs(original[i] * (opt.counters[i] / framesperbar)));
2951
+ opt.counters[i] += 1;
2952
+
2953
+
2954
+ // Make the number negative if the original was
2955
+ if (original[i] < 0) {
2956
+ obj.data[i] *= -1;
2957
+ }
2958
+ } else if (!RGraph.isNull(obj.data[i])) {
2959
+ for (var j=0,len2=obj.data[i].length; j<len2; j+=1) {
2960
+
2961
+ if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) {
2962
+ obj.data[i][j] = Math.min(
2963
+ Math.abs(original[i][j]),
2964
+ Math.abs(((original[i][j] - properties.xaxisScaleMin) * ( opt.counters[i][j] / framesperbar)) + properties.xaxisScaleMin)
2965
+ );
2966
+ opt.counters[i][j] += 1;
2967
+ } else {
2968
+ obj.data[i][j] = Math.min(
2969
+ Math.abs(original[i][j]),
2970
+ Math.abs(original[i][j] * (opt.counters[i][j] / framesperbar))
2971
+ );
2972
+
2973
+ // Having this here seems to skirt a
2974
+ // minification bug
2975
+ opt.counters[i][j] += 1;
2976
+ }
2977
+
2978
+ // Make the number negative if the original was
2979
+ if (original[i][j] < 0) {
2980
+ obj.data[i][j] *= -1;
2981
+ }
2982
+ }
2983
+ }
2984
+ } else {
2985
+ obj.data[i] = typeof obj.data[i] === 'object' && obj.data[i] ? RGraph.arrayPad([], obj.data[i].length, (properties.xaxisScaleMin > 0 ? properties.xaxisScaleMin : 0)) : (RGraph.isNull(obj.data[i]) ? null : (properties.xaxisScaleMin > 0 ? properties.xaxisScaleMin : 0));
2986
+ }
2987
+ }
2988
+
2989
+
2990
+ if (frame >= opt.frames) {
2991
+
2992
+ if (labelsAbove) {
2993
+ obj.set('labelsAbove', true);
2994
+ RGraph.redrawCanvas(obj.canvas);
2995
+ }
2996
+
2997
+ //
2998
+ // Animate the corners to their desired amount
2999
+ // if it has been requested
3000
+ //
3001
+ if (
3002
+ typeof orig_cornersRoundRadius === 'number'
3003
+ || typeof orig_cornersRoundTopRadius === 'number'
3004
+ || typeof orig_cornersRoundBottomRadius === 'number'
3005
+ ) {
3006
+
3007
+ obj.animate({
3008
+ frames: 90,
3009
+ cornersRoundRadius: orig_cornersRoundRadius,
3010
+ cornersRoundTopRadius: orig_cornersRoundTopRadius,
3011
+ cornersRoundBottomRadius: orig_cornersRoundBottomRadius
3012
+ });
3013
+ }
3014
+
3015
+ this.isWave = null;
3016
+
3017
+ callback(obj);
3018
+ } else {
3019
+
3020
+ RGraph.redrawCanvas(obj.canvas);
3021
+ RGraph.Effects.updateCanvas(iterator);
3022
+ }
3023
+ }
3024
+
3025
+ iterator();
3026
+
3027
+ return this;
3028
+ };
3029
+
3030
+
3031
+
3032
+
3033
+
3034
+
3035
+
3036
+
3037
+ //
3038
+ // Couple of functions that allow you to control the
3039
+ // animation effect
3040
+ //
3041
+ this.stopAnimation = function ()
3042
+ {
3043
+ this.stopAnimationRequested = true;
3044
+ };
3045
+
3046
+ this.cancelStopAnimation = function ()
3047
+ {
3048
+ this.stopAnimationRequested = false;
3049
+ };
3050
+
3051
+
3052
+
3053
+
3054
+
3055
+
3056
+
3057
+
3058
+ //
3059
+ // Determines whether the given shape is adjustable or not
3060
+ //
3061
+ // @param object The shape that pertains to the relevant bar
3062
+ //
3063
+ this.isAdjustable = function (shape)
3064
+ {
3065
+ if (RGraph.isNull(properties.adjustableOnly)) {
3066
+ return true;
3067
+ }
3068
+
3069
+ if (RGraph.isArray(properties.adjustableOnly) && properties.adjustableOnly[shape.sequentialIndex]) {
3070
+ return true;
3071
+ }
3072
+
3073
+ return false;
3074
+ };
3075
+
3076
+
3077
+
3078
+
3079
+
3080
+
3081
+
3082
+
3083
+ //
3084
+ // This adds a roundedRect(x, y, width, height, radius) function to the drawing context.
3085
+ // The radius argument dictates by how much the corners are rounded.
3086
+ //
3087
+ // @param number x The X coordinate
3088
+ // @param number y The Y coordinate
3089
+ // @param number width The width of the rectangle
3090
+ // @param number height The height of the rectangle
3091
+ // @param number radius The radius of the corners. Bigger values mean more rounded corners
3092
+ //
3093
+ this.roundedCornersRect = function (x, y, width, height)
3094
+ {
3095
+ var radiusTop = null;
3096
+ var radiusBottom = null;
3097
+
3098
+ // Top radius
3099
+ if (RGraph.isNumber(properties.cornersRoundTopRadius)) {
3100
+ radiusTop = properties.cornersRoundTopRadius;
3101
+ } else {
3102
+ radiusTop = Math.min(width / 2, height / 2, properties.cornersRoundRadius);
3103
+ }
3104
+
3105
+ // Bottom radius
3106
+ if (RGraph.isNumber(properties.cornersRoundBottomRadius)) {
3107
+ radiusBottom = properties.cornersRoundBottomRadius;
3108
+ } else {
3109
+ radiusBottom = Math.min(width / 2, height / 2, properties.cornersRoundRadius);
3110
+ }
3111
+
3112
+
3113
+
3114
+
3115
+
3116
+ if ( (radiusTop + radiusBottom) > height) {
3117
+
3118
+ // Calculate the top and bottom radiii and assign
3119
+ // to temporary variables
3120
+ var a = height / (radiusTop + radiusBottom) * radiusTop;
3121
+ var b = height / (radiusTop + radiusBottom) * radiusBottom;
3122
+
3123
+ // Reassign the values to the correct variables
3124
+ radiusTop = a;
3125
+ radiusBottom = b;
3126
+ }
3127
+
3128
+
3129
+ // Because the function is added to the context prototype
3130
+ // the 'this' variable is actually the context
3131
+
3132
+ // Save the existing state of the canvas so that it can be restored later
3133
+ this.save();
3134
+
3135
+ // Translate to the given X/Y coordinates
3136
+ this.translate(x, y);
3137
+
3138
+ // Move to the center of the top horizontal line
3139
+ this.moveTo(width - radiusTop,0);
3140
+
3141
+ // Draw the rounded corners. The connecting lines in between them are drawn automatically
3142
+ this.arcTo(width,0, width,0 + radiusTop, properties.cornersRoundTop ? radiusTop : 0);
3143
+ this.arcTo(width, height, width - radiusBottom, height, properties.cornersRoundBottom ? radiusBottom : 0);
3144
+ this.lineTo(0,height);
3145
+ this.lineTo(0, 0);
3146
+
3147
+
3148
+ // Draw a line back to the start coordinates
3149
+ this.closePath();
3150
+
3151
+ // Restore the state of the canvas to as it was before the save()
3152
+ this.restore();
3153
+ };
3154
+
3155
+
3156
+
3157
+
3158
+
3159
+
3160
+
3161
+
3162
+ //
3163
+ // This draws a rectangle with rounded corners [for negative
3164
+ // values]
3165
+ //
3166
+ // @param number x The X coordinate
3167
+ // @param number y The Y coordinate
3168
+ // @param number width The width of the rectangle
3169
+ // @param number height The height of the rectangle
3170
+ // @param number radius The radius of the corners. Bigger values mean more rounded corners
3171
+ //
3172
+ this.roundedCornersRectNegative = function (x, y, width, height)
3173
+ {
3174
+ var radiusTop = null;
3175
+ var radiusBottom = null;
3176
+
3177
+ // Top radius
3178
+ if (properties.cornersRoundTop) {
3179
+ if (RGraph.isNumber(properties.cornersRoundTopRadius)) {
3180
+ radiusTop = properties.cornersRoundTopRadius;
3181
+ } else {
3182
+ radiusTop = Math.min(width / 2, height / 2, properties.cornersRoundRadius);
3183
+ }
3184
+ } else {
3185
+ radiusTop = 0;
3186
+ }
3187
+
3188
+ // Bottom radius
3189
+ if (properties.cornersRoundBottom) {
3190
+ if (RGraph.isNumber(properties.cornersRoundBottomRadius)) {
3191
+ radiusBottom = properties.cornersRoundBottomRadius;
3192
+ } else {
3193
+ radiusBottom = Math.min(width / 2, height / 2, properties.cornersRoundRadius);
3194
+ }
3195
+ } else {
3196
+ radiusBottom = 0;
3197
+ }
3198
+
3199
+
3200
+
3201
+
3202
+ if ( (radiusTop + radiusBottom) > height) {
3203
+
3204
+ // Calculate the top and bottom radiii and assign
3205
+ // to temporary variables
3206
+ var a = height / (radiusTop + radiusBottom) * radiusTop;
3207
+ var b = height / (radiusTop + radiusBottom) * radiusBottom;
3208
+
3209
+ // Reassign the values to the correct variables
3210
+ radiusTop = a;
3211
+ radiusBottom = b;
3212
+ }
3213
+
3214
+
3215
+ // Because the function is added to the context prototype
3216
+ // the 'this' variable is actually the context
3217
+
3218
+ // Save the existing state of the canvas so that it can be restored later
3219
+ this.context.save();
3220
+
3221
+ // Translate to the given X/Y coordinates
3222
+ this.context.translate(x, y);
3223
+
3224
+ // Move to the center of the top horizontal line
3225
+ this.context.moveTo(width,0);
3226
+
3227
+ // Draw the rounded corners. The connecting lines in between them are drawn automatically
3228
+ this.context.lineTo(width,height);
3229
+ this.context.lineTo(0 + radiusBottom, height);
3230
+ this.context.arcTo(0,height, 0,height - radiusBottom, properties.cornersRoundBottom ? radiusBottom : 0);
3231
+ this.context.arcTo(0, 0, 0 + radiusTop,0, properties.cornersRoundTop ? radiusTop : 0);
3232
+
3233
+ // Draw a line back to the start coordinates
3234
+ this.context.closePath();
3235
+
3236
+ // Restore the state of the canvas to as it was before the save()
3237
+ this.context.restore();
3238
+ };
3239
+
3240
+
3241
+
3242
+
3243
+
3244
+
3245
+
3246
+
3247
+ //
3248
+ // A worker function that handles Bar chart specific tooltip substitutions
3249
+ //
3250
+ this.tooltipSubstitutions = function (opt)
3251
+ {
3252
+ var indexes = RGraph.sequentialIndexToGrouped(opt.index, this.data);
3253
+
3254
+ if (typeof this.data[indexes[0]] === 'object') {
3255
+ var values = this.data[indexes[0]];
3256
+ } else {
3257
+ var values = [this.data[indexes[0]]];
3258
+ }
3259
+
3260
+ var value = this.data_arr[opt.index];
3261
+ var index = indexes[1];
3262
+ var seq = opt.index;
3263
+
3264
+ // Skirt an indexes bug
3265
+ if (typeof this.data[indexes[0]] === 'object' && properties.grouping === 'stacked') {
3266
+ value = this.data[indexes[0]][this.data[indexes[0]].length - 1 - indexes[1]];
3267
+ }
3268
+
3269
+ //
3270
+ // Return the values to the user
3271
+ //
3272
+ return {
3273
+ index: index,
3274
+ dataset: indexes[0],
3275
+ sequentialIndex: seq,
3276
+ value: value,
3277
+ values: values
3278
+ };
3279
+ };
3280
+
3281
+
3282
+
3283
+
3284
+
3285
+
3286
+
3287
+
3288
+ //
3289
+ // A worker function that returns the correct color/label/value
3290
+ //
3291
+ // @param object specific The indexes that are applicable
3292
+ // @param number index The appropriate index
3293
+ //
3294
+ this.tooltipsFormattedCustom = function (specific, index)
3295
+ {
3296
+ if (this.stackedOrGrouped) {
3297
+ var label = (!RGraph.isNull(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[index])
3298
+ ? properties.tooltipsFormattedKeyLabels[index]
3299
+ : '';
3300
+
3301
+ } else {
3302
+ var label = (
3303
+ !RGraph.isNull(properties.tooltipsFormattedKeyLabels)
3304
+ && typeof properties.tooltipsFormattedKeyLabels === 'object'
3305
+ && properties.tooltipsFormattedKeyLabels[specific.index]
3306
+ )
3307
+ ? properties.tooltipsFormattedKeyLabels[specific.index]
3308
+ : '';
3309
+ }
3310
+
3311
+ return {
3312
+ label: label
3313
+ };
3314
+ };
3315
+
3316
+
3317
+
3318
+
3319
+
3320
+
3321
+
3322
+
3323
+ //
3324
+ // This allows for static tooltip positioning
3325
+ //
3326
+ this.positionTooltipStatic = function (args)
3327
+ {
3328
+ var obj = args.object,
3329
+ e = args.event,
3330
+ tooltip = args.tooltip,
3331
+ index = args.index,
3332
+ canvasXY = RGraph.getCanvasXY(obj.canvas)
3333
+ coords = this.coords[args.index];
3334
+
3335
+ // Position the tooltip in the X direction
3336
+ args.tooltip.style.left = (
3337
+ canvasXY[0] // The X coordinate of the canvas
3338
+ + coords[0] // The X coordinate of the point on the chart
3339
+ + (coords[2] / 2) // Add half of the width of the bar
3340
+ - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width
3341
+ + obj.properties.tooltipsOffsetx // Add any user defined offset
3342
+ ) + 'px';
3343
+
3344
+ args.tooltip.style.top = (
3345
+ canvasXY[1] // The Y coordinate of the canvas
3346
+ + coords[1] // The Y coordinate of the bar on the chart
3347
+ - tooltip.offsetHeight // The height of the tooltip
3348
+ - 10 // An arbitrary amount
3349
+ + obj.properties.tooltipsOffsety // Add any user defined offset
3350
+ ) + 'px';
3351
+
3352
+
3353
+
3354
+ // If the chart is a 3D version the tooltip Y position needs this
3355
+ // adjustment
3356
+ if (properties.variant === '3d') {
3357
+ var left = parseInt(args.tooltip.style.left);
3358
+ var top = coords[1];
3359
+ var angle = properties.variantThreedAngle;
3360
+
3361
+ var adjustment = Math.tan(angle) * left;
3362
+
3363
+ args.tooltip.style.top = parseInt(args.tooltip.style.top) + adjustment + 'px';
3364
+ }
3365
+ };
3366
+
3367
+
3368
+
3369
+
3370
+
3371
+
3372
+
3373
+
3374
+ //
3375
+ // This returns the relevant value for the formatted key
3376
+ // macro %{value}. THIS VALUE SHOULD NOT BE FORMATTED.
3377
+ //
3378
+ // @param number index The index in the dataset to get
3379
+ // the value for
3380
+ //
3381
+ this.getKeyValue = function (index)
3382
+ {
3383
+ if ( RGraph.isArray(this.properties.keyFormattedValueSpecific)
3384
+ && RGraph.isNumber(this.properties.keyFormattedValueSpecific[index])) {
3385
+
3386
+ return this.properties.keyFormattedValueSpecific[index];
3387
+
3388
+ } else {
3389
+ return this.data[index];
3390
+ }
3391
+ };
3392
+
3393
+
3394
+
3395
+
3396
+
3397
+
3398
+
3399
+
3400
+ //
3401
+ // Returns how many data-points there should be when a string
3402
+ // based key property has been specified. For example, this:
3403
+ //
3404
+ // key: '%{property:_labels[%{index}]} %{value_formatted}'
3405
+ //
3406
+ // ...depending on how many bits of data ther is might get
3407
+ // turned into this:
3408
+ //
3409
+ // key: [
3410
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3411
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3412
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3413
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3414
+ // '%{property:_labels[%{index}]} %{value_formatted}',
3415
+ // ]
3416
+ //
3417
+ // ... ie in that case there would be 4 data-points so the
3418
+ // template is repeated 4 times.
3419
+ //
3420
+ this.getKeyNumDatapoints = function ()
3421
+ {
3422
+ var num = 0;
3423
+
3424
+ for (let i=0; i<this.data.length; ++i) {
3425
+ if (RGraph.isArray(this.data[i])) {
3426
+ num = Math.max(
3427
+ num,
3428
+ this.data[i].length
3429
+ );
3430
+ }
3431
+ }
3432
+
3433
+ return num;
3434
+ };
3435
+
3436
+
3437
+
3438
+
3439
+
3440
+
3441
+
3442
+
3443
+ //
3444
+ // Draw a line on the chart - just each of the X points
3445
+ // (ie the top of the bars) connected by a line. This
3446
+ // does mean you can set the colors property to transparent
3447
+ // and you have a vertical line.
3448
+ //
3449
+ this.drawLine = function ()
3450
+ {
3451
+ // Set the clipping region so that the trace
3452
+ //effect works
3453
+ RGraph.clipTo.start(this, [
3454
+ 0,0,
3455
+ this.canvas.width,this.canvas.height * this.properties.animationTraceClip
3456
+ ]);
3457
+
3458
+ if (this.properties.lineShadow) {
3459
+ RGraph.setShadow({
3460
+ object: this,
3461
+ prefix: 'lineShadow'
3462
+ });
3463
+ }
3464
+
3465
+ // Set the line options:
3466
+ // lineJoin
3467
+ // lineCap
3468
+ // lineWidth
3469
+ this.path(
3470
+ 'lj % lc % lw %',
3471
+ this.properties.lineLinejoin,
3472
+ this.properties.lineLinecap,
3473
+ this.properties.lineLinewidth
3474
+ );
3475
+
3476
+ if (this.properties.lineSpline) {
3477
+
3478
+ // Set this so that we can refer to the object
3479
+ var obj = this;
3480
+ var c = RGraph.arrayClone(this.coords);
3481
+
3482
+ Spline(c);
3483
+
3484
+ } else {
3485
+
3486
+
3487
+
3488
+
3489
+
3490
+ // Move to the first point
3491
+ if (this.properties.yaxisPosition === 'right') {
3492
+ this.path(
3493
+ 'b m % %',
3494
+ this.coords[0][0],
3495
+ this.coords[0][1] + (this.coords[0][3] / 2)
3496
+ );
3497
+ } else if (this.properties.yaxisPosition === 'center') {
3498
+ if (this.data[0] > 0) {
3499
+ this.path(
3500
+ 'b m % %',
3501
+ this.coords[0][0] + this.coords[0][2],
3502
+ this.coords[0][1] + (this.coords[0][3] / 2)
3503
+ );
3504
+ } else {
3505
+ this.path(
3506
+ 'b m % %',
3507
+ this.coords[0][0],
3508
+ this.coords[0][1] + (this.coords[0][3] / 2)
3509
+ );
3510
+ }
3511
+ } else {
3512
+ this.path(
3513
+ 'b m % %',
3514
+ this.coords[0][0] + this.coords[0][2],
3515
+ this.coords[0][1] + (this.coords[0][3] / 2)
3516
+ );
3517
+ }
3518
+
3519
+
3520
+
3521
+
3522
+ // Draw a line to subsequent points unless
3523
+ // that point is null, in which case move
3524
+ // to it instead
3525
+ for (let i=1; i<this.coords.length; ++i) {
3526
+
3527
+ if (RGraph.isNull(this.data[i]) || RGraph.isNull(this.data[i - 1])) {
3528
+ var action = 'm';
3529
+ } else {
3530
+ var action = 'l'
3531
+ }
3532
+
3533
+ if (this.properties.yaxisPosition === 'right') {
3534
+ this.path(
3535
+ '% % %',
3536
+ action,
3537
+ this.coords[i][0],
3538
+ this.coords[i][1] + (this.coords[i][3] / 2)
3539
+ );
3540
+ } else if (this.properties.yaxisPosition === 'center') {
3541
+ if (this.data[i] > 0) {
3542
+ this.path(
3543
+ '% % %',
3544
+ action,
3545
+ this.coords[i][0] + this.coords[i][2],
3546
+ this.coords[i][1] + (this.coords[i][3] / 2)
3547
+ );
3548
+ } else {
3549
+ this.path(
3550
+ '% % %',
3551
+ action,
3552
+ this.coords[i][0],
3553
+ this.coords[i][1] + (this.coords[i][3] / 2)
3554
+ );
3555
+ }
3556
+ } else {
3557
+ this.path(
3558
+ '% % %',
3559
+ action,
3560
+ this.coords[i][0] + this.coords[i][2],
3561
+ this.coords[i][1] + (this.coords[i][3] / 2)
3562
+ );
3563
+ }
3564
+ }
3565
+ this.path('s ' + this.properties.lineColor);
3566
+ }
3567
+
3568
+ //
3569
+ // TODO Add more styles of tickmarks
3570
+ //
3571
+ var obj = this;
3572
+ this.coords.forEach(function (v, k, arr)
3573
+ {
3574
+ if (typeof obj.properties.lineTickmarksStyle === 'string') {
3575
+
3576
+ var isEndTick = (k === 0 || k === (arr.length - 1));
3577
+ var isNull = RGraph.isNull(obj.data[k]);
3578
+ var prevIsNull = RGraph.isNull(obj.data[k - 1]);
3579
+ var nextIsNull = RGraph.isNull(obj.data[k + 1]);
3580
+ var isLast = k === (arr.length - 1);
3581
+
3582
+ //
3583
+ // Does this tickmark need to be drawn?
3584
+ //
3585
+ if (isNull && !obj.properties.lineTickmarksDrawNull) return;
3586
+ if (!isNull && prevIsNull && nextIsNull && !obj.properties.lineTickmarksDrawNonNull) return;
3587
+
3588
+ // Determine the X and Y coordinates for the
3589
+ // tickmark
3590
+ var x, y;
3591
+ if (obj.properties.yaxisPosition === 'right') {
3592
+ var x = v[0],
3593
+ y = v[1] + (v[3]/2);
3594
+ } else if (obj.properties.yaxisPosition === 'center') {
3595
+ if (obj.data[k] > 0) {
3596
+ var x = v[0] + v[2],
3597
+ y = v[1] + (v[3]/2);
3598
+ } else {
3599
+ var x = v[0],
3600
+ y = v[1] + (v[3]/2);
3601
+ }
3602
+ } else {
3603
+ var x = v[0] + v[2],
3604
+ y = v[1] + (v[3]/2);
3605
+ }
3606
+
3607
+ //
3608
+ // Draw the various styles of tickmark
3609
+ //
3610
+ switch (obj.properties.lineTickmarksStyle) {
3611
+ case 'circle':
3612
+ case 'filledcircle':
3613
+ case 'filledendcircle':
3614
+ case 'endcircle':
3615
+ if (
3616
+ (obj.properties.lineTickmarksStyle.indexOf('end') >= 0 && isEndTick)
3617
+ || obj.properties.lineTickmarksStyle.indexOf('end') === -1
3618
+ ) {
3619
+ obj.path(
3620
+ 'b a % % % 0 6.29 false s % f %',
3621
+ x,
3622
+ y,
3623
+ obj.properties.lineTickmarksSize,
3624
+ obj.properties.lineColor,
3625
+ obj.properties.lineTickmarksStyle.indexOf('filled') >= 0
3626
+ ? obj.properties.lineColor
3627
+ : 'white'
3628
+ );
3629
+ }
3630
+ break;
3631
+
3632
+ case 'square':
3633
+ case 'rect':
3634
+ case 'filledsquare':
3635
+ case 'filledrect':
3636
+ case 'filledendsquare':
3637
+ case 'filledendrect':
3638
+ case 'endsquare':
3639
+ case 'endrect':
3640
+ if (
3641
+ (obj.properties.lineTickmarksStyle.indexOf('end') >= 0 && isEndTick)
3642
+ || obj.properties.lineTickmarksStyle.indexOf('end') === -1
3643
+ ) {
3644
+
3645
+ obj.path(
3646
+ 'b r % % % % s % f %',
3647
+
3648
+ x - obj.properties.lineTickmarksSize,
3649
+ y - obj.properties.lineTickmarksSize,
3650
+ obj.properties.lineTickmarksSize * 2,
3651
+ obj.properties.lineTickmarksSize * 2,
3652
+
3653
+ obj.properties.lineColor,
3654
+ obj.properties.lineTickmarksStyle.indexOf('filled') >= 0
3655
+ ? obj.properties.lineColor
3656
+ : 'white'
3657
+ );
3658
+ }
3659
+ break;
3660
+ }
3661
+ }
3662
+ });
3663
+
3664
+
3665
+ // Turn the shadow off
3666
+ RGraph.noShadow(this);
3667
+
3668
+ // End the trace animation clipping
3669
+ RGraph.clipTo.end();
3670
+
3671
+
3672
+
3673
+
3674
+ //
3675
+ // This function draws a spline using the HBar coords
3676
+ //
3677
+ // @param array coords The coordinates
3678
+ //
3679
+ function Spline (coords)
3680
+ {
3681
+ var context = obj.context;
3682
+
3683
+ obj.coordsSpline[0] = [];
3684
+
3685
+ var yCoords = [],
3686
+ interval = (obj.canvas.height - obj.properties.marginTop - obj.properties.marginBottom) / coords.length;
3687
+
3688
+ obj.context.beginPath();
3689
+ obj.context.strokeStyle = obj.properties.lineColor;
3690
+
3691
+ //
3692
+ // The drawSpline function needs an array of JUST
3693
+ // the X values - so put the coords into the correct
3694
+ // format
3695
+ //
3696
+ for (var i=0; i<coords.length;++i) {
3697
+ coords[i] = Number(coords[i][0]) + (obj.properties.yaxisPosition === 'right' ? 0 : Number(coords[i][2]) );
3698
+ }
3699
+
3700
+
3701
+
3702
+
3703
+ //
3704
+ // Get the Points array in the format we want -
3705
+ // first value should be null along with the lst
3706
+ // value
3707
+ //
3708
+ var P = [coords[0]];
3709
+ for (var i=0; i<coords.length; ++i) {
3710
+ P.push(coords[i]);
3711
+ }
3712
+ P.push(
3713
+ coords[coords.length - 1] + (coords[coords.length - 1] - coords[coords.length - 2])
3714
+ );
3715
+
3716
+ // This is is necessary to center the points
3717
+ // within each bar/section
3718
+ var halfsection = ((obj.canvas.height - obj.properties.marginTop - obj.properties.marginBottom) / obj.data.length) / 2
3719
+
3720
+ for (var j=1; j<P.length-2; ++j) {
3721
+ for (var t=0; t<10; ++t) {
3722
+
3723
+ var xCoord = Spline( t/10, P[j-1], P[j], P[j+1], P[j+2] );
3724
+
3725
+ yCoords.push(
3726
+ ((j-1) * interval) + (t * (interval / 10)) + obj.properties.marginTop + halfsection
3727
+ );
3728
+
3729
+ obj.context.lineTo(
3730
+ xCoord,
3731
+ yCoords[yCoords.length - 1]
3732
+ );
3733
+
3734
+
3735
+ if (typeof index == 'number') {
3736
+ obj.coordsSpline[0].push(
3737
+ [xCoords[xCoords.length - 1], yCoords[yCoords.length - 1]]
3738
+ );
3739
+ }
3740
+ }
3741
+ }
3742
+
3743
+
3744
+ // Draw the last section
3745
+ var last = [
3746
+ obj.coords[obj.coords.length - 1][0] + (obj.properties.yaxisPosition === 'right' ? 0 : obj.coords[obj.coords.length - 1][2]),
3747
+ obj.coords[obj.coords.length - 1][1] + (obj.coords[obj.coords.length - 1][3] / 2)
3748
+ ];
3749
+
3750
+ obj.context.lineTo(
3751
+ last[0],
3752
+ last[1]
3753
+ );
3754
+ if (typeof index == 'number') {
3755
+ obj.coordsSpline[0].push(
3756
+ [
3757
+ last[0],
3758
+ last[1]
3759
+ ]
3760
+ );
3761
+ }
3762
+
3763
+ obj.context.stroke();
3764
+
3765
+
3766
+
3767
+ function Spline (t, P0, P1, P2, P3)
3768
+ {
3769
+ return 0.5 * ((2 * P1) +
3770
+ ((0-P0) + P2) * t +
3771
+ ((2*P0 - (5*P1) + (4*P2) - P3) * (t*t) +
3772
+ ((0-P0) + (3*P1)- (3*P2) + P3) * (t*t*t)));
3773
+ }
3774
+ }
3775
+ };
3776
+
3777
+
3778
+
3779
+
3780
+
3781
+
3782
+
3783
+
3784
+ //
3785
+ // Trace (for the line only)
3786
+ //
3787
+ // This is a new version of the Trace effect which no longer requires jQuery and is more compatible
3788
+ // with other effects (eg Expand). This new effect is considerably simpler and less code.
3789
+ //
3790
+ // @param object Options for the effect. Currently only "frames" is available.
3791
+ // @param int A function that is called when the ffect is complete
3792
+ //
3793
+ this.trace = function ()
3794
+ {
3795
+ // Cancel any stop request if one is pending
3796
+ this.cancelStopAnimation();
3797
+
3798
+ var obj = this,
3799
+ opt = arguments[0] || {},
3800
+ frames = opt.frames || 60,
3801
+ frame = 0,
3802
+ callback = arguments[1] || function () {};
3803
+
3804
+ obj.set('animationTraceClip', opt.reverse ? 1 : 0);
3805
+
3806
+ // Disable the labelsAbove option
3807
+ if (obj.properties.labelsAbove) {
3808
+ obj.set('labelsAbove', false);
3809
+ var enableLabelsAbove = true;
3810
+ }
3811
+
3812
+
3813
+ function iterator ()
3814
+ {
3815
+ if (obj.stopAnimationRequested) {
3816
+
3817
+ // Reset the flag
3818
+ obj.stopAnimationRequested = false;
3819
+
3820
+ return;
3821
+ }
3822
+
3823
+ RGraph.clear(obj.canvas);
3824
+
3825
+ RGraph.redrawCanvas(obj.canvas);
3826
+
3827
+ if (frame++ < frames) {
3828
+ obj.set('animationTraceClip', opt.reverse ? (1 - (frame / frames)) : (frame / frames));
3829
+ RGraph.Effects.updateCanvas(iterator);
3830
+ } else {
3831
+ if (enableLabelsAbove) {
3832
+ setTimeout(function ()
3833
+ {
3834
+ obj.set('labelsAbove', true);
3835
+ RGraph.redraw();
3836
+ }, 500);
3837
+ }
3838
+ callback(obj);
3839
+ }
3840
+ }
3841
+
3842
+ iterator();
3843
+
3844
+ return this;
3845
+ };
3846
+
3847
+
3848
+
3849
+
3850
+
3851
+
3852
+
3853
+ //
3854
+ // Charts are now always registered
3855
+ //
3856
+ RGraph.register(this);
3857
+
3858
+
3859
+
3860
+
3861
+
3862
+
3863
+
3864
+
3865
+ //
3866
+ // This is the 'end' of the constructor so if the first argument
3867
+ // contains configuration data - handle that.
3868
+ //
3869
+ RGraph.parseObjectStyleConfig(this, conf.options);
3870
+ };