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