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,115 +1,2686 @@
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.Effects=RGraph.Effects||{};RGraph.Effects.Rose=RGraph.Effects.Rose||{};RGraph.Rose=function(conf)
3
- {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var parseConfObjectForOptions=true;}else{var conf={id:conf};conf.data=arguments[1];}
4
- this.id=conf.id;this.canvas=document.getElementById(this.id);this.context=this.canvas.getContext?this.canvas.getContext("2d"):null;this.data=conf.data;this.canvas.__object__=this;this.type='rose';this.isRGraph=true;this.uid=RGraph.CreateUID();this.canvas.uid=this.canvas.uid?this.canvas.uid:RGraph.CreateUID();this.colorsParsed=false;this.coordsText=[];this.original_colors=[];this.firstDraw=true;this.propertyNameAliases={};this.centerx=0;this.centery=0;this.radius=0;this.max=0;this.angles=[];this.angles2=[];this.properties={'chart.axes':false,'chart.axes.color':'black','chart.axes.linewidth':1,'chart.axes.tickmarks':true,'chart.background.grid':true,'chart.background.grid.color':'#ccc','chart.background.grid.size':null,'chart.background.grid.radials.count':null,'chart.background.grid.circles.count':5,'chart.centerx':null,'chart.centery':null,'chart.radius':null,'chart.angles.start':0,'chart.linewidth':1,'chart.colors':['rgba(255,0,0,0.5)','rgba(255,255,0,0.5)','rgba(0,255,255,0.5)','rgb(0,255,0)','gray','blue','rgb(255,128,255)','green','pink','gray','aqua'],'chart.colors.sequential':false,'chart.colors.alpha':null,'chart.colors.stroke':'rgba(0,0,0,0)','chart.margin':5,'chart.margin.left':25,'chart.margin.right':25,'chart.margin.top':25,'chart.margin.bottom':25,'chart.shadow':false,'chart.shadow.color':'#aaa','chart.shadow.offsetx':0,'chart.shadow.offsety':0,'chart.shadow.blur':15,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.bold':null,'chart.title.font':null,'chart.title.size':null,'chart.title.italic':null,'chart.title.color':null,'chart.title.x':null,'chart.title.y':null,'chart.title.halign':null,'chart.title.valign':null,'chart.labels':null,'chart.labels.color':null,'chart.labels.font':null,'chart.labels.size':null,'chart.labels.bold':null,'chart.labels.italic':null,'chart.labels.position':'center','chart.labels.boxed':false,'chart.labels.offset':0,'chart.labels.axes':'n','chart.labels.axes.font':null,'chart.labels.axes.size':null,'chart.labels.axes.color':null,'chart.labels.axes.bold':null,'chart.labels.axes.italic':null,'chart.labels.axes.count':5,'chart.text.color':'black','chart.text.font':'Arial, Verdana, sans-serif','chart.text.size':12,'chart.text.bold':false,'chart.text.italic':false,'chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':false,'chart.key':null,'chart.key.background':'white','chart.key.position':'graph','chart.key.halign':'right','chart.key.shadow':false,'chart.key.shadow.color':'#666','chart.key.shadow.blur':3,'chart.key.shadow.offsetx':2,'chart.key.shadow.offsety':2,'chart.key.position.gutter.boxed':false,'chart.key.position.x':null,'chart.key.position.y':null,'chart.key.color.shape':'square','chart.key.rounded':true,'chart.key.linewidth':1,'chart.key.colors':null,'chart.key.interactive':false,'chart.key.interactive.highlight.chart.stroke':'black','chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)','chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)','chart.key.labels.color':null,'chart.key.labels.font':null,'chart.key.labels.size':null,'chart.key.labels.bold':null,'chart.key.labels.italic':null,'chart.key.labels.offsetx':0,'chart.key.labels.offsety':0,'chart.contextmenu':null,'chart.tooltips':null,'chart.tooltips.event':'onclick','chart.tooltips.effect':'fade','chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.highlight':true,'chart.highlight.stroke':'rgba(0,0,0,0)','chart.highlight.fill':'rgba(255,255,255,0.7)','chart.annotatable':false,'chart.annotatable.color':'black','chart.annotatable.linewidth':1,'chart.resizable':false,'chart.resizable.handle.adjust':[0,0],'chart.resizable.handle.background':null,'chart.adjustable':false,'chart.scale.max':null,'chart.scale.min':0,'chart.scale.decimals':null,'chart.scale.point':'.','chart.scale.thousand':',','chart.scale.units.pre':'','chart.scale.units.post':'','chart.variant':'stacked','chart.variant.threed.depth':10,'chart.exploded':0,'chart.events.mousemove':null,'chart.events.click':null,'chart.animation.roundrobin.factor':1,'chart.animation.roundrobin.radius':true,'chart.animation.grow.multiplier':1,'chart.segment.highlight':false,'chart.segment.highlight.count':null,'chart.segment.highlight.fill':'rgba(0,255,0,0.5)','chart.segment.highlight.stroke':'rgba(0,0,0,0)','chart.clearto':'rgba(0,0,0,0)'}
5
- for(var i=0;i<this.data.length;++i){if(typeof this.data[i]==='string'){this.data[i]=parseFloat(this.data[i]);}else if(typeof this.data[i]==='object'){for(var j=0;j<this.data[i].length;++j){if(typeof this.data[i][j]==='string'){this.data[i][j]=parseFloat(this.data[i][j]);}}}}
6
- var linear_data=RGraph.arrayLinearize(this.data);for(var i=0;i<linear_data.length;++i){this["$"+i]={};}
7
- if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
8
- var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
9
- if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
10
- this.set=this.Set=function(name)
11
- {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
12
- if(name.substr(0,6)!='chart.'){name='chart.'+name;}
13
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
14
- prop[name.toLowerCase()]=value;return this;};this.get=this.Get=function(name)
15
- {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
16
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
17
- return prop[name.toLowerCase()];};this.draw=this.Draw=function()
18
- {RG.fireCustomEvent(this,'onbeforedraw');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.radius=(ma.min(ca.width-this.marginLeft-this.marginRight,ca.height-this.marginTop-this.marginBottom)/2);this.centerx=((ca.width-this.marginLeft-this.marginRight)/2)+this.marginLeft;this.centery=((ca.height-this.marginTop-this.marginBottom)/2)+this.marginTop;this.angles=[];this.angles2=[];this.total=0;this.startRadians=prop['chart.angles.start'];this.coordsText=[];if(prop['chart.key']&&prop['chart.key'].length>0&&prop['chart.key'].length>=3){this.centerx=this.centerx-this.marginRight+5;}
19
- if(typeof prop['chart.centerx']=='number')this.centerx=prop['chart.centerx'];if(typeof prop['chart.centery']=='number')this.centery=prop['chart.centery'];if(typeof prop['chart.radius']=='number')this.radius=prop['chart.radius'];if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
20
- if(prop['chart.variant'].indexOf('3d')!==-1){var scaleX=1.5;this.context.setTransform(scaleX,0,0,1,(ca.width*scaleX-ca.width)* -0.5,0);}
21
- this.drawBackground();if(prop['chart.variant'].indexOf('3d')!==-1){RG.setShadow(this,'rgba(0,0,0,0.35)',0,15,25);for(var i=prop['chart.variant.threed.depth'];i>0;i-=1){this.centery-=1;this.drawRose({storeAngles:false});RG.noShadow(this);for(var j=0,len=this.angles.length;j<len;j+=1){var a=this.angles[j];pa2(co,['b','m',a[4],a[5],'a',a[4],a[5],a[3]+1.5,a[0]-0.01,a[1]+0.01,false,'c','f','rgba(0,0,0,0.1)']);}}}
22
- this.drawRose();this.drawLabels();co.strokeStyle='rgba(0,0,0,0)'
23
- if(prop['chart.contextmenu']){RG.ShowContext(this);}
24
- if(prop['chart.resizable']){RG.AllowResizing(this);}
25
- if(prop['chart.adjustable']){RG.AllowAdjusting(this);}
26
- RG.InstallEventListeners(this);if(prop['chart.segment.highlight']){if(!RG.allowSegmentHighlight){alert('[WARNING] The segment highlight function does not exist - have you included the dynamic library?');}
27
- RG.allowSegmentHighlight({object:this,count:typeof prop['chart.segment.highlight.count']==='number'?prop['chart.segment.highlight.count']:this.data.length,fill:prop['chart.segment.highlight.fill'],stroke:prop['chart.segment.highlight.stroke']});}
28
- if(this.firstDraw){this.firstDraw=false;RG.fireCustomEvent(this,'onfirstdraw');this.firstDrawFunc();}
29
- RG.FireCustomEvent(this,'ondraw');return this;};this.drawBackground=this.DrawBackground=function()
30
- {co.lineWidth=1;if(prop['chart.background.grid.circles.count']){if(typeof(prop['chart.background.grid.circles.count'])=='number'){prop['chart.background.grid.circles.size']=this.radius/prop['chart.background.grid.circles.count'];}
31
- co.beginPath();co.strokeStyle=prop['chart.background.grid.color'];for(var i=prop['chart.background.grid.circles.size'];i<=this.radius;i+=prop['chart.background.grid.circles.size']){co.moveTo(this.centerx+i,this.centery);co.arc(this.centerx,this.centery,i,0,RG.TWOPI,false);}
32
- co.stroke();co.beginPath();if(typeof prop['chart.background.grid.radials.count']!=='number'){prop['chart.background.grid.radials.count']=this.data.length}
33
- if(prop['chart.background.grid.radials.count']>0){var num=(360/prop['chart.background.grid.radials.count']);for(var i=0;i<=360;i+=num){co.arc(this.centerx,this.centery,this.radius,((i/(180/RG.PI))-RG.HALFPI)+this.startRadians,(((i+0.0001)/(180/RG.PI))-RG.HALFPI)+this.startRadians,false);co.lineTo(this.centerx,this.centery);}
34
- co.stroke();}}
35
- if(prop['chart.axes']){co.beginPath();co.strokeStyle=prop['chart.axes.color'];co.lineWidth=prop['chart.axes.linewidth'];co.moveTo(this.centerx-this.radius,ma.round(this.centery));co.lineTo(this.centerx+this.radius,ma.round(this.centery));if(prop['chart.axes.tickmarks']){co.moveTo(ma.round(this.centerx-this.radius),this.centery-5);co.lineTo(ma.round(this.centerx-this.radius),this.centery+5);co.moveTo(ma.round(this.centerx+this.radius),this.centery-5);co.lineTo(ma.round(this.centerx+this.radius),this.centery+5);for(var i=(this.centerx-this.radius);i<(this.centerx+this.radius);i+=(this.radius/5)){co.moveTo(ma.round(i),this.centery-3);co.lineTo(ma.round(i),this.centery+3.5);}
36
- for(var i=(this.centery-this.radius);i<(this.centery+this.radius);i+=(this.radius/5)){co.moveTo(this.centerx-3,ma.round(i));co.lineTo(this.centerx+3,ma.round(i));}}
37
- co.moveTo(ma.round(this.centerx),this.centery-this.radius);co.lineTo(ma.round(this.centerx),this.centery+this.radius);if(prop['chart.axes.tickmarks']){co.moveTo(this.centerx-5,ma.round(this.centery-this.radius));co.lineTo(this.centerx+5,ma.round(this.centery-this.radius));co.moveTo(this.centerx-5,ma.round(this.centery+this.radius));co.lineTo(this.centerx+5,ma.round(this.centery+this.radius));}
38
- co.closePath();co.stroke();}
39
- pa2(co,'b c');};this.drawRose=this.DrawRose=function()
40
- {var max=0,data=this.data,margin=RG.toRadians(prop['chart.margin']),opt=arguments[0]||{};co.lineWidth=prop['chart.linewidth'];if(RG.isNull(prop['chart.scale.max'])){for(var i=0;i<data.length;++i){if(typeof data[i]=='number'){max=ma.max(max,data[i]);}else if(typeof data[i]=='object'&&prop['chart.variant'].indexOf('non-equi-angular')!==-1){max=ma.max(max,data[i][0]);}else{max=ma.max(max,RG.arraySum(data[i]));}}
41
- this.scale2=RG.getScale2(this,{'scale.max':max,'scale.min':0,'scale.thousand':prop['chart.scale.thousand'],'scale.point':prop['chart.scale.point'],'scale.decimals':prop['chart.scale.decimals'],'scale.labels.count':prop['chart.labels.axes.count'],'scale.round':prop['chart.scale.round'],'scale.units.pre':prop['chart.scale.units.pre'],'scale.units.post':prop['chart.scale.units.post']});this.max=this.scale2.max;}else{var ymax=prop['chart.scale.max'];this.scale2=RG.getScale2(this,{'scale.max':ymax,'scale.strict':true,'scale.thousand':prop['chart.scale.thousand'],'scale.point':prop['chart.scale.point'],'scale.decimals':prop['chart.scale.decimals'],'scale.labels.count':prop['chart.labels.axes.count'],'scale.round':prop['chart.scale.round'],'scale.units.pre':prop['chart.scale.units.pre'],'scale.units.post':prop['chart.scale.units.post']});this.max=this.scale2.max}
42
- this.sum=RG.arraySum(data);co.moveTo(this.centerx,this.centery);co.stroke();if(prop['chart.colors.alpha']){co.globalAlpha=prop['chart.colors.alpha'];}
43
- var sequentialIndex=0;if(typeof(prop['chart.variant'])=='string'&&prop['chart.variant'].indexOf('non-equi-angular')!==-1){var total=0;for(var i=0;i<data.length;++i){total+=data[i][1];}
44
- if(prop['chart.shadow']){RG.setShadow(this,prop['chart.shadow.color'],prop['chart.shadow.offsetx'],prop['chart.shadow.offsety'],prop['chart.shadow.blur']);}
45
- for(var i=0;i<this.data.length;++i){var segmentRadians=((this.data[i][1]/total)*RG.TWOPI);var radius=((this.data[i][0]-prop['chart.scale.min'])/(this.max-prop['chart.scale.min']))*this.radius;radius=radius*prop['chart.animation.grow.multiplier'];co.strokeStyle=prop['chart.colors.stroke'];co.fillStyle=prop['chart.colors'][0];if(prop['chart.colors.sequential']){co.fillStyle=prop['chart.colors'][i];}
46
- co.beginPath();var startAngle=(this.startRadians*prop['chart.animation.roundrobin.factor'])-RG.HALFPI+margin;var endAngle=((this.startRadians+segmentRadians)*prop['chart.animation.roundrobin.factor'])-RG.HALFPI-margin;var exploded=this.getexploded(i,startAngle,endAngle,prop['chart.exploded']);var explodedX=exploded[0];var explodedY=exploded[1];co.arc(this.centerx+explodedX,this.centery+explodedY,prop['chart.animation.roundrobin.radius']?radius*prop['chart.animation.roundrobin.factor']:radius,startAngle,endAngle,0);co.lineTo(this.centerx+explodedX,this.centery+explodedY);co.closePath();co.stroke();co.fill();this.angles[i]=[startAngle,endAngle,0,prop['chart.animation.roundrobin.radius']?radius*prop['chart.animation.roundrobin.factor']:radius,this.centerx+explodedX,this.centery+explodedY,co.strokeStyle,co.fillStyle];sequentialIndex++;this.startRadians+=segmentRadians;}
47
- if(prop['chart.shadow']){RG.noShadow(this);this.redrawRose();}}else{var sequentialColorIndex=0;if(prop['chart.shadow']){RG.setShadow(this,prop['chart.shadow.color'],prop['chart.shadow.offsetx'],prop['chart.shadow.offsety'],prop['chart.shadow.blur']);}
48
- for(var i=0;i<this.data.length;++i){var segmentRadians=(1/this.data.length)*RG.TWOPI;if(typeof this.data[i]=='number'){co.beginPath();co.strokeStyle=prop['chart.colors.stroke'];co.fillStyle=prop['chart.colors'][0];if(prop['chart.colors.sequential']){co.fillStyle=prop['chart.colors'][i];}
49
- var radius=((this.data[i]-prop['chart.scale.min'])/(this.max-prop['chart.scale.min']))*this.radius;radius=radius*prop['chart.animation.grow.multiplier'];var startAngle=(this.startRadians*prop['chart.animation.roundrobin.factor'])-RG.HALFPI+margin;var endAngle=(this.startRadians*prop['chart.animation.roundrobin.factor'])+(segmentRadians*prop['chart.animation.roundrobin.factor'])-RG.HALFPI-margin;var exploded=this.getexploded(i,startAngle,endAngle,prop['chart.exploded']);var explodedX=exploded[0];var explodedY=exploded[1];co.arc(this.centerx+explodedX,this.centery+explodedY,prop['chart.animation.roundrobin.radius']?radius*prop['chart.animation.roundrobin.factor']:radius,startAngle,endAngle,0);co.lineTo(this.centerx+explodedX,this.centery+explodedY);co.closePath();co.stroke();co.fill();co.beginPath();if(endAngle==0){}
50
- this.angles[i]=[startAngle,endAngle,0,radius*prop['chart.animation.roundrobin.factor'],this.centerx+explodedX,this.centery+explodedY,co.strokeStyle,co.fillStyle];sequentialIndex++;}else if(typeof(this.data[i])=='object'){var margin=prop['chart.margin']/(180/RG.PI);if(!this.angles2[i]){this.angles2[i]=[];}
51
- for(var j=0;j<this.data[i].length;++j){var startAngle=(this.startRadians*prop['chart.animation.roundrobin.factor'])-RG.HALFPI+margin;var endAngle=(this.startRadians*prop['chart.animation.roundrobin.factor'])+(segmentRadians*prop['chart.animation.roundrobin.factor'])-RG.HALFPI-margin;var exploded=this.getexploded(i,startAngle,endAngle,prop['chart.exploded']);var explodedX=exploded[0];var explodedY=exploded[1];co.strokeStyle=prop['chart.colors.stroke'];co.fillStyle=prop['chart.colors'][j];if(prop['chart.colors.sequential']){co.fillStyle=prop['chart.colors'][sequentialColorIndex++];}
52
- if(j==0){co.beginPath();var startRadius=0;var endRadius=((this.data[i][j]-prop['chart.scale.min'])/(this.max-prop['chart.scale.min']))*this.radius;endRadius=endRadius*prop['chart.animation.grow.multiplier'];co.arc(this.centerx+explodedX,this.centery+explodedY,prop['chart.animation.roundrobin.radius']?endRadius*prop['chart.animation.roundrobin.factor']:endRadius,startAngle,endAngle,0);co.lineTo(this.centerx+explodedX,this.centery+explodedY);co.closePath();co.stroke();co.fill();this.angles[sequentialIndex++]=[startAngle,endAngle,0,endRadius*prop['chart.animation.roundrobin.factor'],this.centerx+explodedX,this.centery+explodedY,co.strokeStyle,co.fillStyle];this.angles2[i][j]=[startAngle,endAngle,0,endRadius*prop['chart.animation.roundrobin.factor'],this.centerx+explodedX,this.centery+explodedY,co.strokeStyle,co.fillStyle];}else{co.beginPath();var startRadius=endRadius;var endRadius=(((this.data[i][j]-prop['chart.scale.min'])/(this.max-prop['chart.scale.min']))*this.radius)+startRadius;endRadius=endRadius*prop['chart.animation.grow.multiplier'];co.arc(this.centerx+explodedX,this.centery+explodedY,startRadius*prop['chart.animation.roundrobin.factor'],startAngle,endAngle,0);co.arc(this.centerx+explodedX,this.centery+explodedY,endRadius*prop['chart.animation.roundrobin.factor'],endAngle,startAngle,true);co.closePath();co.stroke();co.fill();this.angles[sequentialIndex++]=[startAngle,endAngle,startRadius*prop['chart.animation.roundrobin.factor'],endRadius*prop['chart.animation.roundrobin.factor'],this.centerx+explodedX,this.centery+explodedY,co.strokeStyle,co.fillStyle];this.angles2[i][j]=[startAngle,endAngle,startRadius*prop['chart.animation.roundrobin.factor'],endRadius*prop['chart.animation.roundrobin.factor'],this.centerx+explodedX,this.centery+explodedY,co.strokeStyle,co.fillStyle];}}}
53
- this.startRadians+=segmentRadians;}
54
- if(prop['chart.shadow']){RG.noShadow(this);}
55
- if(prop['chart.shadow']){this.redrawRose();}}
56
- if(prop['chart.colors.alpha']){co.globalAlpha=1;}
57
- if(prop['chart.title']){RG.drawTitle(this,prop['chart.title'],(ca.height/2)-this.radius,this.centerx,prop['chart.title.size']?prop['chart.title.size']:prop['chart.text.size']);}};this.redrawRose=function()
58
- {var angles=this.angles;for(var i=0;i<angles.length;++i){pa2(co,'b a % % % % % false a % % % % % true c f % f % ',angles[i][4],angles[i][5],angles[i][2],angles[i][0],angles[i][1],angles[i][4],angles[i][5],angles[i][3],angles[i][1],angles[i][0],angles[i][6],angles[i][7]);}};this.drawLabels=this.DrawLabels=function()
59
- {co.lineWidth=1;var key=prop['chart.key'];if(key&&key.length){RG.DrawKey(this,key,prop['chart.colors']);}
60
- co.fillStyle=prop['chart.text.color'];co.strokeStyle='black';var radius=this.radius,font=prop['chart.text.font'],size=prop['chart.text.size'],axes=prop['chart.labels.axes'].toLowerCase(),decimals=prop['chart.scale.decimals'],units_pre=prop['chart.scale.units.pre'],units_post=prop['chart.scale.units.post'],centerx=this.centerx,centery=this.centery+(prop['chart.variant'].indexOf('3d')!==-1?prop['chart.variant.threed.depth']:0);if(typeof prop['chart.labels']=='object'&&prop['chart.labels']){this.DrawCircularLabels(co,prop['chart.labels'],font,size,radius+10);}
61
- if(typeof(prop['chart.text.size'])=='number'){size=prop['chart.text.size'];}
62
- var color='rgba(255,255,255,0.8)';if(axes.indexOf('n')>-1){if(prop['chart.background.axes']){var offset=-10;var halign='right';}else{var offset=0;var halign='center';}
63
- var textConf=RG.getTextConf({object:this,prefix:'chart.labels.axes'});for(var i=0;i<prop['chart.labels.axes.count'];++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,'x':centerx+offset,'y':centery-(radius*((i+1)/prop['chart.labels.axes.count'])),'text':this.scale2.labels[i],'valign':'center','halign':halign,'bounding':true,'bounding.fill':color,'bounding.stroke':'rgba(0,0,0,0)','tag':'scale'});}}
64
- if(axes.indexOf('s')>-1){if(prop['chart.background.axes']){var offset=-10;var halign='right';}else{var offset=0;var halign='center';}
65
- for(var i=0;i<prop['chart.labels.axes.count'];++i){RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,'x':centerx+offset,'y':centery+(radius*((i+1)/prop['chart.labels.axes.count'])),'text':this.scale2.labels[i],'valign':'center','halign':halign,'bounding':true,'bounding.fill':color,'bounding.stroke':'rgba(0,0,0,0)','tag':'scale'});}}
66
- if(axes.indexOf('e')>-1){for(var i=0;i<prop['chart.labels.axes.count'];++i){if(prop['chart.background.axes']){var offset=10;var valign='top';}else{var offset=0;var valign='center';}
67
- RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,'x':centerx+(radius*((i+1)/prop['chart.labels.axes.count'])),'y':centery+offset,'text':this.scale2.labels[i],'valign':valign,'halign':'center','bounding':true,'bounding.fill':color,'bounding.stroke':'rgba(0,0,0,0)','tag':'scale'});}}
68
- if(axes.indexOf('w')>-1){for(var i=0;i<prop['chart.labels.axes.count'];++i){if(prop['chart.background.axes']){var offset=10;var valign='top';}else{var offset=0;var valign='center';}
69
- RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,'x':centerx-(radius*((i+1)/prop['chart.labels.axes.count'])),'y':centery+offset,'text':this.scale2.labels[i],'valign':valign,'halign':'center','bounding':true,'bounding.fill':color,'bounding.stroke':'rgba(0,0,0,0)','tag':'scale'});}}
70
- if(RG.trim(axes).length>0){RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,'x':centerx,'y':centery,'text':typeof prop['chart.scale.min']==='number'?RG.numberFormat({object:this,number:Number(prop['chart.scale.min']).toFixed(prop['chart.ymin']===0?'0':prop['chart.scale.decimals']),unitspre:units_pre,unitspost:units_post}):'0','valign':'center','halign':'center','bounding':true,'bounding.fill':color,'bounding.stroke':'rgba(0,0,0,0)','tag':'scale'});}};this.drawCircularLabels=this.DrawCircularLabels=function(co,labels,font,size,radius)
71
- {var variant=prop['chart.variant'],position=prop['chart.labels.position'],radius=radius+5+prop['chart.labels.offset'],centerx=this.centerx,centery=this.centery+(prop['chart.variant'].indexOf('3d')!==-1?prop['chart.variant.threed.depth']:0),labelsColor=prop['chart.labels.color']||prop['chart.text.color'],angles=this.angles
72
- var textConf=RG.getTextConf({object:this,prefix:'chart.labels'});for(var i=0;i<this.data.length;++i){if(typeof(variant)=='string'&&variant.indexOf('non-equi-angular')!==-1){var a=Number(angles[i][0])+((angles[i][1]-angles[i][0])/2);}else{var a=(RG.TWOPI/this.data.length)*(i+1)-(RG.TWOPI/(this.data.length*2));var a=a-RG.HALFPI+(prop['chart.labels.position']=='edge'?((RG.TWOPI/this.data.length)/2):0);}
73
- var x=centerx+(ma.cos(a)*radius);var y=centery+(ma.sin(a)*radius);if(x>centerx){halign='left';}else if(Math.round(x)==centerx){halign='center';}else{halign='right';}
74
- RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x,y:y,text:String(labels[i]||''),halign:halign,valign:'center',tag:'labels'});}};this.getShape=this.getSegment=function(e)
75
- {RG.fixEventObject(e);var angles=this.angles;var ret=[];var opt=arguments[1]?arguments[1]:{radius:true};for(var i=0;i<angles.length;++i){var angleStart=angles[i][0];var angleEnd=angles[i][1];var radiusStart=opt.radius===false?0:angles[i][2];var radiusEnd=opt.radius===false?this.radius:angles[i][3];var centerX=angles[i][4];var centerY=angles[i][5];var mouseXY=RG.getMouseXY(e);var mouseX=mouseXY[0]-centerX;var mouseY=mouseXY[1]-centerY;co.beginPath();co.arc(centerX,centerY,radiusStart?radiusStart:0.01,angleStart,angleEnd,false);co.arc(centerX,centerY,radiusEnd,angleEnd,angleStart,true);co.closePath();if(co.isPointInPath(mouseXY[0],mouseXY[1])){angles[i][6]=i;if(RG.parseTooltipText){var tooltip=RG.parseTooltipText(prop['chart.tooltips'],angles[i][6]);}
76
- angles[i]['object']=this;angles[i]['x']=angles[i][4];angles[i]['y']=angles[i][5];angles[i]['angle.start']=angles[i][0];angles[i]['angle.end']=angles[i][1];angles[i]['radius.start']=angles[i][2];angles[i]['radius.end']=angles[i][3];angles[i]['index']=angles[i][6];angles[i]['tooltip']=tooltip?tooltip:null;return angles[i];}}
77
- return null;};this.getExploded=this.getexploded=function(index,startAngle,endAngle,exploded)
78
- {var explodedx,explodedy;if(typeof(exploded)=='object'&&typeof(exploded[index])=='number'){explodedx=Math.cos(((endAngle-startAngle)/2)+startAngle)*exploded[index];explodedy=Math.sin(((endAngle-startAngle)/2)+startAngle)*exploded[index];}else if(typeof(exploded)=='number'){explodedx=Math.cos(((endAngle-startAngle)/2)+startAngle)*exploded;explodedy=Math.sin(((endAngle-startAngle)/2)+startAngle)*exploded;}else{explodedx=0;explodedy=0;}
79
- return[explodedx,explodedy];};this.allowTooltips=this.AllowTooltips=function()
80
- {RG.PreLoadTooltipImages(this);RG.InstallWindowMousedownTooltipListener(this);RG.InstallCanvasMousemoveTooltipListener(this);RG.InstallCanvasMouseupTooltipListener(this);};this.highlight=this.Highlight=function(shape)
81
- {if(prop['chart.tooltips.highlight']){if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);return;}
82
- co.beginPath();co.strokeStyle=prop['chart.highlight.stroke'];co.fillStyle=prop['chart.highlight.fill'];co.arc(shape['x'],shape['y'],shape['radius.end'],shape['angle.start'],shape['angle.end'],false);if(shape['radius.start']>0){co.arc(shape['x'],shape['y'],shape['radius.start'],shape['angle.end'],shape['angle.start'],true);}else{co.lineTo(shape['x'],shape['y']);}
83
- co.closePath();co.stroke();co.fill();}};this.getObjectByXY=function(e)
84
- {var mouseXY=RGraph.getMouseXY(e);var radius=RG.getHypLength(this.centerx,this.centery,mouseXY[0],mouseXY[1]);if(prop['chart.variant'].indexOf('3d')!==-1){radius/=-1;}
85
- if(mouseXY[0]>(this.centerx-this.radius)&&mouseXY[0]<(this.centerx+this.radius)&&mouseXY[1]>(this.centery-this.radius)&&mouseXY[1]<(this.centery+this.radius)&&radius<=this.radius){return this;}};this.getRadius=function(value)
86
- {if(value<0||value>this.max){return null;}
87
- var r=(value/this.max)*this.radius;return r;};this.parseColors=function()
88
- {if(this.original_colors.length===0){this.original_colors['chart.colors']=RG.array_clone(prop['chart.colors']);this.original_colors['chart.key.colors']=RG.array_clone(prop['chart.key.colors']);this.original_colors['chart.highlight.stroke']=RG.array_clone(prop['chart.highlight.stroke']);this.original_colors['chart.highlight.fill']=RG.array_clone(prop['chart.highlight.fill']);}
89
- for(var i=0;i<prop['chart.colors'].length;++i){prop['chart.colors'][i]=this.parseSingleColorForGradient(prop['chart.colors'][i]);}
90
- if(!RG.is_null(prop['chart.key.colors'])){for(var i=0;i<prop['chart.key.colors'].length;++i){prop['chart.key.colors'][i]=this.parseSingleColorForGradient(prop['chart.key.colors'][i]);}}
91
- prop['chart.highlight.fill']=this.parseSingleColorForGradient(prop['chart.highlight.fill']);prop['chart.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.highlight.stroke']);prop['chart.segment.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.segment.highlight.stroke']);prop['chart.segment.highlight.fill']=this.parseSingleColorForGradient(prop['chart.segment.highlight.fill']);};this.reset=function()
92
- {};this.parseSingleColorForGradient=function(color)
93
- {if(!color||typeof(color)!='string'){return color;}
94
- if(color.match(/^gradient\((.*)\)$/i)){if(color.match(/^gradient\(({.*})\)$/i)){return RGraph.parseJSONGradient({object:this,def:RegExp.$1});}
95
- var parts=RegExp.$1.split(':');var grad=co.createRadialGradient(this.centerx,this.centery,0,this.centerx,this.centery,this.radius);var diff=1/(parts.length-1);grad.addColorStop(0,RG.trim(parts[0]));for(var j=1;j<parts.length;++j){grad.addColorStop(j*diff,RG.trim(parts[j]));}}
96
- return grad?grad:color;};this.interactiveKeyHighlight=function(index)
97
- {var segments=this.angles2;for(var i=0;i<this.angles2.length;i+=1){co.beginPath();co.lineWidth=2;co.fillStyle=prop['chart.key.interactive.highlight.chart.fill'];co.strokeStyle=prop['chart.key.interactive.highlight.chart.stroke'];co.arc(segments[i][index][4],segments[i][index][5],segments[i][index][2],segments[i][index][0],segments[i][index][1],false);co.arc(segments[i][index][4],segments[i][index][5],segments[i][index][3],segments[i][index][1],segments[i][index][0],true);co.closePath();co.fill();co.stroke();}
98
- return};this.on=function(type,func)
99
- {if(type.substr(0,2)!=='on'){type='on'+type;}
100
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
101
- return this;};this.firstDrawFunc=function()
102
- {};this.explode=function()
103
- {var obj=this;var opt=arguments[0]||{};var callback=arguments[1]||function(){};var frames=opt.frames?opt.frames:30;var frame=0;var explodedMax=ma.max(ca.width,ca.height);var exploded=Number(this.get('chart.exploded'));function iterator()
104
- {exploded=(frame/frames)*explodedMax;obj.Set('exploded',exploded);RG.clear(ca);RG.redrawCanvas(ca);if(frame++<frames){RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
105
- iterator();return this;};this.roundrobin=this.roundRobin=function()
106
- {var obj=this;var opt=arguments[0]||{}
107
- var frames=opt.frames||30;var frame=0;var original_margin=prop['chart.margin'];var margin=(360/this.data.length)/2;var callback=arguments[1]||function(){};this.Set('chart.margin',margin);this.Set('chart.animation.roundrobin.factor',0);function iterator()
108
- {RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);if(frame++<frames){obj.set('chart.animation.roundrobin.factor',frame/frames);obj.set('chart.margin',(frame/frames)*original_margin);RG.Effects.updateCanvas(iterator);}else{obj.set('chart.animation.roundrobin.factor',1);obj.set('chart.margin',original_margin);callback(obj);}}
109
- iterator();return this;};this.implode=function()
110
- {var obj=this;var opt=arguments[0]||{};var callback=arguments[1]||function(){};var frames=opt.frames||30;var frame=0;var explodedMax=ma.max(ca.width,ca.height);var exploded=explodedMax;function iterator()
111
- {exploded=explodedMax-((frame/frames)*explodedMax);obj.set('chart.exploded',exploded);RG.clear(ca);RG.redrawCanvas(ca);if(frame++<frames){RG.Effects.updateCanvas(iterator);}else{RG.clear(obj.canvas);RG.redrawCanvas(obj.canvas);callback(obj);}}
112
- iterator();return this;};this.grow=function()
113
- {var obj=this;var opt=arguments[0]||{};var callback=arguments[1]||function(){};var frames=opt.frames||30;var frame=0;function iterator()
114
- {obj.set('chart.animation.grow.multiplier',frame/frames);RG.clear(ca);RG.redrawCanvas(ca);if(frame<frames){frame++;RG.Effects.updateCanvas(iterator);}else{callback(obj);}}
115
- iterator();return this;};RG.register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};
12
+ RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
13
+ RGraph.Effects = RGraph.Effects || {};
14
+ RGraph.Effects.Rose = RGraph.Effects.Rose || {};
15
+
16
+ //
17
+ // The rose chart constuctor
18
+ //
19
+ RGraph.Rose = function (conf)
20
+ {
21
+ this.id = conf.id;
22
+ this.canvas = document.getElementById(this.id);
23
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
24
+ this.data = conf.data;
25
+ this.canvas.__object__ = this;
26
+ this.type = 'rose';
27
+ this.isRGraph = true;
28
+ this.isrgraph = true;
29
+ this.rgraph = true;
30
+ this.uid = RGraph.createUID();
31
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID();
32
+ this.colorsParsed = false;
33
+ this.coordsText = [];
34
+ this.original_colors = [];
35
+ this.firstDraw = true; // After the first draw this will be false
36
+ this.stopAnimationRequested = false;// Used to control the animations
37
+
38
+
39
+
40
+
41
+
42
+ this.centerx = 0;
43
+ this.centery = 0;
44
+ this.radius = 0;
45
+ this.max = 0;
46
+ this.angles = [];
47
+ this.angles2 = [];
48
+
49
+ this.properties =
50
+ {
51
+ axes: false,
52
+ axesColor: 'black',
53
+ axesLinewidth: 1,
54
+ axesTickmarks: true,
55
+
56
+ backgroundGrid: true,
57
+ backgroundGridColor: '#ccc',
58
+ backgroundGridSize: null,
59
+ backgroundGridRadialsCount: null,
60
+ backgroundGridRadialsOffset: 0,
61
+ backgroundGridCirclesCount: 5,
62
+ // [TODO] Need linewidth setting
63
+
64
+ centerx: null,
65
+ centery: null,
66
+ radius: null,
67
+
68
+ anglesStart: 0,
69
+
70
+ linewidth: 1,
71
+
72
+ colors: ['rgba(255,0,0,0.5)', 'rgba(255,255,0,0.5)', 'rgba(0,255,255,0.5)', 'rgb(0,255,0)', 'gray', 'blue', 'rgb(255,128,255)','green', 'pink', 'gray', 'aqua'],
73
+ colorsSequential: false,
74
+ colorsAlpha: null,
75
+ colorsStroke: 'rgba(0,0,0,0)',
76
+
77
+ margin: 5,
78
+ marginLeft: 35,
79
+ marginRight: 35,
80
+ marginTop: 35,
81
+ marginBottom: 35,
82
+
83
+ shadow: false,
84
+ shadowColor: '#aaa',
85
+ shadowOffsetx: 0,
86
+ shadowOffsety: 0,
87
+ shadowBlur: 15,
88
+
89
+ title: '',
90
+ titleBold: null,
91
+ titleFont: null,
92
+ titleSize: null,
93
+ titleItalic: null,
94
+ titleColor: null,
95
+ titleX: null,
96
+ titleY: null,
97
+ titleHalign: null,
98
+ titleValign: null,
99
+ titleOffsetx: 0,
100
+ titleOffsety: 0,
101
+ titleSubtitle: '',
102
+ titleSubtitleSize: null,
103
+ titleSubtitleColor: '#aaa',
104
+ titleSubtitleFont: null,
105
+ titleSubtitleBold: null,
106
+ titleSubtitleItalic: null,
107
+ titleSubtitleOffsetx: 0,
108
+ titleSubtitleOffsety: 0,
109
+
110
+ labels: null,
111
+ labelsFormattedDecimals: 0,
112
+ labelsFormattedPoint: '.',
113
+ labelsFormattedThousand: ',',
114
+ labelsFormattedUnitsPre: '',
115
+ labelsFormattedUnitsPost: '',
116
+ labelsColor: null,
117
+ labelsFont: null,
118
+ labelsSize: null,
119
+ labelsBold: null,
120
+ labelsItalic: null,
121
+ labelsPosition: 'center',
122
+ labelsBoxed: false,
123
+ labelsOffsetRadius: 0,
124
+ labelsAxes: 'n',
125
+ labelsAxesFont: null,
126
+ labelsAxesSize: null,
127
+ labelsAxesColor: null,
128
+ labelsAxesBold: null,
129
+ labelsAxesItalic: null,
130
+ labelsAxesCount: 5,
131
+ labelsAxesOffsetx: 0,
132
+ labelsAxesOffsety: 0,
133
+
134
+ textColor: 'black',
135
+ textFont: 'Arial, Verdana, sans-serif',
136
+ textSize: 12,
137
+ textBold: false,
138
+ textItalic: false,
139
+ textAccessible: false,
140
+ textAccessibleOverflow: 'visible',
141
+ textAccessiblePointerevents: false,
142
+ text: null,
143
+
144
+ key: null,
145
+ keyBackground: 'white',
146
+ keyPosition: 'graph',
147
+ keyHalign: 'right',
148
+ keyShadow: false,
149
+ keyShadowColor: '#666',
150
+ keyShadowBlur: 3,
151
+ keyShadowOffsetx: 2,
152
+ keyShadowOffsety: 2,
153
+ keyPositionGutterBoxed: false,
154
+ keyPositionX: null,
155
+ keyPositionY: null,
156
+ keyColorShape: 'square',
157
+ keyRounded: true,
158
+ keyLinewidth: 1,
159
+ keyColors: null,
160
+ keyInteractive: false,
161
+ keyInteractiveHighlightChartStroke: 'black',
162
+ keyInteractiveHighlightChartFill: 'rgba(255,255,255,0.7)',
163
+ keyInteractiveHighlightLabel: 'rgba(255,0,0,0.2)',
164
+ keyLabelsColor: null,
165
+ keyLabelsFont: null,
166
+ keyLabelsSize: null,
167
+ keyLabelsBold: null,
168
+ keyLabelsItalic: null,
169
+ keyLabelsOffsetx: 0,
170
+ keyLabelsOffsety: 0,
171
+ keyFormattedDecimals: 0,
172
+ keyFormattedPoint: '.',
173
+ keyFormattedThousand: ',',
174
+ keyFormattedUnitsPre: '',
175
+ keyFormattedUnitsPost: '',
176
+ keyFormattedValueSpecific: null,
177
+ keyFormattedItemsCount: null,
178
+
179
+ contextmenu: null,
180
+
181
+ tooltips: null,
182
+ tooltipsEvent: 'onclick',
183
+ tooltipsEffect: 'slide',
184
+ tooltipsCssClass: 'RGraph_tooltip',
185
+ tooltipsCss: null,
186
+ tooltipsHighlight: true,
187
+ tooltipsFormattedThousand: ',',
188
+ tooltipsFormattedPoint: '.',
189
+ tooltipsFormattedDecimals: 0,
190
+ tooltipsFormattedUnitsPre: '',
191
+ tooltipsFormattedUnitsPost: '',
192
+ tooltipsFormattedKeyColors: null,
193
+ tooltipsFormattedKeyColorsShape: 'square',
194
+ tooltipsFormattedKeyLabels: [],
195
+ tooltipsFormattedListType: 'ul',
196
+ tooltipsFormattedListItems: null,
197
+ tooltipsFormattedTableHeaders: null,
198
+ tooltipsFormattedTableData: null,
199
+ tooltipsPointer: true,
200
+ tooltipsPointerOffsetx: 0,
201
+ tooltipsPointerOffsety: 0,
202
+ tooltipsPositionStatic: true,
203
+ tooltipsHotspotIgnore: null,
204
+
205
+ highlightStroke: 'rgba(0,0,0,0)',
206
+ highlightFill: 'rgba(255,255,255,0.7)',
207
+
208
+ annotatable: false,
209
+ annotatableColor: 'black',
210
+ annotatableLinewidth: 1,
211
+
212
+ resizable: false,
213
+ resizableHandleAdjust: [0,0],
214
+ resizableHandleBackground: null,
215
+
216
+ adjustable: false,
217
+
218
+ scaleMax: null,
219
+ scaleMin: 0,
220
+ scaleDecimals: null,
221
+ scalePoint: '.',
222
+ scaleThousand: ',',
223
+ scaleUnitsPre: '',
224
+ scaleUnitsPost: '',
225
+
226
+ variant: 'stacked',
227
+ variantThreedDepth: 10,
228
+
229
+ exploded: 0,
230
+
231
+ animationRoundrobinFactor: 1,
232
+ animationRoundrobinRadius: true,
233
+ animationGrowMultiplier: 1,
234
+
235
+ segmentHighlight: false,
236
+ segmentHighlightCount: null,
237
+ segmentHighlightFill: 'rgba(0,255,0,0.5)',
238
+ segmentHighlightStroke: 'rgba(0,0,0,0)',
239
+
240
+ clearto: 'rgba(0,0,0,0)'
241
+ }
242
+
243
+
244
+
245
+ // Go through the data converting it to numbers
246
+ this.data = RGraph.stringsToNumbers(this.data);
247
+
248
+
249
+
250
+
251
+
252
+ //
253
+ // Create the $ objects. In the case of non-equi-angular rose charts it actually creates too many $ objects,
254
+ // but it doesn't matter.
255
+ //
256
+ var linear_data = RGraph.arrayLinearize(this.data);
257
+ this.data_seq = linear_data; // Add .data_seq
258
+ this.data_arr = linear_data; // Add .data_arr
259
+ for (var i=0; i<linear_data.length; ++i) {
260
+ this["$" + i] = {};
261
+ }
262
+
263
+
264
+
265
+
266
+ // Easy access to properties and the path function
267
+ var properties = this.properties;
268
+ this.path = RGraph.pathObjectFunction;
269
+
270
+
271
+
272
+ //
273
+ // "Decorate" the object with the generic effects if the effects library has been included
274
+ //
275
+ if (RGraph.Effects && typeof RGraph.Effects.decorate === 'function') {
276
+ RGraph.Effects.decorate(this);
277
+ }
278
+
279
+
280
+
281
+ // Add the responsive method. This method resides in the common file.
282
+ this.responsive = RGraph.responsive;
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+ //
292
+ // A simple setter
293
+ this.set = function (name)
294
+ {
295
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
296
+
297
+ // BC
298
+ if (name === 'labelsOffset') {
299
+ name = 'labelsOffsetRadius';
300
+ }
301
+
302
+ // the number of arguments is only one and it's an
303
+ // object - parse it for configuration data and return.
304
+ if (arguments.length === 1 && typeof arguments[0] === 'object') {
305
+ for (i in arguments[0]) {
306
+ if (typeof i === 'string') {
307
+ this.set(i, arguments[0][i]);
308
+ }
309
+ }
310
+
311
+ return this;
312
+ }
313
+
314
+ properties[name] = value;
315
+
316
+ return this;
317
+ };
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+ //
327
+ // A simple getter
328
+ //
329
+ // @param string name The name of the property to get
330
+ //
331
+ this.get = function (name)
332
+ {
333
+ // BC
334
+ if (name === 'labelsOffset') {
335
+ name = 'labelsOffsetRadius';
336
+ }
337
+
338
+ return properties[name];
339
+ };
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+ //
349
+ // This method draws the rose chart
350
+ //
351
+ this.draw = function ()
352
+ {
353
+ //
354
+ // Fire the onbeforedraw event
355
+ //
356
+ RGraph.fireCustomEvent(this, 'onbeforedraw');
357
+
358
+
359
+
360
+ // Translate half a pixel for antialiasing purposes - but only if it hasn't been
361
+ // done already
362
+ //
363
+ // MUST be the first thing done!
364
+ //
365
+ if (!this.canvas.__rgraph_aa_translated__) {
366
+ this.context.translate(0.5,0.5);
367
+
368
+ this.canvas.__rgraph_aa_translated__ = true;
369
+ }
370
+
371
+
372
+ //
373
+ // Make the margins easy ro access
374
+ //
375
+ this.marginLeft = properties.marginLeft;
376
+ this.marginRight = properties.marginRight;
377
+ this.marginTop = properties.marginTop;
378
+ this.marginBottom = properties.marginBottom;
379
+
380
+ // Calculate the radius
381
+ this.radius = (Math.min(this.canvas.width - this.marginLeft - this.marginRight, this.canvas.height - this.marginTop - this.marginBottom) / 2);
382
+ this.centerx = ((this.canvas.width - this.marginLeft - this.marginRight) / 2) + this.marginLeft;
383
+ this.centery = ((this.canvas.height - this.marginTop - this.marginBottom) / 2) + this.marginTop;
384
+ this.angles = [];
385
+ this.angles2 = [];
386
+ this.total = 0;
387
+ this.startRadians = properties.anglesStart;
388
+ this.coordsText = [];
389
+
390
+ //
391
+ // Change the centerx marginally if the key is defined
392
+ //
393
+ if (properties.key && properties.key.length > 0 && properties.key.length >= 3) {
394
+ this.centerx = this.centerx - this.marginRight + 5;
395
+ }
396
+
397
+
398
+
399
+ // User specified radius, centerx and centery
400
+ if (typeof properties.centerx == 'number') this.centerx = properties.centerx;
401
+ if (typeof properties.centery == 'number') this.centery = properties.centery;
402
+ if (typeof properties.radius == 'number') this.radius = properties.radius;
403
+
404
+ //
405
+ // Parse the colors for gradients. Its down here so that the center X/Y can be used
406
+ //
407
+ if (!this.colorsParsed) {
408
+
409
+ this.parseColors();
410
+
411
+ // Don't want to do this again
412
+ this.colorsParsed = true;
413
+ }
414
+
415
+
416
+
417
+ // 3D variant
418
+ if (properties.variant.indexOf('3d') !== -1) {
419
+
420
+ var scaleX = 1.5;
421
+
422
+ this.context.setTransform(
423
+ scaleX,
424
+ 0,
425
+ 0,
426
+ 1,
427
+ (this.canvas.width * scaleX - this.canvas.width) * -0.5,
428
+ 0
429
+ );
430
+ }
431
+
432
+
433
+
434
+
435
+
436
+ this.drawBackground();
437
+
438
+
439
+
440
+
441
+
442
+ // If a 3D variant draw the depth
443
+ if (properties.variant.indexOf('3d') !== -1) {
444
+
445
+ // Setting the shadow here means that the first (the bottom Rose)
446
+ // sill have a shadow but not upper iterations.
447
+ RGraph.setShadow(this,'rgba(0,0,0,0.35)',0,15,25);
448
+
449
+ for (var i=properties.variantThreedDepth; i>0; i-=1) {
450
+
451
+ this.centery -= 1;
452
+
453
+ this.drawRose({storeAngles: false});
454
+
455
+ //RGraph.setShadow(this,'rgba(0,0,0,0)',0,0,0);
456
+ RGraph.noShadow(this);
457
+
458
+
459
+ // Make the segments darker
460
+ for (var j=0,len=this.angles.length; j<len; j+=1) {
461
+
462
+ var a = this.angles[j];
463
+
464
+ this.path(
465
+ 'b m % % a % % % % % false c f rgba(0,0,0,0.1) c f rgba(0,0,0,0.1)',
466
+ a[4], a[5],
467
+ a[4], a[5], a[3] + 1.5, a[0] - 0.01, a[1] + 0.01, false
468
+ );
469
+ }
470
+ }
471
+ }
472
+
473
+ this.drawRose();
474
+ this.drawLabels();
475
+
476
+ //
477
+ // Set the strokestyle to transparent because of a strange double stroke bug
478
+ //
479
+ // DO NOT REMOVE
480
+ //
481
+ this.context.strokeStyle = 'rgba(0,0,0,0)'
482
+
483
+
484
+ //
485
+ // Setup the context menu if required
486
+ //
487
+ if (properties.contextmenu) {
488
+ RGraph.showContext(this);
489
+ }
490
+
491
+
492
+ //
493
+ // This function enables adjusting
494
+ //
495
+ if (properties.adjustable) {
496
+ RGraph.allowAdjusting(this);
497
+ }
498
+
499
+
500
+
501
+
502
+ //
503
+ // Add custom text thats specified
504
+ //
505
+ RGraph.addCustomText(this);
506
+
507
+
508
+
509
+
510
+
511
+
512
+
513
+
514
+ //
515
+ // This installs the event listeners
516
+ //
517
+ RGraph.installEventListeners(this);
518
+
519
+
520
+
521
+
522
+
523
+ //
524
+ // Allow the segments to be highlighted
525
+ //
526
+ if (properties.segmentHighlight) {
527
+
528
+ // Check to see if the dynamic library has been included
529
+ if (!RGraph.allowSegmentHighlight) {
530
+ alert('[WARNING] The segment highlight function does not exist - have you included the dynamic library?');
531
+ }
532
+
533
+ RGraph.allowSegmentHighlight({
534
+ object: this,
535
+ count: typeof properties.segmentHighlightCount === 'number' ? properties.segmentHighlightCount : this.data.length,
536
+ fill: properties.segmentHighlightFill,
537
+ stroke: properties.segmentHighlightStroke
538
+ });
539
+ }
540
+
541
+
542
+
543
+ //
544
+ // Fire the onfirstdraw event
545
+ //
546
+ if (this.firstDraw) {
547
+ this.firstDraw = false;
548
+ RGraph.fireCustomEvent(this, 'onfirstdraw');
549
+ this.firstDrawFunc();
550
+ }
551
+
552
+
553
+
554
+ //
555
+ // Fire the RGraph draw event
556
+ //
557
+ RGraph.fireCustomEvent(this, 'ondraw');
558
+
559
+
560
+
561
+
562
+
563
+
564
+
565
+
566
+
567
+ //
568
+ // Install any inline responsive configuration. This
569
+ // should be last in the draw function - even after
570
+ // the draw events.
571
+ //
572
+ RGraph.installInlineResponsive(this);
573
+
574
+
575
+
576
+
577
+
578
+
579
+
580
+
581
+
582
+
583
+
584
+
585
+
586
+ return this;
587
+ };
588
+
589
+
590
+
591
+
592
+
593
+
594
+
595
+
596
+ //
597
+ // Used in chaining. Runs a function there and then - not waiting for
598
+ // the events to fire (eg the onbeforedraw event)
599
+ //
600
+ // @param function func The function to execute
601
+ //
602
+ this.exec = function (func)
603
+ {
604
+ func(this);
605
+
606
+ return this;
607
+ };
608
+
609
+
610
+
611
+
612
+
613
+
614
+
615
+
616
+ //
617
+ // This method draws the rose charts background
618
+ //
619
+ this.drawBackground = function ()
620
+ {
621
+ this.context.lineWidth = 1;
622
+
623
+
624
+ // Draw the background grey circles/spokes
625
+ if (properties.backgroundGridCirclesCount) {
626
+
627
+ if (typeof properties.backgroundGridCirclesCount == 'number') {
628
+ properties.backgroundGridCirclesSize = this.radius / properties.backgroundGridCirclesCount;
629
+ }
630
+
631
+ this.context.beginPath();
632
+ this.context.strokeStyle = properties.backgroundGridColor;
633
+
634
+ // Radius must be greater than 0 for Opera to work
635
+ for (var i=properties.backgroundGridCirclesSize; i<=this.radius; i+=properties.backgroundGridCirclesSize) {
636
+
637
+ // Hmmm... This is questionable
638
+ this.context.moveTo(this.centerx + i, this.centery);
639
+
640
+ // Radius must be greater than 0 for Opera to work
641
+ this.context.arc(
642
+ this.centerx,
643
+ this.centery,
644
+ i,
645
+ 0,
646
+ RGraph.TWOPI,
647
+ false
648
+ );
649
+ }
650
+ this.context.stroke();
651
+
652
+
653
+
654
+
655
+
656
+
657
+ // Draw the background lines that go from the center outwards
658
+ this.context.beginPath();
659
+ if (typeof properties.backgroundGridRadialsCount !== 'number') {
660
+ properties.backgroundGridRadialsCount = this.data.length
661
+ }
662
+
663
+ if (properties.backgroundGridRadialsCount > 0) {
664
+
665
+ var num = (360 / properties.backgroundGridRadialsCount);
666
+ var offset = properties.backgroundGridRadialsOffset;
667
+
668
+ for (var i=0; i<=360; i+=num) {
669
+
670
+ // Radius must be greater than 0 for Opera to work
671
+ this.context.arc(
672
+ this.centerx,
673
+ this.centery,
674
+ this.radius,
675
+ ((i / (180 / RGraph.PI)) - RGraph.HALFPI) + this.startRadians + offset,
676
+ (((i + 0.0001) / (180 / RGraph.PI)) - RGraph.HALFPI) + this.startRadians + offset,
677
+ false
678
+ );
679
+
680
+ this.context.lineTo(this.centerx, this.centery);
681
+ }
682
+ this.context.stroke();
683
+ }
684
+ }
685
+
686
+
687
+
688
+ if (properties.axes) {
689
+
690
+ this.context.beginPath();
691
+ this.context.strokeStyle = properties.axesColor;
692
+ this.context.lineWidth = properties.axesLinewidth;
693
+
694
+ // Draw the X axis
695
+ this.context.moveTo(this.centerx - this.radius, Math.round(this.centery) );
696
+ this.context.lineTo(this.centerx + this.radius, Math.round(this.centery) );
697
+
698
+ if (properties.axesTickmarks) {
699
+ // Draw the X ends
700
+ this.context.moveTo(Math.round(this.centerx - this.radius), this.centery - 5);
701
+ this.context.lineTo(Math.round(this.centerx - this.radius), this.centery + 5);
702
+ this.context.moveTo(Math.round(this.centerx + this.radius), this.centery - 5);
703
+ this.context.lineTo(Math.round(this.centerx + this.radius), this.centery + 5);
704
+
705
+ // Draw the X check marks
706
+ for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=(this.radius / 5)) {
707
+ this.context.moveTo(Math.round(i), this.centery - 3);
708
+ this.context.lineTo(Math.round(i), this.centery + 3.5);
709
+ }
710
+
711
+ // Draw the Y check marks
712
+ for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=(this.radius / 5)) {
713
+ this.context.moveTo(this.centerx - 3, Math.round(i));
714
+ this.context.lineTo(this.centerx + 3, Math.round(i));
715
+ }
716
+ }
717
+
718
+ // Draw the Y axis
719
+ this.context.moveTo(Math.round(this.centerx), this.centery - this.radius);
720
+ this.context.lineTo(Math.round(this.centerx), this.centery + this.radius);
721
+
722
+ if (properties.axesTickmarks) {
723
+ // Draw the Y ends
724
+ this.context.moveTo(this.centerx - 5, Math.round(this.centery - this.radius));
725
+ this.context.lineTo(this.centerx + 5, Math.round(this.centery - this.radius));
726
+
727
+ this.context.moveTo(this.centerx - 5, Math.round(this.centery + this.radius));
728
+ this.context.lineTo(this.centerx + 5, Math.round(this.centery + this.radius));
729
+ }
730
+
731
+ // Stroke it
732
+ this.context.closePath();
733
+ this.context.stroke();
734
+ }
735
+
736
+ this.path('b c');
737
+ };
738
+
739
+
740
+
741
+
742
+
743
+
744
+
745
+
746
+ //
747
+ // This method draws the data on the graph
748
+ //
749
+ this.drawRose = function ()
750
+ {
751
+ var max = 0,
752
+ data = this.data,
753
+ margin = RGraph.toRadians(properties.margin),
754
+ opt = arguments[0] || {};
755
+
756
+ this.context.lineWidth = properties.linewidth;
757
+
758
+ // Work out the maximum value and the sum
759
+ if (RGraph.isNull(properties.scaleMax)) {
760
+
761
+ // Work out the max
762
+ for (var i=0; i<data.length; ++i) {
763
+ if (typeof data[i] == 'number') {
764
+ max = Math.max(max, data[i]);
765
+ } else if (typeof data[i] == 'object' && properties.variant.indexOf('non-equi-angular') !== -1) {
766
+ max = Math.max(max, data[i][0]);
767
+
768
+ // Fallback is stacked
769
+ } else {
770
+ max = Math.max(max, RGraph.arraySum(data[i]));
771
+ }
772
+ }
773
+
774
+ this.scale2 = RGraph.getScale({object: this, options: {
775
+ 'scale.max':max,
776
+ 'scale.min':0,
777
+ 'scale.thousand': properties.scaleThousand,
778
+ 'scale.point': properties.scalePoint,
779
+ 'scale.decimals': properties.scaleDecimals,
780
+ 'scale.labels.count': properties.labelsAxesCount,
781
+ 'scale.round': properties.scaleRound,
782
+ 'scale.units.pre': properties.scaleUnitsPre,
783
+ 'scale.units.post': properties.scaleUnitsPost
784
+ }});
785
+ this.max = this.scale2.max;
786
+
787
+ } else {
788
+
789
+ var ymax = properties.scaleMax;
790
+
791
+
792
+
793
+ this.scale2 = RGraph.getScale({object: this, options: {
794
+ 'scale.max': ymax,
795
+ 'scale.strict': true,
796
+ 'scale.thousand': properties.scaleThousand,
797
+ 'scale.point': properties.scalePoint,
798
+ 'scale.decimals': properties.scaleDecimals,
799
+ 'scale.labels.count': properties.labelsAxesCount,
800
+ 'scale.round': properties.scaleRound,
801
+ 'scale.units.pre': properties.scaleUnitsPre,
802
+ 'scale.units.post': properties.scaleUnitsPost
803
+ }});
804
+ this.max = this.scale2.max
805
+ }
806
+
807
+ this.sum = RGraph.arraySum(data);
808
+
809
+ // Move to the centre
810
+ this.context.moveTo(this.centerx, this.centery);
811
+
812
+ this.context.stroke(); // Stroke the background so it stays grey
813
+
814
+ // Transparency
815
+ if (properties.colorsAlpha) {
816
+ this.context.globalAlpha = properties.colorsAlpha;
817
+ }
818
+
819
+ var sequentialIndex = 0;
820
+
821
+ //
822
+ // A non-equi-angular Rose chart
823
+ //
824
+ if (typeof properties.variant == 'string' && properties.variant.indexOf('non-equi-angular') !== -1) {
825
+
826
+ var total=0;
827
+ for (var i=0; i<data.length; ++i) {
828
+ total += data[i][1];
829
+ }
830
+
831
+ if (properties.shadow) {
832
+ RGraph.setShadow(
833
+ this,
834
+ properties.shadowColor,
835
+ properties.shadowOffsetx,
836
+ properties.shadowOffsety,
837
+ properties.shadowBlur
838
+ );
839
+ }
840
+
841
+ for (var i=0; i<this.data.length; ++i) {
842
+
843
+ var segmentRadians = ((this.data[i][1] / total) * RGraph.TWOPI);
844
+ var radius = ((this.data[i][0] - properties.scaleMin) / (this.max - properties.scaleMin)) * this.radius;
845
+ radius = radius * properties.animationGrowMultiplier;
846
+
847
+ this.context.strokeStyle = properties.colorsStroke;
848
+ this.context.fillStyle = properties.colors[0];
849
+
850
+ if (properties.colorsSequential) {
851
+ this.context.fillStyle = properties.colors[i];
852
+ }
853
+
854
+ this.context.beginPath(); // Begin the segment
855
+
856
+ var startAngle = (this.startRadians * properties.animationRoundrobinFactor) - RGraph.HALFPI + margin;
857
+ var endAngle = ((this.startRadians + segmentRadians) * properties.animationRoundrobinFactor) - RGraph.HALFPI - margin;
858
+
859
+ var exploded = this.getExploded(i, startAngle, endAngle, properties.exploded);
860
+ var explodedX = exploded[0];
861
+ var explodedY = exploded[1];
862
+
863
+
864
+ this.context.arc(
865
+ this.centerx + explodedX,
866
+ this.centery + explodedY,
867
+ properties.animationRoundrobinRadius ? radius * properties.animationRoundrobinFactor : radius,
868
+ startAngle,
869
+ endAngle,
870
+ 0
871
+ );
872
+ this.context.lineTo(this.centerx + explodedX, this.centery + explodedY);
873
+ this.context.closePath(); // End the segment
874
+
875
+ this.context.stroke();
876
+ this.context.fill();
877
+
878
+ // Store the start and end angles
879
+
880
+ this.angles[i] = [
881
+ startAngle,
882
+ endAngle,
883
+ 0,
884
+ properties.animationRoundrobinRadius ? radius * properties.animationRoundrobinFactor : radius,
885
+ this.centerx + explodedX,
886
+ this.centery + explodedY,
887
+ this.context.strokeStyle,
888
+ this.context.fillStyle
889
+ ];
890
+
891
+ sequentialIndex++;
892
+ this.startRadians += segmentRadians;
893
+ }
894
+
895
+ // Turn the shadow off if it's enabled and redraw the chart
896
+ if (properties.shadow) {
897
+ RGraph.noShadow(this);
898
+ this.redrawRose();
899
+ }
900
+
901
+ //
902
+ // Now redraw the rose if the linewidth is larger than 2 so that the
903
+ // fills appear under the strokes
904
+ //
905
+ if (properties.linewidth > 1) {
906
+ this.restrokeRose();
907
+ }
908
+
909
+ } else {
910
+
911
+ var sequentialColorIndex = 0;
912
+
913
+ if (properties.shadow) {
914
+ RGraph.setShadow(
915
+ this,
916
+ properties.shadowColor,
917
+ properties.shadowOffsetx,
918
+ properties.shadowOffsety,
919
+ properties.shadowBlur
920
+ );
921
+ }
922
+
923
+ //
924
+ // Draw regular segments here
925
+ //
926
+ for (var i=0; i<this.data.length; ++i) {
927
+
928
+ var segmentRadians = (1 / this.data.length) * RGraph.TWOPI;
929
+
930
+ if (typeof this.data[i] == 'number') {
931
+ this.context.beginPath(); // Begin the segment
932
+
933
+ this.context.strokeStyle = properties.colorsStroke;
934
+ this.context.fillStyle = properties.colors[0];
935
+
936
+ //
937
+ // This allows sequential colors
938
+ //
939
+ if (properties.colorsSequential) {
940
+ this.context.fillStyle = properties.colors[i];
941
+ }
942
+
943
+ var radius = ((this.data[i] - properties.scaleMin) / (this.max - properties.scaleMin)) * this.radius;
944
+ radius = radius * properties.animationGrowMultiplier;
945
+
946
+ var startAngle = (this.startRadians * properties.animationRoundrobinFactor) - RGraph.HALFPI + margin;
947
+ var endAngle = (this.startRadians * properties.animationRoundrobinFactor) + (segmentRadians * properties.animationRoundrobinFactor) - RGraph.HALFPI - margin;
948
+
949
+ var exploded = this.getExploded(i, startAngle, endAngle, properties.exploded);
950
+ var explodedX = exploded[0];
951
+ var explodedY = exploded[1];
952
+
953
+ this.context.arc(
954
+ this.centerx + explodedX,
955
+ this.centery + explodedY,
956
+ properties.animationRoundrobinRadius ? radius * properties.animationRoundrobinFactor : radius,
957
+ startAngle,
958
+ endAngle,
959
+ 0
960
+ );
961
+ this.context.lineTo(this.centerx + explodedX, this.centery + explodedY);
962
+ this.context.closePath(); // End the segment
963
+ this.context.fill();
964
+ this.context.stroke();
965
+
966
+ // This skirts a double-stroke bug
967
+ this.context.beginPath();
968
+
969
+ if (endAngle == 0) {
970
+ //endAngle = RGraph.TWOPI;
971
+ }
972
+
973
+ // Store the start and end angles
974
+ this.angles[i] = [
975
+ startAngle,
976
+ endAngle,
977
+ 0,
978
+ radius * properties.animationRoundrobinFactor,
979
+ this.centerx + explodedX,
980
+ this.centery + explodedY,
981
+ this.context.strokeStyle,
982
+ this.context.fillStyle
983
+ ];
984
+
985
+ sequentialIndex++;
986
+
987
+ //
988
+ // Draw a stacked segment
989
+ //
990
+ } else if (typeof this.data[i] == 'object') {
991
+
992
+ var margin = properties.margin / (180 / RGraph.PI);
993
+
994
+
995
+ // Initialise the angles2 array
996
+ if (!this.angles2[i]) {
997
+ this.angles2[i] = [];
998
+ }
999
+
1000
+
1001
+ for (var j=0; j<this.data[i].length; ++j) {
1002
+
1003
+ var startAngle = (this.startRadians * properties.animationRoundrobinFactor) - RGraph.HALFPI + margin;
1004
+ var endAngle = (this.startRadians * properties.animationRoundrobinFactor)+ (segmentRadians * properties.animationRoundrobinFactor) - RGraph.HALFPI - margin;
1005
+
1006
+ var exploded = this.getExploded(i, startAngle, endAngle, properties.exploded);
1007
+ var explodedX = exploded[0];
1008
+ var explodedY = exploded[1];
1009
+
1010
+ this.context.strokeStyle = properties.colorsStroke;
1011
+ this.context.fillStyle = properties.colors[j];
1012
+
1013
+ // This facilitates sequential color support
1014
+ if (properties.colorsSequential) {
1015
+ this.context.fillStyle = properties.colors[sequentialColorIndex++];
1016
+ }
1017
+
1018
+ if (j == 0) {
1019
+ this.context.beginPath(); // Begin the segment
1020
+ var startRadius = 0;
1021
+ var endRadius = ((this.data[i][j] - properties.scaleMin) / (this.max - properties.scaleMin)) * this.radius;
1022
+ endRadius = endRadius * properties.animationGrowMultiplier;
1023
+
1024
+ this.context.arc(this.centerx + explodedX,
1025
+ this.centery + explodedY,
1026
+ properties.animationRoundrobinRadius ? endRadius * properties.animationRoundrobinFactor : endRadius,
1027
+ startAngle,
1028
+ endAngle,
1029
+ 0);
1030
+ this.context.lineTo(this.centerx + explodedX, this.centery + explodedY);
1031
+ this.context.closePath(); // End the segment
1032
+ this.context.stroke();
1033
+ this.context.fill();
1034
+
1035
+ this.angles[sequentialIndex++] = [
1036
+ startAngle,
1037
+ endAngle,
1038
+ 0,
1039
+ endRadius * properties.animationRoundrobinFactor,
1040
+ this.centerx + explodedX,
1041
+ this.centery + explodedY,
1042
+ this.context.strokeStyle,
1043
+ this.context.fillStyle
1044
+ ];
1045
+
1046
+ this.angles2[i][j] = [
1047
+ startAngle,
1048
+ endAngle,
1049
+ 0,
1050
+ endRadius * properties.animationRoundrobinFactor,
1051
+ this.centerx + explodedX,
1052
+ this.centery + explodedY,
1053
+ this.context.strokeStyle,
1054
+ this.context.fillStyle
1055
+ ];
1056
+
1057
+ } else {
1058
+
1059
+ this.context.beginPath(); // Begin the segment
1060
+
1061
+ var startRadius = endRadius; // This comes from the prior iteration of this loop
1062
+ var endRadius = (((this.data[i][j] - properties.scaleMin) / (this.max - properties.scaleMin)) * this.radius) + startRadius;
1063
+ endRadius = endRadius * properties.animationGrowMultiplier;
1064
+
1065
+ this.context.arc(this.centerx + explodedX,
1066
+ this.centery + explodedY,
1067
+ startRadius * properties.animationRoundrobinFactor,
1068
+ startAngle,
1069
+ endAngle,
1070
+ 0);
1071
+
1072
+ this.context.arc(this.centerx + explodedX,
1073
+ this.centery + explodedY,
1074
+ endRadius * properties.animationRoundrobinFactor,
1075
+ endAngle,
1076
+ startAngle,
1077
+ true);
1078
+
1079
+ this.context.closePath(); // End the segment
1080
+ this.context.stroke();
1081
+ this.context.fill();
1082
+
1083
+
1084
+ this.angles[sequentialIndex++] = [
1085
+ startAngle,
1086
+ endAngle,
1087
+ startRadius * properties.animationRoundrobinFactor,
1088
+ endRadius * properties.animationRoundrobinFactor,
1089
+ this.centerx + explodedX,
1090
+ this.centery + explodedY,
1091
+ this.context.strokeStyle,
1092
+ this.context.fillStyle
1093
+ ];
1094
+
1095
+ this.angles2[i][j] = [
1096
+ startAngle,
1097
+ endAngle,
1098
+ startRadius * properties.animationRoundrobinFactor,
1099
+ endRadius * properties.animationRoundrobinFactor,
1100
+ this.centerx + explodedX,
1101
+ this.centery + explodedY,
1102
+ this.context.strokeStyle,
1103
+ this.context.fillStyle
1104
+ ];
1105
+ }
1106
+ }
1107
+ }
1108
+
1109
+ this.startRadians += segmentRadians;
1110
+
1111
+ }
1112
+
1113
+
1114
+ if (properties.shadow) {
1115
+ RGraph.noShadow(this);
1116
+ }
1117
+
1118
+
1119
+
1120
+ //
1121
+ // Now redraw the rose if the shadow is enabled so that
1122
+ // the rose appears over the shadow
1123
+ //
1124
+ if (properties.shadow) {
1125
+ this.redrawRose();
1126
+ }
1127
+
1128
+
1129
+ //
1130
+ // Now redraw the rose if the linewidth is larger than 2 so that the
1131
+ // fills appear under the strokes
1132
+ //
1133
+ if (properties.linewidth > 1) {
1134
+ this.restrokeRose();
1135
+ }
1136
+
1137
+
1138
+ //
1139
+ // Now redraw the rose if the shadow is enabled so that
1140
+ // the rose appears over the shadow
1141
+ //
1142
+ if (properties.shadow) {
1143
+ this.redrawRose();
1144
+ }
1145
+ }
1146
+
1147
+ // Turn off the transparency
1148
+ if (properties.colorsAlpha) {
1149
+ this.context.globalAlpha = 1;
1150
+ }
1151
+
1152
+ // Draw the title if any has been set
1153
+ if (properties.title) {
1154
+ RGraph.drawTitle(this);
1155
+ }
1156
+ };
1157
+
1158
+
1159
+
1160
+
1161
+
1162
+
1163
+
1164
+
1165
+ //
1166
+ // This function redraws the stroke on the chart so that
1167
+ // the strokes appear above the fill
1168
+ //
1169
+ this.restrokeRose = function ()
1170
+ {
1171
+ var angles = this.angles;
1172
+
1173
+ for (var i=0; i<angles.length; ++i) {
1174
+ this.path(
1175
+ 'b a % % % % % false a % % % % % true c s %',
1176
+ angles[i][4], // x
1177
+ angles[i][5], // y
1178
+ angles[i][2], // radius
1179
+ angles[i][0], // start angle
1180
+ angles[i][1], // end angle
1181
+
1182
+ angles[i][4], // x
1183
+ angles[i][5], // y
1184
+ angles[i][3], // radius
1185
+ angles[i][1], // end angle
1186
+ angles[i][0], // start angle
1187
+ angles[i][6] // strokestyle
1188
+ );
1189
+ }
1190
+ };
1191
+
1192
+
1193
+
1194
+
1195
+
1196
+
1197
+
1198
+
1199
+ //
1200
+ // This function redraws the rose if the shadow is enabled so the it
1201
+ // appears above the shadow
1202
+ //
1203
+ this.redrawRose = function ()
1204
+ {
1205
+ var angles = this.angles;
1206
+
1207
+ for (var i=0; i<angles.length; ++i) {
1208
+
1209
+ this.path(
1210
+ 'b a % % % % % false a % % % % % true c f % f % ',
1211
+ angles[i][4], angles[i][5],angles[i][2],angles[i][0],angles[i][1],
1212
+ angles[i][4],angles[i][5],angles[i][3],angles[i][1],angles[i][0],
1213
+ angles[i][6],angles[i][7]
1214
+ );
1215
+ }
1216
+ };
1217
+
1218
+
1219
+
1220
+
1221
+
1222
+
1223
+
1224
+
1225
+ //
1226
+ // Unsuprisingly, draws the labels
1227
+ //
1228
+ this.drawLabels = function ()
1229
+ {
1230
+ if (properties.labels && properties.labels.length) {
1231
+ //
1232
+ // If the labels option is a string then turn it
1233
+ // into an array.
1234
+ //
1235
+
1236
+ if (typeof properties.labels === 'string') {
1237
+ properties.labels = RGraph.arrayPad({
1238
+ array: [],
1239
+ length: this.data.length,
1240
+ value: properties.labels
1241
+ });
1242
+ }
1243
+
1244
+ for (var i=0; i<properties.labels.length; ++i) {
1245
+ properties.labels[i] = RGraph.labelSubstitution({
1246
+ object: this,
1247
+ text: properties.labels[i],
1248
+ index: i,
1249
+ value: this.data[i],
1250
+ decimals: properties.labelsFormattedDecimals || 0,
1251
+ unitsPre: properties.labelsFormattedUnitsPre || '',
1252
+ unitsPost: properties.labelsFormattedUnitsPost || '',
1253
+ thousand: properties.labelsFormattedThousand || ',',
1254
+ point: properties.labelsFormattedPoint || '.'
1255
+ });
1256
+ }
1257
+ }
1258
+
1259
+
1260
+
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+
1269
+
1270
+ this.context.lineWidth = 1;
1271
+ var key = properties.key;
1272
+
1273
+ if (key && key.length) {
1274
+ RGraph.drawKey(this, key, properties.colors);
1275
+ }
1276
+
1277
+ // Set the color to black
1278
+ this.context.fillStyle = properties.textColor;
1279
+ this.context.strokeStyle = 'black';
1280
+
1281
+ var radius = this.radius,
1282
+ font = properties.textFont,
1283
+ size = properties.textSize,
1284
+ axes = properties.labelsAxes.toLowerCase(),
1285
+ decimals = properties.scaleDecimals,
1286
+ units_pre = properties.scaleUnitsPre,
1287
+ units_post = properties.scaleUnitsPost,
1288
+ centerx = this.centerx,
1289
+ centery = this.centery + (properties.variant.indexOf('3d') !== -1 ? properties.variantThreedDepth : 0);
1290
+
1291
+ // Draw any circular labels
1292
+ if (typeof properties.labels == 'object' && properties.labels) {
1293
+ this.drawCircularLabels(this.context, properties.labels, font, size, radius + 10);
1294
+ }
1295
+
1296
+
1297
+ // Size can be specified seperately for the scale now
1298
+ if (typeof properties.textSize == 'number') {
1299
+ size = properties.textSize;
1300
+ }
1301
+
1302
+
1303
+ var color = 'rgba(255,255,255,0.8)';
1304
+
1305
+ // The "North" axis labels
1306
+ if (axes.indexOf('n') > -1) {
1307
+
1308
+ // The offset for the labels
1309
+ if (properties.backgroundAxes) {
1310
+ var offset = -10;
1311
+ var halign = 'right';
1312
+ } else {
1313
+ var offset = 0;
1314
+ var halign = 'center';
1315
+ }
1316
+
1317
+ var textConf = RGraph.getTextConf({
1318
+ object: this,
1319
+ prefix: 'labelsAxes'
1320
+ });
1321
+
1322
+
1323
+
1324
+
1325
+ for (var i=0; i<properties.labelsAxesCount; ++i) {
1326
+ RGraph.text({
1327
+
1328
+ object: this,
1329
+
1330
+ font: textConf.font,
1331
+ size: textConf.size,
1332
+ color: textConf.color,
1333
+ bold: textConf.bold,
1334
+ italic: textConf.italic,
1335
+
1336
+ x: centerx + offset + properties.labelsAxesOffsetx,
1337
+ y: centery - (radius * ((i+1) / properties.labelsAxesCount)) + properties.labelsAxesOffsety,
1338
+
1339
+ text:this.scale2.labels[i],
1340
+
1341
+ valign:'center',
1342
+ halign: halign,
1343
+ bounding:true,
1344
+ boundingFill:color,
1345
+ boundingStroke: 'rgba(0,0,0,0)',
1346
+ tag: 'scale'
1347
+ });
1348
+ }
1349
+ }
1350
+
1351
+
1352
+
1353
+
1354
+ // The "South" axis labels
1355
+ if (axes.indexOf('s') > -1) {
1356
+
1357
+ // The offset for the labels
1358
+ if (properties.backgroundAxes) {
1359
+ var offset = -10;
1360
+ var halign = 'right';
1361
+ } else {
1362
+ var offset = 0;
1363
+ var halign = 'center';
1364
+ }
1365
+
1366
+ for (var i=0; i<properties.labelsAxesCount; ++i) {
1367
+ RGraph.text({
1368
+
1369
+ object: this,
1370
+
1371
+ font: textConf.font,
1372
+ size: textConf.size,
1373
+ color: textConf.color,
1374
+ bold: textConf.bold,
1375
+ italic: textConf.italic,
1376
+
1377
+ 'x':centerx + offset + properties.labelsAxesOffsetx,
1378
+ 'y':centery + (radius * ((i+1) / properties.labelsAxesCount)) + properties.labelsAxesOffsety,
1379
+
1380
+ 'text':this.scale2.labels[i],
1381
+ 'valign':'center',
1382
+ 'halign':halign,
1383
+ 'bounding':true,
1384
+ 'bounding.fill':color,
1385
+ 'bounding.stroke':'rgba(0,0,0,0)',
1386
+ 'tag': 'scale'
1387
+ });
1388
+ }
1389
+ }
1390
+
1391
+ // The "East" axis labels
1392
+ if (axes.indexOf('e') > -1) {
1393
+ for (var i=0; i<properties.labelsAxesCount; ++i) {
1394
+
1395
+ // The offset for the labels
1396
+ if (properties.backgroundAxes) {
1397
+ var offset = 10;
1398
+ var valign = 'top';
1399
+ } else {
1400
+ var offset = 0;
1401
+ var valign = 'center';
1402
+ }
1403
+
1404
+ RGraph.text({
1405
+
1406
+ object: this,
1407
+
1408
+ font: textConf.font,
1409
+ size: textConf.size,
1410
+ color: textConf.color,
1411
+ bold: textConf.bold,
1412
+ italic: textConf.italic,
1413
+
1414
+ 'x':centerx + (radius * ((i+1) / properties.labelsAxesCount)) + properties.labelsAxesOffsetx,
1415
+ 'y':centery + offset + properties.labelsAxesOffsety,
1416
+
1417
+ 'text':this.scale2.labels[i],
1418
+ 'valign':valign,
1419
+ 'halign':'center',
1420
+ 'bounding':true,
1421
+ 'bounding.fill':color,
1422
+ 'bounding.stroke':'rgba(0,0,0,0)',
1423
+ 'tag': 'scale'
1424
+ });
1425
+ }
1426
+ }
1427
+
1428
+ // The "West" axis labels
1429
+ if (axes.indexOf('w') > -1) {
1430
+ for (var i=0; i<properties.labelsAxesCount; ++i) {
1431
+
1432
+ // The offset for the labels
1433
+ if (properties.backgroundAxes) {
1434
+ var offset = 10;
1435
+ var valign = 'top';
1436
+ } else {
1437
+ var offset = 0;
1438
+ var valign = 'center';
1439
+ }
1440
+
1441
+ RGraph.text({
1442
+
1443
+ object: this,
1444
+
1445
+ font: textConf.font,
1446
+ size: textConf.size,
1447
+ color: textConf.color,
1448
+ bold: textConf.bold,
1449
+ italic: textConf.italic,
1450
+
1451
+ 'x':centerx - (radius * ((i+1) / properties.labelsAxesCount)) + properties.labelsAxesOffsetx,
1452
+ 'y':centery + offset + properties.labelsAxesOffsety,
1453
+
1454
+ 'text':this.scale2.labels[i],
1455
+ 'valign':valign,
1456
+ 'halign':'center',
1457
+ 'bounding':true,
1458
+ 'bounding.fill':color,
1459
+ 'bounding.stroke': 'rgba(0,0,0,0)',
1460
+ 'tag': 'scale'
1461
+ });
1462
+ }
1463
+ }
1464
+
1465
+ // Draw the minimum value
1466
+ if (RGraph.trim(axes).length > 0) {
1467
+ RGraph.text({
1468
+
1469
+ object: this,
1470
+
1471
+ font: textConf.font,
1472
+ size: textConf.size,
1473
+ color: textConf.color,
1474
+ bold: textConf.bold,
1475
+ italic: textConf.italic,
1476
+
1477
+ 'x':centerx + properties.labelsAxesOffsetx,
1478
+ 'y':centery + properties.labelsAxesOffsety,
1479
+ 'text':typeof properties.scaleMin === 'number' ?
1480
+ RGraph.numberFormat({
1481
+ object: this,
1482
+ number: Number(properties.scaleMin).toFixed(properties.scaleMin === 0 ? '0' : properties.scaleDecimals),
1483
+ unitspre: units_pre,
1484
+ unitspost: units_post
1485
+ }) : '0',
1486
+ 'valign':'center',
1487
+ 'halign':'center',
1488
+ 'bounding':true,
1489
+ 'bounding.fill':color,
1490
+ 'bounding.stroke':'rgba(0,0,0,0)',
1491
+ 'tag': 'scale'
1492
+ });
1493
+ }
1494
+ };
1495
+
1496
+
1497
+
1498
+
1499
+
1500
+
1501
+
1502
+
1503
+ //
1504
+ // Draws the circular labels that go around the charts
1505
+ //
1506
+ // @param labels array The labels that go around the chart
1507
+ //
1508
+ this.drawCircularLabels = function (context, labels, font, size, radius)
1509
+ {
1510
+ var variant = properties.variant,
1511
+ position = properties.labelsPosition,
1512
+ radius = radius + 5 + properties.labelsOffsetRadius,
1513
+ centerx = this.centerx,
1514
+ centery = this.centery + (properties.variant.indexOf('3d') !== -1 ? properties.variantThreedDepth : 0),
1515
+ labelsColor = properties.labelsColor || properties.textColor,
1516
+ angles = this.angles;
1517
+
1518
+
1519
+
1520
+ var textConf = RGraph.getTextConf({
1521
+ object: this,
1522
+ prefix: 'labels'
1523
+ });
1524
+
1525
+ for (var i=0; i<this.data.length; ++i) {
1526
+
1527
+ if (typeof variant == 'string' && variant.indexOf('non-equi-angular') !== -1) {
1528
+ var a = Number(angles[i][0]) + ((angles[i][1] - angles[i][0]) / 2);
1529
+ } else {
1530
+ var a = (RGraph.TWOPI / this.data.length) * (i + 1) - (RGraph.TWOPI / (this.data.length * 2));
1531
+ a = a - RGraph.HALFPI + (properties.labelsPosition == 'edge' ? ((RGraph.TWOPI / this.data.length) / 2) : 0);
1532
+
1533
+ // MJLR bug fix 21/04/2020 - label positions ignored anglesStart property
1534
+ a = a + properties.anglesStart;
1535
+ }
1536
+
1537
+ var x = centerx + (Math.cos(a) * radius);
1538
+ var y = centery + (Math.sin(a) * radius);
1539
+
1540
+ // Horizontal alignment
1541
+ if (x > centerx) {
1542
+ halign = 'left';
1543
+ } else if (Math.round(x) == centerx) {
1544
+ halign = 'center';
1545
+ } else {
1546
+ halign = 'right';
1547
+ }
1548
+
1549
+ RGraph.text({
1550
+
1551
+ object: this,
1552
+
1553
+ font: textConf.font,
1554
+ size: textConf.size,
1555
+ color: textConf.color,
1556
+ bold: textConf.bold,
1557
+ italic: textConf.italic,
1558
+
1559
+ x: x,
1560
+ y: y,
1561
+ text: String(labels[i] || ''),
1562
+ halign: halign,
1563
+ valign: 'center',
1564
+ tag: 'labels',
1565
+ cssClass: RGraph.getLabelsCSSClassName({
1566
+ object: this,
1567
+ name: 'labelsClass',
1568
+ index: i
1569
+ })
1570
+ });
1571
+ }
1572
+ };
1573
+
1574
+
1575
+
1576
+
1577
+
1578
+
1579
+
1580
+
1581
+ //
1582
+ // This function is for use with circular graph types, eg the Pie or Rose. Pass it your event object
1583
+ // and it will pass you back the corresponding segment details as an array:
1584
+ //
1585
+ // [x, y, r, startAngle, endAngle]
1586
+ //
1587
+ // Angles are measured in degrees, and are measured from the "east" axis (just like the canvas).
1588
+ //
1589
+ // @param object e Your event object
1590
+ // @param object Options (OPTIONAL):
1591
+ // radius - whether to take into account
1592
+ // the radius of the segment
1593
+ //
1594
+ this.getShape = function (e)
1595
+ {
1596
+ var angles = this.angles;
1597
+ var ret = [];
1598
+ var opt = arguments[1] ? arguments[1] : {radius: true};
1599
+
1600
+ //
1601
+ // Go through all of the angles checking each one
1602
+ //
1603
+ for (var i=0; i<angles.length ; ++i) {
1604
+
1605
+ if (RGraph.tooltipsHotspotIgnore(this, i)) {
1606
+ continue;
1607
+ }
1608
+
1609
+ var angleStart = angles[i][0],
1610
+ angleEnd = angles[i][1],
1611
+ radiusStart = opt.radius === false ? 0 : angles[i][2],
1612
+ radiusEnd = opt.radius === false ? this.radius : angles[i][3],
1613
+ centerX = angles[i][4],
1614
+ centerY = angles[i][5],// - (properties.variant.indexOf('3d') !== -1 ? properties.variantThreedDepth : 0);
1615
+ mouseXY = RGraph.getMouseXY(e),
1616
+ mouseX = mouseXY[0] - centerX,
1617
+ mouseY = mouseXY[1] - centerY;
1618
+
1619
+ // New click testing (the 0.01 is there because Opera doesn't like 0 as the radius)
1620
+ this.path(
1621
+ 'b a % % % % % % a % % % % % % c',
1622
+ centerX, centerY, radiusStart ? radiusStart : 0.01, angleStart, angleEnd, false,
1623
+ centerX, centerY, radiusEnd, angleEnd, angleStart, true
1624
+ );
1625
+
1626
+ // No stroke() or fill()
1627
+
1628
+
1629
+ if (this.context.isPointInPath(mouseXY[0], mouseXY[1])) {
1630
+
1631
+ angles[i][6] = i;
1632
+
1633
+ if (RGraph.parseTooltipText) {
1634
+ var tooltip = RGraph.parseTooltipText(properties.tooltips, angles[i][6]);
1635
+ }
1636
+
1637
+ var indexes = RGraph.sequentialIndexToGrouped(i, this.data);
1638
+
1639
+ // Add the textual keys which are used in the return value
1640
+ //
1641
+ angles[i].object = this;
1642
+ angles[i].x = angles[i][4];
1643
+ angles[i].y = angles[i][5];
1644
+ angles[i]['angle.start'] = angles[i][0];
1645
+ angles[i]['angle.end'] = angles[i][1];
1646
+ angles[i]['radius.start'] = angles[i][2];
1647
+ angles[i]['radius.end'] = angles[i][3];
1648
+ angles[i].index = indexes[1];
1649
+ angles[i].dataset = indexes[0];
1650
+ angles[i].sequentialIndex = angles[i][6];
1651
+ angles[i].tooltip = tooltip ? tooltip : null;
1652
+ angles[i].label = (
1653
+ typeof properties.labels === 'object'
1654
+ && !RGraph.isNull(properties.labels)
1655
+ && typeof properties.labels[indexes[0]] === 'string'
1656
+ )
1657
+ ? properties.labels[angles[i].dataset]
1658
+ : null;
1659
+
1660
+ // Adjust the indexes in the case of non-equi-angular charts
1661
+ if (properties.variant === 'non-equi-angular') {
1662
+ angles[i].dataset = angles[i].sequentialIndex;
1663
+ angles[i].index = 0;
1664
+ angles[i].label = (RGraph.isArray(properties.labels) && typeof properties.labels[angles[i].dataset] === 'string')
1665
+ ? properties.labels[angles[i].dataset]
1666
+ : null;
1667
+ }
1668
+
1669
+ return {
1670
+ object: this,
1671
+ x: angles[i][4],
1672
+ y: angles[i][5],
1673
+ angleStart: angles[i][0],
1674
+ angleEnd: angles[i][1],
1675
+ radiusStart: angles[i][2],
1676
+ radiusEnd: angles[i][3],
1677
+ dataset: angles[i].dataset,
1678
+ index: angles[i].index,
1679
+ sequentialIndex: angles[i][6],
1680
+ label: angles[i].label,
1681
+ tooltip: typeof tooltip === 'string' ? tooltip : null
1682
+ };
1683
+ }
1684
+ }
1685
+
1686
+ return null;
1687
+ };
1688
+
1689
+
1690
+
1691
+
1692
+
1693
+
1694
+
1695
+
1696
+ //
1697
+ // Returns any exploded for a particular segment
1698
+ //
1699
+ this.getExploded = function (index, startAngle, endAngle, exploded)
1700
+ {
1701
+ var explodedx, explodedy;
1702
+
1703
+ //
1704
+ // Retrieve any exploded - the exploded can be an array of numbers or a single number
1705
+ // (which is applied to all segments)
1706
+ //
1707
+ if (typeof exploded == 'object' && typeof exploded[index] == 'number') {
1708
+ explodedx = Math.cos(((endAngle - startAngle) / 2) + startAngle) * exploded[index];
1709
+ explodedy = Math.sin(((endAngle - startAngle) / 2) + startAngle) * exploded[index];
1710
+
1711
+ } else if (typeof exploded == 'number') {
1712
+ explodedx = Math.cos(((endAngle - startAngle) / 2) + startAngle) * exploded;
1713
+ explodedy = Math.sin(((endAngle - startAngle) / 2) + startAngle) * exploded;
1714
+
1715
+ } else {
1716
+ explodedx = 0;
1717
+ explodedy = 0;
1718
+ }
1719
+
1720
+ return [explodedx, explodedy];
1721
+ };
1722
+
1723
+
1724
+
1725
+
1726
+
1727
+
1728
+
1729
+
1730
+ //
1731
+ // This function facilitates the installation of tooltip event listeners if
1732
+ // tooltips are defined.
1733
+ //
1734
+ this.allowTooltips = function ()
1735
+ {
1736
+ // Preload any tooltip images that are used in the tooltips
1737
+ RGraph.preLoadTooltipImages(this);
1738
+
1739
+
1740
+ //
1741
+ // This installs the window mousedown event listener that lears any
1742
+ // highlight that may be visible.
1743
+ //
1744
+ RGraph.installWindowMousedownTooltipListener(this);
1745
+
1746
+
1747
+ //
1748
+ // This installs the canvas mousemove event listener. This function
1749
+ // controls the pointer shape.
1750
+ //
1751
+ RGraph.installCanvasMousemoveTooltipListener(this);
1752
+
1753
+
1754
+ //
1755
+ // This installs the canvas mouseup event listener. This is the
1756
+ // function that actually shows the appropriate tooltip (if any).
1757
+ //
1758
+ RGraph.installCanvasMouseupTooltipListener(this);
1759
+ };
1760
+
1761
+
1762
+
1763
+
1764
+
1765
+
1766
+
1767
+
1768
+ //
1769
+ // Each object type has its own Highlight() function which highlights the appropriate shape
1770
+ //
1771
+ // @param object shape The shape to highlight
1772
+ //
1773
+ this.highlight = function (shape)
1774
+ {
1775
+ if (properties.tooltipsHighlight) {
1776
+
1777
+ if (typeof properties.highlightStyle === 'function'){
1778
+ (properties.highlightStyle)(shape);
1779
+ return;
1780
+ }
1781
+
1782
+
1783
+
1784
+
1785
+
1786
+
1787
+
1788
+
1789
+
1790
+ // Highlight all of the rects except this one - essentially an inverted highlight
1791
+ if (typeof properties.highlightStyle === 'string' && properties.highlightStyle === 'invert') {
1792
+ for (var i=0; i<this.angles.length; ++i) {
1793
+
1794
+ if (i !== shape.sequentialIndex) {
1795
+
1796
+ // Add the new segment highlight
1797
+ this.path(
1798
+ 'b m % % a % % % % % false',
1799
+ this.angles[i][4], this.angles[i][5],this.angles[i][4], this.angles[i][5],this.angles[i][3],this.angles[i][0], this.angles[i][1]
1800
+ );
1801
+
1802
+ if (this.angles[i][2] > 0) {
1803
+ this.path(
1804
+ 'a % % % % % true',
1805
+ this.angles[i][4], this.angles[i][5],this.angles[i][2],this.angles[i][1], this.angles[i][0]
1806
+ );
1807
+
1808
+ } else {
1809
+ this.path(
1810
+ 'l % %',
1811
+ this.angles[i][4], this.angles[i][5]
1812
+ );
1813
+ }
1814
+
1815
+ this.path(
1816
+ 'c s % f %',
1817
+ properties.highlightStroke, properties.highlightFill
1818
+ );
1819
+ }
1820
+ }
1821
+
1822
+ return;
1823
+ }
1824
+
1825
+
1826
+
1827
+
1828
+
1829
+
1830
+
1831
+
1832
+
1833
+
1834
+
1835
+
1836
+
1837
+
1838
+ // Add the new segment highlight
1839
+ this.path('b a % % % % % false',shape.x, shape.y, shape.radiusEnd, shape.angleStart, shape.angleEnd);
1840
+
1841
+ if (shape.radiusStart > 0) {
1842
+ this.path('a % % % % % true',shape.x, shape.y, shape.radiusStart, shape.angleEnd, shape.angleStart);
1843
+ } else {
1844
+ this.path('l % %',shape.x, shape.y);
1845
+ }
1846
+
1847
+ this.path('c s % f %', properties.highlightStroke, properties.highlightFill);
1848
+ }
1849
+ };
1850
+
1851
+
1852
+
1853
+
1854
+
1855
+
1856
+
1857
+
1858
+ //
1859
+ // The getObjectByXY() worker method. Don't call this call:
1860
+ //
1861
+ // RGraph.ObjectRegistry.getObjectByXY(e)
1862
+ //
1863
+ // @param object e The event object
1864
+ //
1865
+ this.getObjectByXY = function (e)
1866
+ {
1867
+ var mouseXY = RGraph.getMouseXY(e);
1868
+
1869
+ // Work out the radius
1870
+ var radius = RGraph.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]);
1871
+
1872
+ // Account for the 3D stretching effect
1873
+ if (properties.variant.indexOf('3d') !== -1) {
1874
+ radius /= -1;
1875
+
1876
+ // Can be 100 because there's a radius check done as well
1877
+ var additional3D = 100;
1878
+
1879
+ } else {
1880
+ var additional3D = 0;
1881
+ }
1882
+
1883
+ if (
1884
+ mouseXY[0] > (this.centerx - this.radius - additional3D)
1885
+ && mouseXY[0] < (this.centerx + this.radius + additional3D)
1886
+ && mouseXY[1] > (this.centery - this.radius)
1887
+ && mouseXY[1] < (this.centery + this.radius)
1888
+ && radius <= this.radius
1889
+ ) {
1890
+
1891
+ return this;
1892
+ }
1893
+ };
1894
+
1895
+
1896
+
1897
+
1898
+
1899
+
1900
+
1901
+
1902
+ //
1903
+ // This method gives you the relevant radius for a particular value
1904
+ //
1905
+ // @param number value The relevant value to get the radius for
1906
+ //
1907
+ this.getRadius = function (value)
1908
+ {
1909
+ // Range checking (the Rose minimum is always 0)
1910
+ if (value < 0 || value > this.max) {
1911
+ return null;
1912
+ }
1913
+
1914
+ var r = (value / this.max) * this.radius;
1915
+
1916
+ return r;
1917
+ };
1918
+
1919
+
1920
+
1921
+
1922
+
1923
+
1924
+
1925
+
1926
+ //
1927
+ // This allows for easy specification of gradients
1928
+ //
1929
+ this.parseColors = function ()
1930
+ {
1931
+ // Save the original colors so that they can be restored when the canvas is reset
1932
+ if (this.original_colors.length === 0) {
1933
+ this.original_colors.colors = RGraph.arrayClone(properties.colors);
1934
+ this.original_colors.keyColors = RGraph.arrayClone(properties.keyColors);
1935
+ this.original_colors.highlightStroke = RGraph.arrayClone(properties.highlightStroke);
1936
+ this.original_colors.highlightFill = RGraph.arrayClone(properties.highlightFill);
1937
+ }
1938
+
1939
+
1940
+ for (var i=0; i<properties.colors.length; ++i) {
1941
+ properties.colors[i] = this.parseSingleColorForGradient(properties.colors[i]);
1942
+ }
1943
+
1944
+ //
1945
+ // Key colors
1946
+ //
1947
+ if (!RGraph.isNull(properties.keyColors)) {
1948
+ for (var i=0; i<properties.keyColors.length; ++i) {
1949
+ properties.keyColors[i] = this.parseSingleColorForGradient(properties.keyColors[i]);
1950
+ }
1951
+ }
1952
+
1953
+ properties.highlightFill = this.parseSingleColorForGradient(properties.highlightFill);
1954
+ properties.highlightStroke = this.parseSingleColorForGradient(properties.highlightStroke);
1955
+ properties.segmentHighlightStroke = this.parseSingleColorForGradient(properties.segmentHighlightStroke);
1956
+ properties.segmentHighlightFill = this.parseSingleColorForGradient(properties.segmentHighlightFill);
1957
+ };
1958
+
1959
+
1960
+
1961
+
1962
+
1963
+
1964
+
1965
+
1966
+ //
1967
+ // Use this function to reset the object to the post-constructor state. Eg reset colors if
1968
+ // need be etc
1969
+ //
1970
+ this.reset = function ()
1971
+ {
1972
+ };
1973
+
1974
+
1975
+
1976
+
1977
+
1978
+
1979
+
1980
+
1981
+ //
1982
+ // This parses a single color value
1983
+ //
1984
+ this.parseSingleColorForGradient = function (color)
1985
+ {
1986
+ if (!color || typeof color != 'string') {
1987
+ return color;
1988
+ }
1989
+
1990
+ if (color.match(/^gradient\((.*)\)$/i)) {
1991
+
1992
+ // Allow for JSON gradients
1993
+ if (color.match(/^gradient\(({.*})\)$/i)) {
1994
+ return RGraph.parseJSONGradient({object: this, def: RegExp.$1});
1995
+ }
1996
+
1997
+ var parts = RegExp.$1.split(':');
1998
+
1999
+ // Create the gradient
2000
+ //var grad = context.createLinearGradient(0,0,canvas.width,0);
2001
+ var grad = this.context.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
2002
+
2003
+ var diff = 1 / (parts.length - 1);
2004
+
2005
+ grad.addColorStop(0, RGraph.trim(parts[0]));
2006
+
2007
+ for (var j=1; j<parts.length; ++j) {
2008
+ grad.addColorStop(j * diff, RGraph.trim(parts[j]));
2009
+ }
2010
+ }
2011
+
2012
+ return grad ? grad : color;
2013
+ };
2014
+
2015
+
2016
+
2017
+
2018
+
2019
+
2020
+
2021
+
2022
+ //
2023
+ // This function handles highlighting an entire data-series for the interactive
2024
+ // key
2025
+ //
2026
+ // @param int index The index of the data series to be highlighted
2027
+ //
2028
+ this.interactiveKeyHighlight = function (index)
2029
+ {
2030
+ var segments = this.angles2;
2031
+
2032
+ for (var i=0; i<this.angles2.length; i+=1) {
2033
+ this.context.beginPath();
2034
+ this.context.lineWidth = 2;
2035
+ this.context.fillStyle = properties.keyInteractiveHighlightChartFill;
2036
+ this.context.strokeStyle = properties.keyInteractiveHighlightChartStroke;
2037
+ this.context.arc(segments[i][index][4], segments[i][index][5], segments[i][index][2], segments[i][index][0], segments[i][index][1], false);
2038
+ this.context.arc(segments[i][index][4], segments[i][index][5], segments[i][index][3], segments[i][index][1], segments[i][index][0], true);
2039
+ this.context.closePath();
2040
+ this.context.fill();
2041
+ this.context.stroke();
2042
+ }
2043
+
2044
+ return;
2045
+ };
2046
+
2047
+
2048
+
2049
+
2050
+
2051
+
2052
+
2053
+
2054
+ //
2055
+ // Using a function to add events makes it easier to facilitate method chaining
2056
+ //
2057
+ // @param string type The type of even to add
2058
+ // @param function func
2059
+ //
2060
+ this.on = function (type, func)
2061
+ {
2062
+ if (type.substr(0,2) !== 'on') {
2063
+ type = 'on' + type;
2064
+ }
2065
+
2066
+ if (typeof this[type] !== 'function') {
2067
+ this[type] = func;
2068
+ } else {
2069
+ RGraph.addCustomEventListener(this, type, func);
2070
+ }
2071
+
2072
+ return this;
2073
+ };
2074
+
2075
+
2076
+
2077
+
2078
+
2079
+
2080
+
2081
+
2082
+ //
2083
+ // This function runs once only
2084
+ // (put at the end of the file (before any effects))
2085
+ //
2086
+ this.firstDrawFunc = function ()
2087
+ {
2088
+ };
2089
+
2090
+
2091
+
2092
+
2093
+
2094
+
2095
+
2096
+
2097
+ //
2098
+ // Rose chart explode
2099
+ //
2100
+ // Explodes the Rose chart - gradually incrementing the size of the explode property
2101
+ //
2102
+ // @param object Optional options for the effect. You can pass in frames here - such as:
2103
+ // myRose.roundRobin({frames: 60}; function () {alert('Done!');})
2104
+ // @param function A callback function which is called when the effect is finished
2105
+ //
2106
+ this.explode = function ()
2107
+ {
2108
+ // Cancel any stop request if one is pending
2109
+ this.cancelStopAnimation();
2110
+
2111
+ var obj = this,
2112
+ opt = arguments[0] || {},
2113
+ callback = arguments[1] || function (){},
2114
+ frames = opt.frames ? opt.frames : 30,
2115
+ frame = 0,
2116
+ explodedMax = Math.max(this.canvas.width, this.canvas.height),
2117
+ exploded = Number(this.get('exploded')),
2118
+ originalExploded = RGraph.arrayClone(this.get('exploded'));
2119
+
2120
+
2121
+ // Set the exploded to the start point - which is
2122
+ // explodedMax
2123
+ if (RGraph.isArray(originalExploded)) {
2124
+ var exploded = new Array(this.data.length);
2125
+ for (let i=0; i<this.data.length; ++i) {
2126
+ exploded[i] = originalExploded[i] || 0;
2127
+ originalExploded[i] = originalExploded[i] || 0;
2128
+ }
2129
+ } else {
2130
+ var exploded = explodedMax;
2131
+ }
2132
+
2133
+
2134
+ function iterator ()
2135
+ {
2136
+ if (obj.stopAnimationRequested) {
2137
+
2138
+ // Reset the flag
2139
+ obj.stopAnimationRequested = false;
2140
+
2141
+ return;
2142
+ }
2143
+
2144
+ if (RGraph.isArray(originalExploded)) {
2145
+
2146
+ for (let i=0; i<obj.data.length; ++i) {
2147
+ exploded[i] = ((frame / frames) * (explodedMax - originalExploded[i])) + originalExploded[i];
2148
+ }
2149
+ } else {
2150
+ //exploded = explodedMax - ((frame / frames) * (explodedMax - originalExploded));
2151
+ exploded = (frame / frames) * explodedMax;
2152
+ }
2153
+
2154
+
2155
+
2156
+ // Set the new value
2157
+ obj.set('exploded', exploded);
2158
+
2159
+ RGraph.clear(obj.canvas);
2160
+ RGraph.redrawCanvas(obj.canvas);
2161
+
2162
+ if (frame++ < frames) {
2163
+ RGraph.Effects.updateCanvas(iterator);
2164
+ } else {
2165
+ callback(obj);
2166
+ }
2167
+ }
2168
+
2169
+
2170
+
2171
+
2172
+ iterator();
2173
+
2174
+ return this;
2175
+ };
2176
+
2177
+
2178
+
2179
+
2180
+
2181
+
2182
+
2183
+
2184
+ //
2185
+ // RoundRobin
2186
+ //
2187
+ // This effect is similar to the Pie chart RoundRobin effect
2188
+ //
2189
+ // @param object Optional options for the effect. You can pass in frames here - such as:
2190
+ // myRose.roundRobin({frames: 60}; function () {alert('Done!');})
2191
+ // @param function A callback function which is called when the effect is finished
2192
+ //
2193
+ this.roundrobin =
2194
+ this.roundRobin = function ()
2195
+ {
2196
+ // Cancel any stop request if one is pending
2197
+ this.cancelStopAnimation();
2198
+
2199
+ var obj = this,
2200
+ opt = arguments[0] || {},
2201
+ frames = opt.frames || 30,
2202
+ frame = 0,
2203
+ margin = (360 / this.data.length) / 2,
2204
+ callback = arguments[1] || function () {}
2205
+
2206
+
2207
+ // Save a copy of the original margin
2208
+ this.originalMargin = this.get('margin');
2209
+
2210
+ this.set('margin', margin);
2211
+ this.set('animationRoundrobinFactor', 0);
2212
+
2213
+ function iterator ()
2214
+ {
2215
+ if (obj.stopAnimationRequested) {
2216
+
2217
+ // Reset the flag
2218
+ obj.stopAnimationRequested = false;
2219
+
2220
+ return;
2221
+ }
2222
+
2223
+
2224
+ RGraph.clear(obj.canvas);
2225
+ RGraph.redrawCanvas(obj.canvas);
2226
+
2227
+ if (frame++ < frames) {
2228
+ obj.set('animationRoundrobinFactor', frame / frames);
2229
+ obj.set('margin', (frame / frames) * obj.originalMargin);
2230
+ RGraph.Effects.updateCanvas(iterator);
2231
+ } else {
2232
+ obj.set('animationRoundrobinFactor', 1);
2233
+ obj.set('margin', obj.originalMargin);
2234
+
2235
+ callback(obj);
2236
+ }
2237
+ }
2238
+
2239
+ iterator();
2240
+
2241
+ return this;
2242
+ };
2243
+
2244
+
2245
+
2246
+
2247
+
2248
+
2249
+
2250
+
2251
+ //
2252
+ // Rose chart implode
2253
+ //
2254
+ // Implodes the Rose chart - gradually decreasing the size of the explode property. It starts at the largest of
2255
+ // the canvas width./height
2256
+ //
2257
+ // @param object Optional options for the effect. You can pass in frames here - such as:
2258
+ // myRose.implode({frames: 60}; function () {alert('Done!');})
2259
+ // @param function A callback function which is called when the effect is finished
2260
+ //
2261
+ this.implode = function ()
2262
+ {
2263
+ // Cancel any stop request if one is pending
2264
+ this.cancelStopAnimation();
2265
+
2266
+ var obj = this,
2267
+ opt = arguments[0] || {},
2268
+ callback = arguments[1] || function (){},
2269
+ frames = opt.frames || 30,
2270
+ frame = 0,
2271
+ explodedMax = Math.max(this.canvas.width, this.canvas.height),
2272
+ originalExploded = RGraph.arrayClone(this.get('exploded'));
2273
+
2274
+
2275
+
2276
+ // Set the exploded to the start point - which is
2277
+ // explodedMax
2278
+ if (RGraph.isArray(originalExploded)) {
2279
+
2280
+ var exploded = new Array(this.data.length);
2281
+
2282
+ for (let i=0; i<this.data.length; ++i) {
2283
+ exploded[i] = explodedMax;
2284
+ }
2285
+ } else {
2286
+ var exploded = explodedMax;
2287
+ }
2288
+
2289
+
2290
+
2291
+ function iterator ()
2292
+ {
2293
+ if (obj.stopAnimationRequested) {
2294
+
2295
+ // Reset the flag
2296
+ obj.stopAnimationRequested = false;
2297
+
2298
+ return;
2299
+ }
2300
+
2301
+ if (RGraph.isArray(originalExploded)) {
2302
+ for (let i=0; i<obj.data.length; ++i) {
2303
+ exploded[i] = explodedMax - ((frame / frames) * (explodedMax - (originalExploded[i] || 0)));
2304
+ }
2305
+ } else {
2306
+ exploded = explodedMax - ((frame / frames) * (explodedMax - originalExploded));
2307
+ }
2308
+
2309
+ // Set the new value
2310
+ obj.set('exploded', exploded);
2311
+
2312
+ RGraph.clear(obj.canvas);
2313
+ RGraph.redrawCanvas(obj.canvas);
2314
+
2315
+ if (frame++ < frames) {
2316
+ RGraph.Effects.updateCanvas(iterator);
2317
+ } else {
2318
+ RGraph.clear(obj.canvas);
2319
+ RGraph.redrawCanvas(obj.canvas);
2320
+ callback(obj);
2321
+ }
2322
+ }
2323
+
2324
+ iterator();
2325
+
2326
+ return this;
2327
+ };
2328
+
2329
+
2330
+
2331
+
2332
+
2333
+
2334
+
2335
+
2336
+ //
2337
+ // Rose chart Grow
2338
+ //
2339
+ // This effect gradually increases the size of the Rose chart
2340
+ //
2341
+ // @param object Optional options for the effect. You can pass in frames here - such as:
2342
+ // myRose.grow({frames: 60}; function () {alert('Done!');})
2343
+ // @param function A callback function which is called when the effect is finished
2344
+ //
2345
+ this.grow = function ()
2346
+ {
2347
+ // Cancel any stop request if one is pending
2348
+ this.cancelStopAnimation();
2349
+
2350
+ var obj = this,
2351
+ opt = arguments[0] || {},
2352
+ callback = arguments[1] || function (){},
2353
+ frames = opt.frames || 30,
2354
+ frame = 0;
2355
+
2356
+ function iterator ()
2357
+ {
2358
+ if (obj.stopAnimationRequested) {
2359
+
2360
+ // Reset the flag
2361
+ obj.stopAnimationRequested = false;
2362
+
2363
+ return;
2364
+ }
2365
+
2366
+ obj.set('animationGrowMultiplier', frame / frames);
2367
+
2368
+ RGraph.clear(obj.canvas);
2369
+ RGraph.redrawCanvas(obj.canvas);
2370
+
2371
+ if (frame < frames) {
2372
+ frame++;
2373
+ RGraph.Effects.updateCanvas(iterator);
2374
+ } else {
2375
+ callback(obj);
2376
+ }
2377
+ }
2378
+
2379
+ iterator();
2380
+
2381
+ return this;
2382
+ };
2383
+
2384
+
2385
+
2386
+
2387
+
2388
+
2389
+
2390
+
2391
+ //
2392
+ // Couple of functions that allow you to control the
2393
+ // animation effect
2394
+ //
2395
+ this.stopAnimation = function ()
2396
+ {
2397
+ // Reset the clip
2398
+ this.set('animationGrowMultiplier', 1);
2399
+
2400
+ // Reset the RoundRobin factor
2401
+ this.set('animationRoundrobinFactor', 1);
2402
+
2403
+ // Set the original margin
2404
+ if (RGraph.isNumber(this.originalMargin)) {
2405
+ this.set('margin', this.originalMargin);
2406
+ }
2407
+
2408
+ // Reset the exploded
2409
+ this.set('exploded', []);
2410
+
2411
+ this.stopAnimationRequested = true;
2412
+ };
2413
+
2414
+ this.cancelStopAnimation = function ()
2415
+ {
2416
+ this.stopAnimationRequested = false;
2417
+ };
2418
+
2419
+
2420
+
2421
+
2422
+
2423
+
2424
+
2425
+
2426
+ //
2427
+ // A worker function that handles Bar chart specific tooltip substitutions
2428
+ //
2429
+ this.tooltipSubstitutions = function (opt)
2430
+ {
2431
+ var indexes = RGraph.sequentialIndexToGrouped(opt.index, this.data);
2432
+ var value = this.data_arr[opt.index];
2433
+ var values = typeof this.data[indexes[0]] === 'number' ? [this.data[indexes[0]]] : this.data[indexes[0]];
2434
+
2435
+ if (properties.variant === 'non-equi-angular' && RGraph.isArray(this.data[indexes[0]])) {
2436
+ value = this.data[opt.index][0];
2437
+ value2 = this.data[opt.index][1];
2438
+ indexes = [opt.index, 0];
2439
+ }
2440
+
2441
+ return {
2442
+ index: indexes[1],
2443
+ dataset: indexes[0],
2444
+ sequentialIndex: opt.index,
2445
+ value: value,
2446
+ value2: typeof value2 === 'number' ? value2 : null,
2447
+ values: values
2448
+ };
2449
+ };
2450
+
2451
+
2452
+
2453
+
2454
+
2455
+
2456
+
2457
+
2458
+ //
2459
+ // A worker function that returns the correct color/label/value
2460
+ //
2461
+ // @param object specific The indexes that are applicable
2462
+ // @param number index The appropriate index
2463
+ //
2464
+ this.tooltipsFormattedCustom = function (specific, index, colors)
2465
+ {
2466
+ var color = properties.colors[index];
2467
+
2468
+ // Accommodate colorsSequential
2469
+ if (properties.colorsSequential) {
2470
+ color = colors[specific.sequential];
2471
+ }
2472
+
2473
+ // Different variations of the Rose chart
2474
+
2475
+ // REGULAR CHART
2476
+ if (typeof this.data[specific.dataset] === 'number') {
2477
+
2478
+ var label = properties.tooltipsFormattedKeyLabels[0] || '';
2479
+ var color = properties.colors[0];
2480
+
2481
+ if (properties.tooltipsFormattedKeyColors && properties.tooltipsFormattedKeyColors[0]) {
2482
+ color = properties.tooltipsFormattedKeyColors[0];
2483
+ }
2484
+
2485
+
2486
+ // NON-EQUI-ANGULAR CHART
2487
+ } else if (typeof this.data[specific.dataset] === 'object' && properties.variant === 'non-equi-angular') {
2488
+
2489
+ // Don't show the second value on a non-equi-angular chart
2490
+ if (index === 0) {
2491
+
2492
+ var color = colors[0];
2493
+ var value = this.data[specific.dataset][0];
2494
+
2495
+ // Allow for specific tooltip key colors
2496
+ if (properties.tooltipsFormattedKeyColors && properties.tooltipsFormattedKeyColors[specific.index]) {
2497
+ color = properties.tooltipsFormattedKeyColors[specific.index];
2498
+ }
2499
+ } else {
2500
+ var skip = true;
2501
+ }
2502
+
2503
+ // STACKED CHART
2504
+ } else if (typeof this.data[specific.dataset] === 'object' && properties.variant !== 'non-equi-angular') {
2505
+ // Allow for specific tooltip key colors
2506
+ if (properties.tooltipsFormattedKeyColors && properties.tooltipsFormattedKeyColors[index]) {
2507
+ color = properties.tooltipsFormattedKeyColors[index];
2508
+ }
2509
+ }
2510
+
2511
+ //label = ( (typeof properties.tooltipsFormattedKeyLabels === 'object' && typeof properties.tooltipsFormattedKeyLabels[specific.index] === 'string') ? properties.tooltipsFormattedKeyLabels[specific.index] : '');
2512
+
2513
+ return {
2514
+ continue: skip,
2515
+ label: label,
2516
+ color: color,
2517
+ value: value
2518
+ };
2519
+ };
2520
+
2521
+
2522
+
2523
+
2524
+
2525
+
2526
+
2527
+
2528
+ //
2529
+ // This allows for static tooltip positioning
2530
+ //
2531
+ this.positionTooltipStatic = function (args)
2532
+ {
2533
+ var obj = args.object,
2534
+ e = args.event,
2535
+ tooltip = args.tooltip,
2536
+ index = args.index,
2537
+ canvasXY = RGraph.getCanvasXY(obj.canvas)
2538
+ segment = this.angles[args.index],
2539
+ shape = this.getShape(e),
2540
+ angle = ((shape.angleEnd - shape.angleStart) / 2) + shape.angleStart,
2541
+ multiplier = 0.5;
2542
+
2543
+ var endpoint = RGraph.getRadiusEndPoint(
2544
+ shape.x,
2545
+ shape.y,
2546
+ angle,
2547
+ ((shape.radiusEnd - shape.radiusStart) / 2) + shape.radiusStart
2548
+ );
2549
+
2550
+
2551
+ // Allow for the 3D stretching of the canvas
2552
+ if (properties.variant.indexOf('3d') !== -1) {
2553
+ var width = this.radius / 2;
2554
+ endpoint[0] = (endpoint[0] - this.centerx) * 1.5 + this.centerx;
2555
+ }
2556
+
2557
+ // Position the tooltip in the X direction
2558
+ args.tooltip.style.left = (
2559
+ canvasXY[0] // The X coordinate of the canvas
2560
+ + endpoint[0] // The X coordinate of the bar on the chart
2561
+ - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width
2562
+ + obj.properties.tooltipsOffsetx // Add any user defined offset
2563
+ ) + 'px';
2564
+
2565
+ args.tooltip.style.top = (
2566
+ canvasXY[1] // The Y coordinate of the canvas
2567
+ + endpoint[1] // The Y coordinate of the bar on the chart
2568
+ - tooltip.offsetHeight // The height of the tooltip
2569
+ + obj.properties.tooltipsOffsety // Add any user defined offset
2570
+ - 10 // Account for the pointer
2571
+ ) + 'px';
2572
+ };
2573
+
2574
+
2575
+
2576
+
2577
+
2578
+
2579
+
2580
+
2581
+ //
2582
+ // This returns the relevant value for the formatted key
2583
+ // macro %{value}. THIS VALUE SHOULD NOT BE FORMATTED.
2584
+ //
2585
+ // @param number index The index in the dataset to get
2586
+ // the value for
2587
+ //
2588
+ this.getKeyValue = function (index)
2589
+ {
2590
+ if ( RGraph.isArray(this.properties.keyFormattedValueSpecific)
2591
+ && RGraph.isNumber(this.properties.keyFormattedValueSpecific[index])) {
2592
+
2593
+ return this.properties.keyFormattedValueSpecific[index];
2594
+
2595
+
2596
+
2597
+
2598
+ // Allow for non-equi-angular Rose charts
2599
+ } else if (this.properties.variant === 'non-equi-angular') {
2600
+
2601
+ var total = 0;
2602
+
2603
+ for (let i=0; i<this.data.length; ++i) {
2604
+ total += this.data[i][0];
2605
+ }
2606
+
2607
+ return total;
2608
+
2609
+
2610
+
2611
+
2612
+
2613
+
2614
+ // Regular or stacked Rose charts
2615
+ } else {
2616
+ var total = 0;
2617
+
2618
+ for (let i=0; i<this.data.length; ++i) {
2619
+ if (RGraph.isArray(this.data[i])) {
2620
+ total += this.data[i][index];
2621
+ } else {
2622
+ total += this.data[i];
2623
+ }
2624
+ }
2625
+
2626
+ return total;
2627
+ }
2628
+ };
2629
+
2630
+
2631
+
2632
+
2633
+
2634
+
2635
+
2636
+
2637
+ //
2638
+ // Returns how many data-points there should be when a string
2639
+ // based key property has been specified. For example, this:
2640
+ //
2641
+ // key: '%{property:_labels[%{index}]} %{value_formatted}'
2642
+ //
2643
+ // ...depending on how many bits of data ther is might get
2644
+ // turned into this:
2645
+ //
2646
+ // key: [
2647
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2648
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2649
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2650
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2651
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2652
+ // ]
2653
+ //
2654
+ // ... ie in that case there would be 4 data-points so the
2655
+ // template is repeated 4 times.
2656
+ //
2657
+ this.getKeyNumDatapoints = function ()
2658
+ {
2659
+ return this.properties.variant === 'non-equi-angular' ? 1 : this.data[0].length;
2660
+ };
2661
+
2662
+
2663
+
2664
+
2665
+
2666
+
2667
+
2668
+
2669
+ //
2670
+ // Register this object
2671
+ //
2672
+ RGraph.register(this);
2673
+
2674
+
2675
+
2676
+
2677
+
2678
+
2679
+
2680
+
2681
+ //
2682
+ // This is the 'end' of the constructor so if the first argument
2683
+ // contains configuration data - handle that.
2684
+ //
2685
+ RGraph.parseObjectStyleConfig(this, conf.options);
2686
+ };