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,109 +1,2334 @@
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.Radar=function(conf)
3
- {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var parseConfObjectForOptions=true;if(typeof conf.data[0]==='number'||typeof conf.data[0]==='string'){conf.data=[conf.data];}}else{var conf={id:conf,data:[]};if(typeof arguments[1]==='object'&&typeof arguments[1][0]==='number'){for(var i=1;i<arguments.length;++i){conf.data.push(RGraph.arrayClone(arguments[i]));}}else if(typeof arguments[1]==='object'&&typeof arguments[1][0]==='object'&&typeof arguments[1][0][0]==='number'){conf.data=RGraph.arrayClone(arguments[1]);}}
4
- this.id=conf.id;this.canvas=document.getElementById(conf.id);this.context=this.canvas.getContext?this.canvas.getContext("2d"):null;this.canvas.__object__=this;this.type='radar';this.isRGraph=true;this.data=[];this.max=0;this.uid=RGraph.CreateUID();this.canvas.uid=this.canvas.uid?this.canvas.uid:RGraph.CreateUID();this.colorsParsed=false;this.coords=[];this.coordsText=[];this.original_data=[];this.original_colors=[];this.firstDraw=true;this.propertyNameAliases={};for(var i=0,len=conf.data.length;i<len;++i){for(var j=0;j<conf.data[i].length;++j){if(typeof conf.data[i][j]==='string'){conf.data[i][j]=parseFloat(conf.data[i][j]);}}
5
- this.original_data.push(RGraph.arrayClone(conf.data[i]));this.data.push(RGraph.arrayClone(conf.data[i]));this.max=Math.max(this.max,RGraph.arrayMax(conf.data[i]));}
6
- this.properties={'chart.margin.left':25,'chart.margin.right':25,'chart.margin.top':25,'chart.margin.bottom':25,'chart.linewidth':1,'chart.colors.stroke':'#aaa','chart.colors':['rgba(255,0,0,0.75)','rgba(0,255,255,0.25)','rgba(255,0,0,0.5)','red','green','blue','pink','aqua','brown','orange','grey'],'chart.colors.alpha':null,'chart.circle':0,'chart.circle.fill':'red','chart.circle.stroke':'black','chart.labels':[],'chart.labels.font':null,'chart.labels.size':null,'chart.labels.color':null,'chart.labels.bold':null,'chart.labels.size':null,'chart.labels.offset':10,'chart.labels.axes':'','chart.labels.background.fill':'white','chart.labels.boxed':false,'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.boxed':null,'chart.labels.axes.boxed.zero':true,'chart.labels.axes.boxed.background':'rgba(255,255,255,0.7)','chart.labels.axes.specific':null,'chart.labels.axes.count':5,'chart.background.circles':true,'chart.background.circles.count':null,'chart.background.circles.color':'#ddd','chart.background.circles.poly':true,'chart.background.circles.spokes':24,'chart.text.size':12,'chart.text.font':'Arial, Verdana, sans-serif','chart.text.color':'black','chart.text.bold':false,'chart.text.italic':false,'chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':false,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.color':null,'chart.title.bold':null,'chart.title.italic':null,'chart.title.size':null,'chart.title.font':null,'chart.title.x':null,'chart.title.y':null,'chart.title.halign':null,'chart.title.valign':null,'chart.linewidth':1,'chart.key':null,'chart.key.background':'white','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':'graph','chart.key.halign':'right','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':'rgba(255,0,0,0.3)','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.annotatable':false,'chart.annotate.color':'black','chart.annotate.linewidth':1,'chart.tooltips.effect':'fade','chart.tooltips.event':'onmousemove','chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.highlight':true,'chart.highlight.stroke':'gray','chart.highlight.fill':'rgba(255,255,255,0.7)','chart.highlight.point.radius':2,'chart.resizable':false,'chart.resize.handle.adjust':[0,0],'chart.resize.handle.background':null,'chart.scale.max':null,'chart.scale.decimals':0,'chart.scale.point':'.','chart.scale.thousand':',','chart.scale.units.pre':'','chart.scale.units.post':'','chart.accumulative':false,'chart.radius':null,'chart.events.click':null,'chart.events.mousemove':null,'chart.tooltips':null,'chart.tooltips.event':'onmousemove','chart.centerx':null,'chart.centery':null,'chart.radius':null,'chart.xaxis.tickmarks.count':5,'chart.yaxis.tickmarks.count':5,'chart.axes.color':'rgba(0,0,0,0)','chart.highlights':false,'chart.highlights.stroke':'#ddd','chart.highlights.fill':null,'chart.highlights.radius':3,'chart.fill.click':null,'chart.fill.mousemove':null,'chart.fill.tooltips':null,'chart.fill.highlight.fill':'rgba(255,255,255,0.7)','chart.fill.highlight.stroke':'rgba(0,0,0,0)','chart.fill.mousemove.redraw':false,'chart.animation.trace.clip':1,'chart.clearto':'rgba(0,0,0,0)'}
7
- for(var dataset=0;dataset<this.data.length;++dataset){if(this.data[dataset].length<3){alert('[RADAR] You must specify at least 3 data points');return;}}
8
- var idx=0;for(var dataset=0;dataset<this.data.length;++dataset){for(var i=0,len=this.data[dataset].length;i<len;++i){this['$'+(idx++)]={};}}
9
- if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
10
- var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
11
- if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
12
- this.set=this.Set=function(name,value)
13
- {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof name==='object'){RG.parseObjectStyleConfig(this,name);return this;}
14
- if(name.substr(0,6)!='chart.'){name='chart.'+name;}
15
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
16
- prop[name]=value;return this;};this.get=this.Get=function(name)
17
- {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
18
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
19
- return prop[name];};this.draw=this.Draw=function()
20
- {RG.FireCustomEvent(this,'onbeforedraw');this.coords=[];this.coords2=[];this.coordsText=[];this.data=RG.arrayClone(this.original_data);if(prop['chart.accumulative']){for(var i=0;i<this.data.length;++i){if(this.data[i].length!=this.data[0].length){alert('[RADAR] Error! When the radar has chart.accumulative set to true all the datasets must have the same number of elements');}}}
21
- if(RG.isNull(prop['chart.labels.axes.boxed'])){prop['chart.labels.axes.boxed']=[];for(var i=0;i<((RG.isArray(prop['chart.labels.axes.specific'])&&prop['chart.labels.axes.specific'].length)||prop['chart.labels.axes.count']||5);++i){prop['chart.labels.axes.boxed'][i]=false;}}
22
- 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.centerx=((ca.width-this.marginLeft-this.marginRight)/2)+this.marginLeft;this.centery=((ca.height-this.marginTop-this.marginBottom)/2)+this.marginTop;this.radius=Math.min(ca.width-this.marginLeft-this.marginRight,ca.height-this.marginTop-this.marginBottom)/2;if(typeof prop['chart.centerx']=='number')this.centerx=2*prop['chart.centerx'];if(typeof prop['chart.centery']=='number')this.centery=2*prop['chart.centery'];if(typeof prop['chart.radius']=='number')this.radius=prop['chart.radius'];if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
23
- if(!prop['chart.scale.max']){if(prop['chart.accumulative']){var accumulation=[];var len=this.original_data[0].length
24
- for(var i=1;i<this.original_data.length;++i){if(this.original_data[i].length!=len){alert('[RADAR] Error! Stacked Radar chart datasets must all be the same size!');}
25
- for(var j=0;j<this.original_data[i].length;++j){this.data[i][j]+=this.data[i-1][j];this.max=Math.max(this.max,this.data[i][j]);}}}
26
- this.scale2=RG.getScale2(this,{'scale.max':typeof prop['chart.scale.max']==='number'?prop['chart.scale.max']:this.max,'scale.min':0,'scale.decimals':Number(prop['chart.scale.decimals']),'scale.point':prop['chart.scale.point'],'scale.thousand':prop['chart.scale.thousand'],'scale.round':prop['chart.scale.round'],'scale.units.pre':prop['chart.scale.units.pre'],'scale.units.post':prop['chart.scale.units.post'],'scale.labels.count':prop['chart.labels.axes.count']});this.max=this.scale2.max;}else{var ymax=prop['chart.scale.max'];this.scale2=RG.getScale2(this,{'scale.max':ymax,'scale.min':0,'scale.strict':true,'scale.decimals':Number(prop['chart.scale.decimals']),'scale.point':prop['chart.scale.point'],'scale.thousand':prop['chart.scale.thousand'],'scale.round':prop['chart.scale.round'],'scale.units.pre':prop['chart.scale.units.pre'],'scale.units.post':prop['chart.scale.units.post'],'scale.labels.count':prop['chart.labels.axes.count']});this.max=this.scale2.max;}
27
- this.drawBackground();this.drawAxes();this.drawCircle();this.drawLabels();co.save();co.beginPath();co.arc(this.centerx,this.centery,this.radius*2,-RG.HALFPI,(RG.TWOPI*prop['chart.animation.trace.clip'])-RG.HALFPI,false);co.lineTo(this.centerx,this.centery);co.closePath();co.clip();this.DrawChart();this.DrawHighlights();co.restore();this.drawAxisLabels();if(prop['chart.title']){RG.drawTitle(this,prop['chart.title'],this.marginTop,null,null)}
28
- if(prop['chart.key']){RG.drawKey(this,prop['chart.key'],prop['chart.colors']);}
29
- if(prop['chart.contextmenu']){RG.showContext(this);}
30
- if(prop['chart.resizable']){RG.allowResizing(this);}
31
- RG.installEventListeners(this);if((prop['chart.fill.click']||prop['chart.fill.mousemove']||!RG.isNull(prop['chart.fill.tooltips']))&&!this.__fill_click_listeners_installed__){this.addFillListeners();this.__fill_click_listeners_installed__=true;}
32
- if(this.firstDraw){this.firstDraw=false;RG.fireCustomEvent(this,'onfirstdraw');this.firstDrawFunc();}
33
- RGraph.fireCustomEvent(this,'ondraw');return this;};this.exec=function(func)
34
- {func(this);return this;};this.drawBackground=this.DrawBackground=function()
35
- {var color=prop['chart.background.circles.color'];var poly=prop['chart.background.circles.poly'];var spacing=prop['chart.background.circles.spacing'];var spokes=prop['chart.background.circles.spokes'];co.lineWidth=1;if(prop['chart.background.circles']&&poly==false){co.strokeStyle=color;co.beginPath();var numrings=typeof(prop['chart.background.circles.count'])=='number'?prop['chart.background.circles.count']:prop['chart.labels.axes.count'];for(var r=0;r<=this.radius;r+=(this.radius/numrings)){co.moveTo(this.centerx,this.centery);co.arc(this.centerx,this.centery,r,0,RG.TWOPI,false);}
36
- co.stroke();co.strokeStyle=color;for(var i=0;i<360;i+=(360/spokes)){co.beginPath();co.arc(this.centerx,this.centery,this.radius,(i/360)*RG.TWOPI,((i+0.001)/360)*RG.TWOPI,false);co.lineTo(this.centerx,this.centery);co.stroke();}}else if(prop['chart.background.circles']&&poly==true){co.strokeStyle=color;var increment=360/this.data[0].length
37
- for(var i=0;i<360;i+=increment){co.beginPath();co.arc(this.centerx,this.centery,this.radius,((i/360)*RG.TWOPI)-RG.HALFPI,(((i+0.001)/360)*RG.TWOPI)-RG.HALFPI,false);co.lineTo(this.centerx,this.centery);co.stroke();}
38
- co.strokeStyle=color;var numrings=typeof prop['chart.background.circles.count']==='number'?prop['chart.background.circles.count']:prop['chart.labels.axes.count'];for(var r=0;r<=this.radius;r+=(this.radius/numrings)){co.beginPath();for(var a=0;a<=360;a+=(360/this.data[0].length)){co.arc(this.centerx,this.centery,r,RG.toRadians(a)-RG.HALFPI,RG.toRadians(a)+0.001-RG.HALFPI,false);}
39
- co.closePath();co.stroke();}}};this.drawAxes=this.DrawAxes=function()
40
- {co.strokeStyle=prop['chart.axes.color'];co.lineWidth=prop['chart.axes.linewidth'];var halfsize=this.radius;co.beginPath();co.moveTo(Math.round(this.centerx),this.centery+this.radius);co.lineTo(Math.round(this.centerx),this.centery-this.radius);co.moveTo(this.centerx-5,Math.round(this.centery+this.radius));co.lineTo(this.centerx+5,Math.round(this.centery+this.radius));co.moveTo(this.centerx-5,Math.round(this.centery-this.radius));co.lineTo(this.centerx+5,Math.round(this.centery-this.radius));for(var y=(this.centery-this.radius);y<(this.centery+this.radius);y+=(this.radius/prop['chart.yaxis.tickmarks.count'])){co.moveTo(this.centerx-3,Math.round(y));co.lineTo(this.centerx+3,Math.round(y));}
41
- co.moveTo(this.centerx-this.radius,Math.round(this.centery));co.lineTo(this.centerx+this.radius,Math.round(this.centery));co.moveTo(Math.round(this.centerx-this.radius),this.centery-5);co.lineTo(Math.round(this.centerx-this.radius),this.centery+5);co.moveTo(Math.round(this.centerx+this.radius),this.centery-5);co.lineTo(Math.round(this.centerx+this.radius),this.centery+5);for(var x=(this.centerx-this.radius);x<(this.centerx+this.radius);x+=(this.radius/prop['chart.xaxis.tickmarks.count'])){co.moveTo(Math.round(x),this.centery-3);co.lineTo(Math.round(x),this.centery+3);}
42
- co.stroke();};this.drawChart=this.DrawChart=function()
43
- {var alpha=prop['chart.colors.alpha'];if(typeof(alpha)=='number'){var oldAlpha=co.globalAlpha;co.globalAlpha=alpha;}
44
- var numDatasets=this.data.length;for(var dataset=0;dataset<this.data.length;++dataset){co.beginPath();var coords_dataset=[];for(var i=0;i<this.data[dataset].length;++i){var coords=this.GetCoordinates(dataset,i);if(coords_dataset==null){coords_dataset=[];}
45
- coords_dataset.push(coords);this.coords.push(coords);}
46
- this.coords2[dataset]=coords_dataset;co.strokeStyle=(typeof(prop['chart.colors.stroke'])=='object'&&prop['chart.colors.stroke'][dataset])?prop['chart.colors.stroke'][dataset]:prop['chart.colors.stroke'];co.fillStyle=prop['chart.colors'][dataset]?prop['chart.colors'][dataset]:'rgba(0,0,0,0)';if(co.fillStyle==='transparent'){co.fillStyle='rgba(0,0,0,0)';}
47
- co.lineWidth=prop['chart.linewidth'];for(i=0;i<coords_dataset.length;++i){if(i==0){co.moveTo(coords_dataset[i][0],coords_dataset[i][1]);}else{co.lineTo(coords_dataset[i][0],coords_dataset[i][1]);}}
48
- if(prop['chart.accumulative']&&dataset>0){co.lineTo(coords_dataset[0][0],coords_dataset[0][1]);co.moveTo(last_coords[0][0],last_coords[0][1]);for(var i=coords_dataset.length-1;i>=0;--i){co.lineTo(last_coords[i][0],last_coords[i][1]);}}
49
- var last_coords=coords_dataset;co.closePath();co.fill();co.stroke();}
50
- if(typeof(alpha)=='number'){co.globalAlpha=oldAlpha;}};this.getCoordinates=this.GetCoordinates=function(dataset,index)
51
- {var len=this.data[dataset].length;var mag=(this.data[dataset][index]/this.max)*this.radius;var angle=(RG.TWOPI/len)*index;angle-=RG.HALFPI;var x=Math.cos(angle)*mag;var y=Math.sin(angle)*mag;x=this.centerx+x;y=this.centery+y;return[x,y];};this.drawLabels=this.DrawLabels=function()
52
- {var labels=prop['chart.labels'];if(labels&&labels.length>0){co.lineWidth=1;co.strokeStyle='gray';co.fillStyle=prop['chart.labels.color']||prop['chart.text.color'];var bgFill=prop['chart.labels.background.fill'],bold=prop['chart.labels.bold'],bgBoxed=prop['chart.labels.boxed'],offset=prop['chart.labels.offset'],font=prop['chart.text.font'],size=prop['chart.text.size'],radius=this.radius,color=prop['chart.labels.color']||prop['chart.text.color']
53
- for(var i=0;i<labels.length;++i){var angle=(RG.TWOPI/prop['chart.labels'].length)*i;angle-=RG.HALFPI;var x=this.centerx+(ma.cos(angle)*(radius+offset));var y=this.centery+(ma.sin(angle)*(radius+offset));var halign=x<this.centerx?'right':'left';if(i==0||(i/labels.length)==0.5)halign='center';if(labels[i]&&labels[i].length){var textConf=RG.getTextConf({object:this,prefix:'chart.labels'});RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:x,y:y,text:labels[i],valign:'center',halign:halign,bounding:bgBoxed,boundingFill:bgFill,tag:'labels'});}}}};this.drawCircle=this.DrawCircle=function()
54
- {var circle={};circle.limit=prop['chart.circle'];circle.fill=prop['chart.circle.fill'];circle.stroke=prop['chart.circle.stroke'];if(circle.limit){var r=(circle.limit/this.max)*this.radius;co.fillStyle=circle.fill;co.strokeStyle=circle.stroke;co.beginPath();co.arc(this.centerx,this.centery,r,0,RG.TWOPI,0);co.fill();co.stroke();}};this.drawAxisLabels=this.DrawAxisLabels=function()
55
- {if(RG.isArray(prop['chart.labels.axes.specific'])&&prop['chart.labels.axes.specific'].length){this.drawSpecificAxisLabels();return;}
56
- co.lineWidth=1;co.fillStyle='black';co.strokeStyle='black';var r=this.radius,font=prop['chart.text.font'],axes=prop['chart.labels.axes'].toLowerCase(),color=prop['chart.labels.axes.boxed.background'],drawzero=false,units_pre=prop['chart.scale.units.pre'],units_post=prop['chart.scale.units.post'],decimals=prop['chart.scale.decimals'],bold=prop['chart.labels.axes.bold'],boxed=prop['chart.labels.axes.boxed'],centerx=this.centerx,centery=this.centery,scale=this.scale;co.fillStyle=prop['chart.text.color'];var textConf=RG.getTextConf({object:this,prefix:'chart.labels.axes'});if(axes.indexOf('n')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:centerx,y:centery-(r*((i+1)/this.scale2.labels.length)),text:this.scale2.labels[i],valign:'center',halign:'center',bounding:boxed[i]||color,boundingFill:color,boundingStroke:'rgba(0,0,0,0)',tag:'scale'});}
57
- drawzero=true;}
58
- if(axes.indexOf('s')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:centerx,y:centery+(r*((i+1)/this.scale2.labels.length)),text:this.scale2.labels[i],valign:'center',halign:'center',bounding:boxed[i]||color,boundingFill:color,boundingStroke:'rgba(0,0,0,0)',tag:'scale'});}
59
- drawzero=true;}
60
- if(axes.indexOf('e')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:centerx+(r*((i+1)/this.scale2.labels.length)),y:centery,text:this.scale2.labels[i],valign:'center',halign:'center',bounding:boxed[i]||color,boundingFill:color,boundingStroke:'rgba(0,0,0,0)',tag:'scale'});}
61
- drawzero=true;}
62
- if(axes.indexOf('w')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:centerx-(r*((i+1)/this.scale2.labels.length)),y:centery,text:this.scale2.labels[i],valign:'center',halign:'center',bounding:boxed[i]||color,boundingFill:color,boundingStroke:'rgba(0,0,0,0)',tag:'scale'});}
63
- drawzero=true;}
64
- if(drawzero){RG.Text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,x:centerx,y:centery,text:RG.numberFormat({object:this,number:Number(0).toFixed(),unitspre:units_pre,unitspost:units_post}),valign:'center',halign:'center',bounding:color?true:false,boundingFill:color,boundingStroke:'rgba(0,0,0,0)',tag:'scale'});}};this.drawSpecificAxisLabels=this.DrawSpecificAxisLabels=function()
65
- {var labels=prop['chart.labels.axes.specific'];var bold=RG.arrayPad(prop['chart.labels.axes.bold'],labels.length);var boxed=RG.arrayPad(prop['chart.labels.axes.boxed'],labels.length);var reversed_labels=RG.arrayReverse(labels);var reversed_bold=RG.arrayReverse(bold);var reversed_boxed=RG.arrayReverse(boxed);var font=prop['chart.text.font'];var axes=prop['chart.labels.axes'].toLowerCase();co.fillStyle=prop['chart.text.color'];var textConf=RG.getTextConf({object:this,prefix:'chart.labels.axes'});for(var i=0;i<labels.length;++i){if(axes.indexOf('n')>-1)RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,tag:'labels.axes.specific',x:this.centerx,y:this.centery-this.radius+((this.radius/labels.length)*i),text:reversed_labels[i],valign:'center',halign:'center',bounding:reversed_boxed[i],boundingFill:'white'});if(axes.indexOf('s')>-1)RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,tag:'labels.axes.specific',x:this.centerx,y:this.centery+((this.radius/labels.length)*(i+1)),text:labels[i],valign:'center',halign:'center',bounding:boxed[i],boundingFill:'white'});if(axes.indexOf('w')>-1)RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,tag:'labels.axes.specific',x:this.centerx-this.radius+((this.radius/labels.length)*i),y:this.centery,text:reversed_labels[i],valign:'center',halign:'center',bounding:reversed_boxed[i],boundingFill:'white'});if(axes.indexOf('e')>-1)RG.text2(this,{font:textConf.font,size:textConf.size,color:textConf.color,bold:textConf.bold,italic:textConf.italic,tag:'labels.axes.specific',x:this.centerx+((this.radius/labels.length)*(i+1)),y:this.centery,text:labels[i],valign:'center',halign:'center',bounding:boxed[i],boundingFill:'white'});}};this.getShape=this.getPoint=function(e)
66
- {for(var i=0;i<this.coords.length;++i){var x=this.coords[i][0];var y=this.coords[i][1];var tooltips=prop['chart.tooltips'];var index=Number(i);var mouseXY=RG.getMouseXY(e);var mouseX=mouseXY[0];var mouseY=mouseXY[1];if(mouseX<(x+5)&&mouseX>(x-5)&&mouseY>(y-5)&&mouseY<(y+5)){var tooltip=RG.parseTooltipText(prop['chart.tooltips'],index);return{0:this,'object':this,1:x,'x':x,2:y,'y':y,3:null,'dataset':null,4:index,'index':i,'tooltip':tooltip}}}};this.highlight=this.Highlight=function(shape)
67
- {if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else{RG.Highlight.Point(this,shape);}};this.getObjectByXY=function(e)
68
- {var mouseXY=RG.getMouseXY(e);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)){return this;}};this.drawHighlights=this.DrawHighlights=function()
69
- {if(prop['chart.highlights']){var sequentialIdx=0;var dataset=0;var index=0;var radius=prop['chart.highlights.radius'];for(var dataset=0;dataset<this.data.length;++dataset){for(var index=0;index<this.data[dataset].length;++index){co.beginPath();co.strokeStyle=prop['chart.highlights.stroke'];co.fillStyle=prop['chart.highlights.fill']?prop['chart.highlights.fill']:((typeof(prop['chart.colors.stroke'])=='object'&&prop['chart.colors.stroke'][dataset])?prop['chart.colors.stroke'][dataset]:prop['chart.colors.stroke']);co.arc(this.coords[sequentialIdx][0],this.coords[sequentialIdx][1],radius,0,RG.TWOPI,false);co.stroke();co.fill();++sequentialIdx;}}}};this.getRadius=function(value)
70
- {if(value<0||value>this.max){return null;}
71
- var radius=(value/this.max)*this.radius;return radius;};this.getAngle=function(numitems,index)
72
- {var angle=(RG.TWOPI/numitems)*index;angle-=RG.HALFPI;return angle;};this.parseColors=function()
73
- {if(this.original_colors.length===0){this.original_colors['chart.colors']=RG.arrayClone(prop['chart.colors']);this.original_colors['chart.key.colors']=RG.arrayClone(prop['chart.key.colors']);this.original_colors['chart.title.color']=RG.arrayClone(prop['chart.title.color']);this.original_colors['chart.text.color']=RG.arrayClone(prop['chart.text.color']);this.original_colors['chart.labels.color']=RG.arrayClone(prop['chart.labels.color']);this.original_colors['chart.labels.axes.color']=RG.arrayClone(prop['chart.labels.axes.color']);this.original_colors['chart.highlight.stroke']=RG.arrayClone(prop['chart.highlight.stroke']);this.original_colors['chart.highlight.fill']=RG.arrayClone(prop['chart.highlight.fill']);this.original_colors['chart.circle.fill']=RG.arrayClone(prop['chart.circle.fill']);this.original_colors['chart.circle.stroke']=RG.arrayClone(prop['chart.circle.stroke']);this.original_colors['chart.colors.stroke']=RG.arrayClone(prop['chart.colors.stroke']);}
74
- for(var i=0;i<prop['chart.colors'].length;++i){prop['chart.colors'][i]=this.parseSingleColorForGradient(prop['chart.colors'][i]);}
75
- var keyColors=prop['chart.key.colors'];if(typeof(keyColors)!='null'&&keyColors&&keyColors.length){for(var i=0;i<prop['chart.key.colors'].length;++i){prop['chart.key.colors'][i]=this.parseSingleColorForGradient(prop['chart.key.colors'][i]);}}
76
- prop['chart.title.color']=this.parseSingleColorForGradient(prop['chart.title.color']);prop['chart.text.color']=this.parseSingleColorForGradient(prop['chart.text.color']);prop['chart.labels.color']=this.parseSingleColorForGradient(prop['chart.labels.color']);prop['chart.labels.axes.color']=this.parseSingleColorForGradient(prop['chart.labels.axes.color']);prop['chart.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.highlight.stroke']);prop['chart.highlight.fill']=this.parseSingleColorForGradient(prop['chart.highlight.fill']);prop['chart.circle.fill']=this.parseSingleColorForGradient(prop['chart.circle.fill']);prop['chart.circle.stroke']=this.parseSingleColorForGradient(prop['chart.circle.stroke']);if(typeof prop['chart.colors.stroke']==='object'){for(var i=0;i<prop['chart.colors.stroke'].length;++i){prop['chart.colors.stroke'][i]=this.parseSingleColorForGradient(prop['chart.colors.stroke'][i]);}}else{prop['chart.colors.stroke']=this.parseSingleColorForGradient(prop['chart.colors.stroke']);}};this.reset=function()
77
- {};this.parseSingleColorForGradient=function(color)
78
- {if(!color||typeof(color)!='string'){return color;}
79
- if(color.match(/^gradient\((.*)\)$/i)){if(color.match(/^gradient\(({.*})\)$/i)){return RGraph.parseJSONGradient({object:this,def:RegExp.$1});}
80
- 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]));}}
81
- return grad?grad:color;};this.addFillListeners=this.AddFillListeners=function(e)
82
- {var obj=this;var func=function(e)
83
- {var coords=this.coords;var coords2=this.coords2;var mouseXY=RG.getMouseXY(e);var dataset=0;if(e.type=='mousemove'&&prop['chart.fill.mousemove.redraw']){RG.RedrawCanvas(ca);}
84
- for(var dataset=(obj.coords2.length-1);dataset>=0;--dataset){co.beginPath();co.moveTo(obj.coords2[dataset][0][0],obj.coords2[dataset][0][1]);for(var j=0;j<obj.coords2[dataset].length;++j){co.lineTo(obj.coords2[dataset][j][0],obj.coords2[dataset][j][1]);}
85
- co.lineTo(obj.coords2[dataset][0][0],obj.coords2[dataset][0][1]);if(prop['chart.accumulative']&&dataset>0){co.lineTo(obj.coords2[dataset-1][0][0],obj.coords2[dataset-1][0][1]);for(var j=(obj.coords2[dataset-1].length-1);j>=0;--j){co.lineTo(obj.coords2[dataset-1][j][0],obj.coords2[dataset-1][j][1]);}}
86
- co.closePath();if(co.isPointInPath(mouseXY[0],mouseXY[1])){var inPath=true;break;}}
87
- if(inPath){var fillTooltips=prop['chart.fill.tooltips'];if(e.type=='click'){if(prop['chart.fill.click']){prop['chart.fill.click'](e,dataset);}
88
- if(prop['chart.fill.tooltips']&&prop['chart.fill.tooltips'][dataset]){obj.DatasetTooltip(e,dataset);}}
89
- if(e.type=='mousemove'){if(prop['chart.fill.mousemove']){prop['chart.fill.mousemove'](e,dataset);}
90
- if(!RG.is_null(fillTooltips)){e.target.style.cursor='pointer';}
91
- if(prop['chart.fill.tooltips']&&prop['chart.fill.tooltips'][dataset]){e.target.style.cursor='pointer';}}
92
- e.stopPropagation();}else if(e.type=='mousemove'){ca.style.cursor='default';}};if(prop['chart.fill.click']||!RG.is_null(prop['chart.fill.tooltips'])){ca.addEventListener('click',func,false);}
93
- if(prop['chart.fill.mousemove']||!RG.isNull(prop['chart.fill.tooltips'])){ca.addEventListener('mousemove',func,false);}};this.highlightDataset=this.HighlightDataset=function(dataset)
94
- {co.beginPath();for(var j=0;j<this.coords2[dataset].length;++j){if(j==0){co.moveTo(this.coords2[dataset][0][0],this.coords2[dataset][0][1]);}else{co.lineTo(this.coords2[dataset][j][0],this.coords2[dataset][j][1]);}}
95
- co.lineTo(this.coords2[dataset][0][0],this.coords2[dataset][0][1]);if(prop['chart.accumulative']&&dataset>0){co.lineTo(this.coords2[dataset-1][0][0],this.coords2[dataset-1][0][1]);for(var j=(this.coords2[dataset-1].length-1);j>=0;--j){co.lineTo(this.coords2[dataset-1][j][0],this.coords2[dataset-1][j][1]);}}
96
- co.strokeStyle=prop['chart.fill.highlight.stroke'];co.fillStyle=prop['chart.fill.highlight.fill'];co.stroke();co.fill();};this.datasetTooltip=this.DatasetTooltip=function(e,dataset)
97
- {this.HighlightDataset(dataset);var text=prop['chart.fill.tooltips'][dataset];var x=0;var y=this.coords2[dataset][0][1]+RG.getCanvasXY(ca)[1];RG.Tooltip(this,text,x,y,0,e);};this.interactiveKeyHighlight=function(index)
98
- {var coords=this.coords2[index];if(coords){var pre_linewidth=co.lineWidth;var pre_linecap=co.lineCap;co.lineWidth=prop['chart.linewidth']+10;co.lineCap='round';co.strokeStyle=prop['chart.key.interactive.highlight.chart.stroke'];co.beginPath();for(var i=0,len=coords.length;i<len;i+=1){if(i==0){co.moveTo(coords[i][0],coords[i][1]);}else{co.lineTo(coords[i][0],coords[i][1]);}}
99
- co.closePath();co.stroke();co.lineWidth=pre_linewidth;co.lineCap=pre_linecap;}};this.on=function(type,func)
100
- {if(type.substr(0,2)!=='on'){type='on'+type;}
101
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
102
- return this;};this.firstDrawFunc=function()
103
- {};this.grow=function()
104
- {var obj=this;var callback=arguments[1]?arguments[1]:function(){};var opt=arguments[0]?arguments[0]:{};var frames=opt.frames?opt.frames:30;var frame=0;var data=RG.array_clone(obj.data);function iterator()
105
- {for(var i=0,len=data.length;i<len;++i){for(var j=0,len2=data[i].length;j<len2;++j){obj.original_data[i][j]=(frame/frames)*data[i][j];}}
106
- RGraph.clear(obj.canvas);RGraph.redrawCanvas(obj.canvas);if(frame<frames){frame++;RGraph.Effects.updateCanvas(iterator);}else{callback(obj);}}
107
- iterator();return this;};this.trace=function()
108
- {var obj=this;var opt=arguments[0]||{};var frames=opt.frames||60;var frame=0;var callback=arguments[1]||function(){};obj.set('chart.animation.trace.clip',0);var iterator=function()
109
- {if(frame<frames){obj.set('chart.animation.trace.clip',frame/frames);frame++;RG.redrawCanvas(obj.canvas);RG.Effects.updateCanvas(iterator);}else{obj.set('chart.animation.trace.clip',1);RG.redrawCanvas(obj.canvas);callback(obj);}};iterator();return this;};RG.register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};
12
+ RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true};
13
+
14
+ //
15
+ // The traditional radar chart constructor
16
+ //
17
+ // @param string id The ID of the canvas
18
+ // @param array data An array of data to represent
19
+ //
20
+ RGraph.Radar = function (conf)
21
+ {
22
+ conf.data = RGraph.stringsToNumbers(conf.data);
23
+
24
+ if (typeof conf.data[0] === 'number') {
25
+ conf.data = [conf.data];
26
+ }
27
+
28
+ this.id = conf.id;
29
+ this.canvas = document.getElementById(conf.id);
30
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
31
+ this.canvas.__object__ = this;
32
+ this.type = 'radar';
33
+ this.isRGraph = true;
34
+ this.isrgraph = true;
35
+ this.rgraph = true;
36
+ this.data = [];
37
+ this.max = 0;
38
+ this.uid = RGraph.createUID();
39
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID();
40
+ this.colorsParsed = false;
41
+ this.coords = [];
42
+ this.coordsText = [];
43
+ this.original_data = [];
44
+ this.original_colors = [];
45
+ this.firstDraw = true; // After the first draw this will be false
46
+ this.stopAnimationRequested = false;// Used to control the animations
47
+
48
+
49
+
50
+
51
+
52
+ //
53
+ // Add the data to the .original_data array and work out the max value
54
+ //
55
+ // 2/5/14 Now also use this loop to ensure that the data pieces
56
+ // are numbers
57
+ //
58
+ for (var i=0,len=conf.data.length; i<len; ++i) {
59
+
60
+ // Convert strings to numbers
61
+ for (var j=0; j<conf.data[i].length; ++j) {
62
+ if (typeof conf.data[i][j] === 'string') {
63
+ conf.data[i][j] = parseFloat(conf.data[i][j]);
64
+ }
65
+ }
66
+
67
+ this.original_data.push(RGraph.arrayClone(conf.data[i]));
68
+ this.data.push(RGraph.arrayClone(conf.data[i]));
69
+ this.max = Math.max(this.max, RGraph.arrayMax(conf.data[i]));
70
+
71
+ // Keep a copy of the unmodified_data
72
+ this.unmodified_data = RGraph.arrayClone(this.original_data);
73
+
74
+ }
75
+
76
+
77
+ this.properties =
78
+ {
79
+ marginLeft: 35,
80
+ marginRight: 35,
81
+ marginTop: 35,
82
+ marginBottom: 35,
83
+
84
+ linewidth: 1,
85
+
86
+ colorsStroke: '#aaa',
87
+ colors: ['rgba(255,0,0,0.75)','rgba(0,255,255,0.25)','rgba(255,0,0,0.5)', 'red', 'green', 'blue', 'pink', 'aqua','brown','orange','grey'],
88
+ colorsAlpha: null,
89
+ circle: 0,
90
+
91
+ circleFill: 'red',
92
+ circleStroke: 'black',
93
+
94
+ labels: [],
95
+ labelsFormattedDecimals: 0,
96
+ labelsFormattedPoint: '.',
97
+ labelsFormattedThousand: ',',
98
+ labelsFormattedUnitsPre: '',
99
+ labelsFormattedUnitsPost: '',
100
+ labelsFont: null,
101
+ labelsSize: null,
102
+ labelsColor: null,
103
+ labelsBold: null,
104
+ labelsSize: null,
105
+ labelsOffsetRadius: 0,
106
+
107
+ labelsBackgroundFill: 'white',
108
+ labelsBoxed: false,
109
+
110
+ labelsAxes: '',
111
+ labelsAxesFont: null,
112
+ labelsAxesSize: null,
113
+ labelsAxesColor: null,
114
+ labelsAxesBold: null,
115
+ labelsAxesItalic: null,
116
+ labelsAxesBoxed: null, // This defaults to true - but that's set in the Draw() method
117
+ labelsAxesBoxedZero: true,
118
+ labelsAxesBoxedBackground: 'rgba(255,255,255,0.7)',
119
+ labelsAxesSpecific: null,
120
+ labelsAxesCount: 5,
121
+ labelsAxesOffsetx: 0,
122
+ labelsAxesOffsety: 0,
123
+
124
+ backgroundGrid: true,
125
+ backgroundGridCount: null,
126
+ backgroundGridColor: '#ddd',
127
+ backgroundGridPoly: true,
128
+ backgroundGridSpokes: 24,
129
+
130
+ textSize: 12,
131
+ textFont: 'Arial, Verdana, sans-serif',
132
+ textColor: 'black',
133
+ textBold: false,
134
+ textItalic: false,
135
+ textAccessible: false,
136
+ textAccessibleOverflow: 'visible',
137
+ textAccessiblePointerevents: false,
138
+ text: null,
139
+
140
+ title: '',
141
+ titleColor: null,
142
+ titleBold: null,
143
+ titleItalic: null,
144
+ titleSize: null,
145
+ titleFont: null,
146
+ titleX: null,
147
+ titleY: null,
148
+ titleHalign: null,
149
+ titleValign: null,
150
+ titleOffsetx: 0,
151
+ titleOffsety: 0,
152
+ titleSubtitle: '',
153
+ titleSubtitleSize: null,
154
+ titleSubtitleColor: '#aaa',
155
+ titleSubtitleFont: null,
156
+ titleSubtitleBold: null,
157
+ titleSubtitleItalic: null,
158
+ titleSubtitleOffsetx: 0,
159
+ titleSubtitleOffsety: 0,
160
+
161
+ linewidth: 1,
162
+
163
+ key: null,
164
+ keyBackground: 'white',
165
+ keyShadow: false,
166
+ keyShadowColor: '#666',
167
+ keyShadowBlur: 3,
168
+ keyShadowOffsetx: 2,
169
+ keyShadowOffsety: 2,
170
+ keyPosition: 'graph',
171
+ keyHalign: 'right',
172
+ keyPositionGutterBoxed: false,
173
+ keyPositionX: null,
174
+ keyPositionY: null,
175
+ keyColorShape: 'square',
176
+ keyRounded: true,
177
+ keyLinewidth: 1,
178
+ keyColors: null,
179
+ keyInteractive: false,
180
+ keyInteractiveHighlightChartStroke: 'rgba(255,0,0,0.3)',
181
+ keyInteractiveHighlightLabel: 'rgba(255,0,0,0.2)',
182
+ keyLabelsColor: null,
183
+ keyLabelsFont: null,
184
+ keyLabelsSize: null,
185
+ keyLabelsBold: null,
186
+ keyLabelsItalic: null,
187
+ keyLabelsOffsetx: 0,
188
+ keyLabelsOffsety: 0,
189
+ keyFormattedDecimals: 0,
190
+ keyFormattedPoint: '.',
191
+ keyFormattedThousand: ',',
192
+ keyFormattedUnitsPre: '',
193
+ keyFormattedUnitsPost: '',
194
+ keyFormattedValueSpecific: null,
195
+ keyFormattedItemsCount: null,
196
+
197
+ contextmenu: null,
198
+
199
+ annotatable: false,
200
+ annotateColor: 'black',
201
+ annotateLinewidth: 1,
202
+
203
+ tooltips: null,
204
+ tooltipsEvent: 'mousemove',
205
+ tooltipsEffect: 'slide',
206
+ tooltipsCssClass: 'RGraph_tooltip',
207
+ tooltipsCss: null,
208
+ tooltipsHighlight: true,
209
+ tooltipsNohideonclear: false,
210
+ tooltipsFormattedThousand: ',',
211
+ tooltipsFormattedPoint: '.',
212
+ tooltipsFormattedDecimals: 0,
213
+ tooltipsFormattedUnitsPre: '',
214
+ tooltipsFormattedUnitsPost: '',
215
+ tooltipsFormattedKeyColors: null,
216
+ tooltipsFormattedKeyColorsShape: 'square',
217
+ tooltipsFormattedKeyLabels: [],
218
+ tooltipsFormattedListType: 'ul',
219
+ tooltipsFormattedListItems: null,
220
+ tooltipsFormattedTableHeaders: null,
221
+ tooltipsFormattedTableData: null,
222
+ tooltipsPointer: true,
223
+ tooltipsPointerOffsetx: 0,
224
+ tooltipsPointerOffsety: 0,
225
+ tooltipsPositionStatic: true,
226
+ tooltipsHotspotIgnore: null,
227
+
228
+ highlightStroke: 'gray',
229
+ highlightFill: 'rgba(255,255,255,0.7)',
230
+ highlightPointRadius: 2,
231
+
232
+ resizable: false,
233
+ resizeHandleAdjust: [0,0],
234
+ resizeHandleBackground: null,
235
+
236
+ scaleMax: null,
237
+ scaleDecimals: 0,
238
+ scalePoint: '.',
239
+ scaleThousand: ',',
240
+ scaleUnitsPre: '',
241
+ scaleUnitsPost: '',
242
+
243
+ accumulative: false,
244
+
245
+ radius: null,
246
+
247
+ centerx: null,
248
+ centery: null,
249
+ radius: null,
250
+
251
+ xaxisTickmarksCount: 5,
252
+
253
+ yaxisTickmarksCount: 5,
254
+
255
+ axesColor: 'rgba(0,0,0,0)',
256
+
257
+ highlights: false,
258
+ highlightsStroke: '#ddd',
259
+ highlightsFill: null,
260
+ highlightsRadius: 3,
261
+ highlightStyleInvertStroke: 'transparent',
262
+ highlightStyleInvertFill: 'rgba(255,255,255,0.7)',
263
+
264
+ fillClick: null,
265
+ fillMousemove: null,
266
+ fillTooltips: null,
267
+ fillHighlightFill: 'rgba(255,255,255,0.7)',
268
+ fillHighlightStroke: 'rgba(0,0,0,0)',
269
+ fillMousemoveRedraw: false,
270
+
271
+ animationTraceClip: 1,
272
+
273
+ clearto: 'rgba(0,0,0,0)'
274
+ }
275
+
276
+
277
+
278
+ // Must have at least 3 points
279
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
280
+ if (this.data[dataset].length < 3) {
281
+ alert('[RADAR] You must specify at least 3 data points');
282
+ return;
283
+ }
284
+ }
285
+
286
+
287
+ //
288
+ // Linearize the data and then create the $ objects
289
+ //
290
+ var idx = 0;
291
+ this.data_arr = [];
292
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
293
+ for (var i=0,len=this.data[dataset].length; i<len; ++i) {
294
+ this['$' + (idx++)] = {};
295
+ this.data_arr.push(this.data[dataset][i])
296
+ }
297
+ }
298
+
299
+
300
+
301
+
302
+ // Easy access to properties and the path function
303
+ var properties = this.properties;
304
+ this.path = RGraph.pathObjectFunction;
305
+
306
+
307
+
308
+ //
309
+ // "Decorate" the object with the generic effects if the effects library has been included
310
+ //
311
+ if (RGraph.Effects && typeof RGraph.Effects.decorate === 'function') {
312
+ RGraph.Effects.decorate(this);
313
+ }
314
+
315
+
316
+
317
+ // Add the responsive method. This method resides in the common file.
318
+ this.responsive = RGraph.responsive;
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+ //
328
+ // A simple setter
329
+ //
330
+ this.set = function (name)
331
+ {
332
+
333
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
334
+
335
+ if (name === 'labelsOffset') {
336
+ name = 'labelsOffsetRadius';
337
+ }
338
+
339
+ // Change backgroundCircles to backgroundGrid
340
+ if (name.startsWith('backgroundCircles')) {
341
+ name = name.replace(/^backgroundCircles/,'backgroundGrid');
342
+ }
343
+
344
+ // the number of arguments is only one and it's an
345
+ // object - parse it for configuration data and return.
346
+ if (arguments.length === 1 && typeof arguments[0] === 'object') {
347
+ for (i in arguments[0]) {
348
+ if (typeof i === 'string') {
349
+ this.set(i, arguments[0][i]);
350
+ }
351
+ }
352
+
353
+ return this;
354
+ }
355
+
356
+ properties[name] = value;
357
+
358
+ return this;
359
+ };
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+ //
369
+ // A simple getter
370
+ //
371
+ // @param string name The name of the property to get
372
+ //
373
+ this.get = function (name)
374
+ {
375
+ return properties[name];
376
+ };
377
+
378
+
379
+
380
+
381
+
382
+
383
+
384
+
385
+ //
386
+ // The draw method which does all the brunt of the work
387
+ //
388
+ this.draw = function ()
389
+ {
390
+ //
391
+ // Fire the onbeforedraw event
392
+ //
393
+ RGraph.fireCustomEvent(this, 'onbeforedraw');
394
+
395
+
396
+
397
+ // Translate half a pixel for antialiasing purposes - but only if it hasn't been
398
+ // done already
399
+ //
400
+ // MUST be the first thing done!
401
+ //
402
+ if (!this.canvas.__rgraph_aa_translated__) {
403
+ this.context.translate(0.5,0.5);
404
+
405
+ this.canvas.__rgraph_aa_translated__ = true;
406
+ }
407
+
408
+
409
+ // NB: Colors are parsed further down
410
+
411
+ // Reset the coords array to stop it growing
412
+ this.coords = [];
413
+ this.coords2 = [];
414
+ this.coordsText = [];
415
+
416
+ //
417
+ // Reset the data to the original_data
418
+ //
419
+ this.data = RGraph.arrayClone(this.original_data);
420
+
421
+ // Loop thru the data array if the accumulative option is enable checking to see if all the
422
+ // datasets have the same number of elements.
423
+ if (properties.accumulative) {
424
+ for (var i=0; i<this.data.length; ++i) {
425
+ if (this.data[i].length != this.data[0].length) {
426
+ alert('[RADAR] Error! When the radar has the accumulative option set to true all the datasets must have the same number of elements');
427
+ }
428
+ }
429
+ }
430
+
431
+
432
+ //
433
+ // This defaults to true, but needs to be an array with a size matching the number of
434
+ // labels.
435
+ //
436
+ if (RGraph.isNull(properties.labelsAxesBoxed)) {
437
+ properties.labelsAxesBoxed = [];
438
+ for (var i=0; i<( (RGraph.isArray(properties.labelsAxesSpecific) && properties.labelsAxesSpecific.length) || properties.labelsAxesCount || 5); ++i) {
439
+ properties.labelsAxesBoxed[i] = false;
440
+ }
441
+ }
442
+
443
+
444
+
445
+ //
446
+ // Make the margins easy to access
447
+ //
448
+ this.marginLeft = properties.marginLeft;
449
+ this.marginRight = properties.marginRight;
450
+ this.marginTop = properties.marginTop;
451
+ this.marginBottom = properties.marginBottom;
452
+
453
+ this.centerx = ((this.canvas.width - this.marginLeft - this.marginRight) / 2) + this.marginLeft;
454
+ this.centery = ((this.canvas.height - this.marginTop - this.marginBottom) / 2) + this.marginTop;
455
+ this.radius = Math.min(this.canvas.width - this.marginLeft - this.marginRight, this.canvas.height - this.marginTop - this.marginBottom) / 2;
456
+
457
+
458
+
459
+ //
460
+ // Allow these to be set by hand
461
+ //
462
+ if (typeof properties.centerx === 'number') this.centerx = 2 * properties.centerx;
463
+ if (typeof properties.centery === 'number') this.centery = 2 * properties.centery;
464
+ if (typeof properties.radius === 'number') this.radius = properties.radius;
465
+
466
+
467
+ //
468
+ // Parse the colors for gradients. Its down here so that the center X/Y can be used
469
+ //
470
+ if (!this.colorsParsed) {
471
+
472
+ this.parseColors();
473
+
474
+ // Don't want to do this again
475
+ this.colorsParsed = true;
476
+ }
477
+
478
+
479
+
480
+ // Work out the maximum value and the sum
481
+ if (!properties.scaleMax) {
482
+
483
+ // this.max is calculated in the constructor
484
+
485
+ // Work out this.max again if the chart is (now) set to be accumulative
486
+ if (properties.accumulative) {
487
+
488
+ var accumulation = [];
489
+ var len = this.original_data[0].length
490
+
491
+ for (var i=1; i<this.original_data.length; ++i) {
492
+ if (this.original_data[i].length != len) {
493
+ alert('[RADAR] Error! Stacked Radar chart datasets must all be the same size!');
494
+ }
495
+
496
+ for (var j=0; j<this.original_data[i].length; ++j) {
497
+ this.data[i][j] += this.data[i - 1][j];
498
+ this.max = Math.max(this.max, this.data[i][j]);
499
+ }
500
+ }
501
+ }
502
+
503
+
504
+ this.scale2 = RGraph.getScale({object: this, options: {
505
+ 'scale.max': typeof properties.scaleMax === 'number' ? properties.scaleMax : this.max,
506
+ 'scale.min': 0,
507
+ 'scale.decimals': Number(properties.scaleDecimals),
508
+ 'scale.point': properties.scalePoint,
509
+ 'scale.thousand': properties.scaleThousand,
510
+ 'scale.round': properties.scaleRound,
511
+ 'scale.units.pre': properties.scaleUnitsPre,
512
+ 'scale.units.post': properties.scaleUnitsPost,
513
+ 'scale.labels.count': properties.labelsAxesCount
514
+ }});
515
+ this.max = this.scale2.max;
516
+
517
+ } else {
518
+
519
+ var ymax = properties.scaleMax;
520
+
521
+ this.scale2 = RGraph.getScale({object: this, options: {
522
+ 'scale.max': ymax,
523
+ 'scale.min': 0,
524
+ 'scale.strict': true,
525
+ 'scale.decimals': Number(properties.scaleDecimals),
526
+ 'scale.point': properties.scalePoint,
527
+ 'scale.thousand': properties.scaleThousand,
528
+ 'scale.round': properties.scaleRound,
529
+ 'scale.units.pre': properties.scaleUnitsPre,
530
+ 'scale.units.post': properties.scaleUnitsPost,
531
+ 'scale.labels.count': properties.labelsAxesCount
532
+ }});
533
+ this.max = this.scale2.max;
534
+ }
535
+
536
+ this.drawBackground();
537
+ this.drawAxes();
538
+ this.drawCircle();
539
+ this.drawLabels();
540
+
541
+
542
+ //
543
+ // Allow clipping (for the trace() effect for example)
544
+ //
545
+ this.context.save();
546
+ this.context.beginPath();
547
+ this.context.arc(this.centerx, this.centery, this.radius * 2, -RGraph.HALFPI, (RGraph.TWOPI * properties.animationTraceClip) - RGraph.HALFPI, false);
548
+ this.context.lineTo(this.centerx, this.centery);
549
+ this.context.closePath();
550
+ this.context.clip();
551
+
552
+ this.drawChart();
553
+ this.drawHighlights();
554
+ this.context.restore();
555
+
556
+ //
557
+ // Draw the axis labels
558
+ //
559
+ this.drawAxisLabels();
560
+
561
+ // Draw the title
562
+ if (properties.title) {
563
+ RGraph.drawTitle(this);
564
+ }
565
+
566
+ // Draw the key if necessary
567
+ // obj, key, colors
568
+ if (properties.key) {
569
+ RGraph.drawKey(this, properties.key, properties.colors);
570
+ }
571
+
572
+ //
573
+ // Show the context menu
574
+ //
575
+ if (properties.contextmenu) {
576
+ RGraph.showContext(this);
577
+ }
578
+
579
+
580
+
581
+
582
+ //
583
+ // Add custom text thats specified
584
+ //
585
+ RGraph.addCustomText(this);
586
+
587
+
588
+
589
+
590
+
591
+
592
+
593
+
594
+ //
595
+ // This installs the event listeners
596
+ //
597
+ RGraph.installEventListeners(this);
598
+
599
+ //
600
+ // This installs the Radar chart specific area listener
601
+ //
602
+ if ( (properties.fillClick || properties.fillMousemove || !RGraph.isNull(properties.fillTooltips)) && !this.__fill_click_listeners_installed__) {
603
+ this.addFillListeners();
604
+ this.__fill_click_listeners_installed__ = true;
605
+ }
606
+
607
+
608
+
609
+ //
610
+ // Fire the onfirstdraw event
611
+ //
612
+ if (this.firstDraw) {
613
+ this.firstDraw = false;
614
+ RGraph.fireCustomEvent(this, 'onfirstdraw');
615
+ this.firstDrawFunc();
616
+ }
617
+
618
+
619
+
620
+
621
+ //
622
+ // Fire the RGraph draw event
623
+ //
624
+ RGraph.fireCustomEvent(this, 'ondraw');
625
+
626
+
627
+
628
+
629
+
630
+
631
+
632
+
633
+
634
+ //
635
+ // Install any inline responsive configuration. This
636
+ // should be last in the draw function - even after
637
+ // the draw events.
638
+ //
639
+ RGraph.installInlineResponsive(this);
640
+
641
+
642
+
643
+
644
+
645
+
646
+
647
+
648
+
649
+
650
+
651
+
652
+ return this;
653
+ };
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+ //
663
+ // Used in chaining. Runs a function there and then - not waiting for
664
+ // the events to fire (eg the onbeforedraw event)
665
+ //
666
+ // @param function func The function to execute
667
+ //
668
+ this.exec = function (func)
669
+ {
670
+ func(this);
671
+
672
+ return this;
673
+ };
674
+
675
+
676
+
677
+
678
+
679
+
680
+
681
+
682
+ //
683
+ // Draws the background circles
684
+ //
685
+ this.drawBackground = function ()
686
+ {
687
+ var color = properties.backgroundGridColor;
688
+ var poly = properties.backgroundGridPoly;
689
+ var spacing = properties.backgroundGridSpacing;
690
+ var spokes = properties.backgroundGridSpokes;
691
+
692
+
693
+
694
+
695
+ // Set the linewidth for the grid (so that repeated redrawing works OK)
696
+ this.context.lineWidth = 1;
697
+
698
+
699
+
700
+
701
+ //
702
+ // Draws the background circles
703
+ //
704
+ if (properties.backgroundGrid && poly == false) {
705
+
706
+
707
+
708
+
709
+
710
+ // Draw the concentric circles
711
+ this.context.strokeStyle = color;
712
+ this.context.beginPath();
713
+
714
+ var numrings = typeof properties.backgroundGridCount == 'number' ? properties.backgroundGridCount : properties.labelsAxesCount;
715
+
716
+ // TODO Currently set to 5 - needs changing
717
+ for (var r=0; r<=this.radius; r+=(this.radius / numrings)) {
718
+ this.context.moveTo(this.centerx, this.centery);
719
+ this.context.arc(this.centerx, this.centery,r, 0, RGraph.TWOPI, false);
720
+ }
721
+ this.context.stroke();
722
+
723
+
724
+
725
+
726
+
727
+ //
728
+ // Draw the diagonals/spokes
729
+ //
730
+ this.context.strokeStyle = color;
731
+
732
+ for (var i=0; i<360; i+=(360 / spokes)) {
733
+ this.context.beginPath();
734
+ this.context.arc(
735
+ this.centerx,
736
+ this.centery,
737
+ this.radius,
738
+ (i / 360) * RGraph.TWOPI,
739
+ ((i+0.001) / 360) * RGraph.TWOPI,
740
+ false
741
+ ); // The 0.01 avoids a bug in Chrome 6
742
+ this.context.lineTo(this.centerx, this.centery);
743
+ this.context.stroke();
744
+ }
745
+
746
+
747
+
748
+
749
+
750
+
751
+ //
752
+ // The background"circles" are actually drawn as a poly based on how
753
+ // many points there are
754
+ // (ie hexagons if there are 6 points, squares if there are four etc)
755
+ //
756
+ } else if (properties.backgroundGrid && poly == true) {
757
+
758
+ //
759
+ // Draw the diagonals/spokes
760
+ //
761
+ this.context.strokeStyle = color;
762
+ var increment = 360 / this.data[0].length
763
+
764
+ for (var i=0; i<360; i+=increment) {
765
+
766
+ this.context.beginPath();
767
+ this.context.arc(
768
+ this.centerx,
769
+ this.centery,
770
+ this.radius,
771
+ ((i / 360) * RGraph.TWOPI) - RGraph.HALFPI,
772
+ (((i + 0.001) / 360) * RGraph.TWOPI) - RGraph.HALFPI,
773
+ false); // The 0.001 avoids a bug in Chrome 6
774
+ this.context.lineTo(this.centerx, this.centery);
775
+ this.context.stroke();
776
+ }
777
+
778
+
779
+ //
780
+ // Draw the lines that go around the Radar chart
781
+ //
782
+ this.context.strokeStyle = color;
783
+
784
+ var numrings = typeof properties.backgroundGridCount === 'number' ? properties.backgroundGridCount : properties.labelsAxesCount;
785
+
786
+ for (var r=0; r<=this.radius; r+=(this.radius / numrings)) {
787
+ this.context.beginPath();
788
+ for (var a=0; a<=360; a+=(360 / this.data[0].length)) {
789
+ this.context.arc(
790
+ this.centerx,
791
+ this.centery,
792
+ r,
793
+ RGraph.toRadians(a) - RGraph.HALFPI,
794
+ RGraph.toRadians(a) + 0.001 - RGraph.HALFPI,
795
+ false
796
+ );
797
+ }
798
+ this.context.closePath();
799
+ this.context.stroke();
800
+ }
801
+ }
802
+ };
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+ //
812
+ // Draws the axes
813
+ //
814
+ this.drawAxes = function ()
815
+ {
816
+ this.context.strokeStyle = properties.axesColor;
817
+ this.context.lineWidth = properties.axesLinewidth;
818
+
819
+ var halfsize = this.radius;
820
+
821
+ this.context.beginPath();
822
+ //
823
+ // The Y axis
824
+ //
825
+ this.context.moveTo(Math.round(this.centerx), this.centery + this.radius);
826
+ this.context.lineTo(Math.round(this.centerx), this.centery - this.radius);
827
+
828
+
829
+ // Draw the bits at either end of the Y axis
830
+ this.context.moveTo(this.centerx - 5, Math.round(this.centery + this.radius));
831
+ this.context.lineTo(this.centerx + 5, Math.round(this.centery + this.radius));
832
+ this.context.moveTo(this.centerx - 5, Math.round(this.centery - this.radius));
833
+ this.context.lineTo(this.centerx + 5, Math.round(this.centery - this.radius));
834
+
835
+ // Draw Y axis tick marks
836
+ for (var y=(this.centery - this.radius); y<(this.centery + this.radius); y+=(this.radius/properties.yaxisTickmarksCount)) {
837
+ this.context.moveTo(this.centerx - 3, Math.round(y));
838
+ this.context.lineTo(this.centerx + 3, Math.round(y));
839
+ }
840
+
841
+ //
842
+ // The X axis
843
+ //
844
+ this.context.moveTo(this.centerx - this.radius, Math.round(this.centery));
845
+ this.context.lineTo(this.centerx + this.radius, Math.round(this.centery));
846
+
847
+ // Draw the bits at the end of the X axis
848
+ this.context.moveTo(Math.round(this.centerx - this.radius), this.centery - 5);
849
+ this.context.lineTo(Math.round(this.centerx - this.radius), this.centery + 5);
850
+ this.context.moveTo(Math.round(this.centerx + this.radius), this.centery - 5);
851
+ this.context.lineTo(Math.round(this.centerx + this.radius), this.centery + 5);
852
+
853
+ // Draw X axis tick marks
854
+ for (var x=(this.centerx - this.radius); x<(this.centerx + this.radius); x+=(this.radius/properties.xaxisTickmarksCount)) {
855
+ this.context.moveTo(Math.round(x), this.centery - 3);
856
+ this.context.lineTo(Math.round(x), this.centery + 3);
857
+ }
858
+
859
+ // Stroke it
860
+ this.context.stroke();
861
+ };
862
+
863
+
864
+
865
+
866
+
867
+
868
+
869
+
870
+ //
871
+ // The function which actually draws the radar chart
872
+ //
873
+ this.drawChart = function ()
874
+ {
875
+ var alpha = properties.colorsAlpha;
876
+
877
+ if (typeof alpha == 'number') {
878
+ var oldAlpha = this.context.globalAlpha;
879
+ this.context.globalAlpha = alpha;
880
+ }
881
+
882
+ var numDatasets = this.data.length;
883
+
884
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
885
+
886
+ this.context.beginPath();
887
+
888
+ var coords_dataset = [];
889
+
890
+ for (var i=0; i<this.data[dataset].length; ++i) {
891
+
892
+ var coords = this.getCoordinates(dataset, i);
893
+
894
+ if (coords_dataset == null) {
895
+ coords_dataset = [];
896
+ }
897
+
898
+ coords_dataset.push(coords);
899
+ this.coords.push(coords);
900
+ }
901
+
902
+ this.coords2[dataset] = coords_dataset;
903
+
904
+
905
+ //
906
+ // Now go through the coords and draw the chart itself
907
+ //
908
+ // 18/5/2012 - colorsStroke can now be an array of colors as well as a single color
909
+ //
910
+
911
+ this.context.strokeStyle = (typeof properties.colorsStroke == 'object' && properties.colorsStroke[dataset]) ? properties.colorsStroke[dataset] : properties.colorsStroke;
912
+ this.context.fillStyle = properties.colors[dataset] ? properties.colors[dataset] : 'rgba(0,0,0,0)';
913
+ if (this.context.fillStyle === 'transparent') {
914
+ this.context.fillStyle = 'rgba(0,0,0,0)';
915
+ }
916
+ this.context.lineWidth = properties.linewidth;
917
+
918
+ for (i=0; i<coords_dataset.length; ++i) {
919
+ if (i == 0) {
920
+ this.context.moveTo(coords_dataset[i][0], coords_dataset[i][1]);
921
+ } else {
922
+ this.context.lineTo(coords_dataset[i][0], coords_dataset[i][1]);
923
+ }
924
+ }
925
+
926
+
927
+ // If on the second or greater dataset, backtrack
928
+ if (properties.accumulative && dataset > 0) {
929
+
930
+ // This goes back to the start coords of this particular dataset
931
+ this.context.lineTo(coords_dataset[0][0], coords_dataset[0][1]);
932
+
933
+ //Now move down to the end point of the previous dataset
934
+ this.context.moveTo(last_coords[0][0], last_coords[0][1]);
935
+
936
+ for (var i=coords_dataset.length - 1; i>=0; --i) {
937
+ this.context.lineTo(last_coords[i][0], last_coords[i][1]);
938
+ }
939
+ }
940
+
941
+ // This is used by the next iteration of the loop
942
+ var last_coords = coords_dataset;
943
+
944
+ this.context.closePath();
945
+
946
+ this.context.fill();
947
+ this.context.stroke();
948
+ }
949
+
950
+ // Reset the globalAlpha
951
+ if (typeof alpha == 'number') {
952
+ this.context.globalAlpha = oldAlpha;
953
+ }
954
+ };
955
+
956
+
957
+
958
+
959
+
960
+
961
+
962
+
963
+ //
964
+ // Gets the coordinates for a particular mark
965
+ //
966
+ // @param number i The index of the data (ie which one it is)
967
+ // @return array A two element array of the coordinates
968
+ //
969
+ this.getCoordinates = function (dataset, index)
970
+ {
971
+ // The number of data points
972
+ var len = this.data[dataset].length;
973
+
974
+ // The magnitude of the data (NOT the x/y coords)
975
+ var mag = (this.data[dataset][index] / this.max) * this.radius;
976
+
977
+ //
978
+ // Get the angle
979
+ //
980
+ var angle = (RGraph.TWOPI / len) * index; // In radians
981
+ angle -= RGraph.HALFPI;
982
+
983
+
984
+ //
985
+ // Work out the X/Y coordinates
986
+ //
987
+ var x = Math.cos(angle) * mag;
988
+ var y = Math.sin(angle) * mag;
989
+
990
+ //
991
+ // Put the coordinate in the right quadrant
992
+ //
993
+ x = this.centerx + x;
994
+ y = this.centery + y;
995
+
996
+ return [x,y];
997
+ };
998
+
999
+
1000
+
1001
+
1002
+
1003
+
1004
+
1005
+
1006
+ //
1007
+ // This function adds the labels to the chart
1008
+ //
1009
+ this.drawLabels = function ()
1010
+ {
1011
+ if (properties.labels && properties.labels.length) {
1012
+ //
1013
+ // If the labels option is a string then turn it
1014
+ // into an array.
1015
+ //
1016
+ if (typeof properties.labels === 'string') {
1017
+ properties.labels = RGraph.arrayPad({
1018
+ array: [],
1019
+ length: this.data[0].length,
1020
+ value: properties.labels
1021
+ });
1022
+ }
1023
+
1024
+ for (var i=0; i<properties.labels.length; ++i) {
1025
+ properties.labels[i] = RGraph.labelSubstitution({
1026
+ object: this,
1027
+ text: properties.labels[i],
1028
+ index: i,
1029
+ value: this.data[0][i],
1030
+ decimals: properties.labelsFormattedDecimals || 0,
1031
+ unitsPre: properties.labelsFormattedUnitsPre || '',
1032
+ unitsPost: properties.labelsFormattedUnitsPost || '',
1033
+ thousand: properties.labelsFormattedThousand || ',',
1034
+ point: properties.labelsFormattedPoint || '.'
1035
+ });
1036
+ }
1037
+ }
1038
+
1039
+
1040
+
1041
+
1042
+
1043
+ var labels = properties.labels;
1044
+
1045
+ if (labels && labels.length > 0) {
1046
+
1047
+ this.context.lineWidth = 1;
1048
+ this.context.strokeStyle = 'gray';
1049
+ this.context.fillStyle = properties.labelsColor || properties.textColor;
1050
+
1051
+ var bgFill = properties.labelsBackgroundFill,
1052
+ bold = properties.labelsBold,
1053
+ bgBoxed = properties.labelsBoxed,
1054
+ offset = properties.labelsOffsetRadius,
1055
+ font = properties.textFont,
1056
+ size = properties.textSize,
1057
+ radius = this.radius,
1058
+ color = properties.labelsColor || properties.textColor
1059
+
1060
+ for (var i=0; i<labels.length; ++i) {
1061
+
1062
+ var angle = (RGraph.TWOPI / properties.labels.length) * i;
1063
+ angle -= RGraph.HALFPI;
1064
+
1065
+ var x = this.centerx + (Math.cos(angle) * (radius + 10 + offset));
1066
+ var y = this.centery + (Math.sin(angle) * (radius + 10 + offset));
1067
+
1068
+ //
1069
+ // Horizontal alignment
1070
+ //
1071
+ var halign = x < this.centerx ? 'right' : 'left' ;
1072
+ if (i == 0 || (i / labels.length) == 0.5) halign = 'center';
1073
+
1074
+ if (labels[i] && labels[i].length) {
1075
+
1076
+ var textConf = RGraph.getTextConf({
1077
+ object: this,
1078
+ prefix: 'labels'
1079
+ });
1080
+
1081
+ RGraph.text({
1082
+
1083
+ object: this,
1084
+
1085
+ font: textConf.font,
1086
+ size: textConf.size,
1087
+ color: textConf.color,
1088
+ bold: textConf.bold,
1089
+ italic: textConf.italic,
1090
+
1091
+ x: x,
1092
+ y: y,
1093
+ text: labels[i],
1094
+ valign: 'center',
1095
+ halign: halign,
1096
+ bounding: bgBoxed,
1097
+ boundingFill: bgFill,
1098
+ tag: 'labels',
1099
+ cssClass: RGraph.getLabelsCSSClassName({
1100
+ object: this,
1101
+ name: 'labelsClass',
1102
+ index: i
1103
+ })
1104
+ });
1105
+ }
1106
+ }
1107
+ }
1108
+ };
1109
+
1110
+
1111
+
1112
+
1113
+
1114
+
1115
+
1116
+
1117
+ //
1118
+ // Draws the circle. No arguments as it gets the information from the object properties.
1119
+ //
1120
+ this.drawCircle = function ()
1121
+ {
1122
+ var circle = {};
1123
+ circle.limit = properties.circle;
1124
+ circle.fill = properties.circleFill;
1125
+ circle.stroke = properties.circleStroke;
1126
+
1127
+ if (circle.limit) {
1128
+
1129
+ var r = (circle.limit / this.max) * this.radius;
1130
+
1131
+ this.context.fillStyle = circle.fill;
1132
+ this.context.strokeStyle = circle.stroke;
1133
+
1134
+ this.context.beginPath();
1135
+ this.context.arc(this.centerx, this.centery, r, 0, RGraph.TWOPI, 0);
1136
+ this.context.fill();
1137
+ this.context.stroke();
1138
+ }
1139
+ };
1140
+
1141
+
1142
+
1143
+
1144
+
1145
+
1146
+
1147
+
1148
+ //
1149
+ // Draws the labels
1150
+ //
1151
+ this.drawAxisLabels = function ()
1152
+ {
1153
+ //
1154
+ // Draw specific axis labels
1155
+ //
1156
+ if (RGraph.isArray(properties.labelsAxesSpecific) && properties.labelsAxesSpecific.length) {
1157
+ this.drawSpecificAxisLabels();
1158
+ return;
1159
+ }
1160
+
1161
+ this.context.lineWidth = 1;
1162
+
1163
+ // Set the color to black
1164
+ this.context.fillStyle = 'black';
1165
+ this.context.strokeStyle = 'black';
1166
+
1167
+ var r = this.radius,
1168
+ font = properties.textFont,
1169
+ axes = properties.labelsAxes.toLowerCase(),
1170
+ color = properties.labelsAxesBoxedBackground,
1171
+ drawzero = false,
1172
+ units_pre = properties.scaleUnitsPre,
1173
+ units_post = properties.scaleUnitsPost,
1174
+ decimals = properties.scaleDecimals,
1175
+ bold = properties.labelsAxesBold,
1176
+ boxed = properties.labelsAxesBoxed,
1177
+ centerx = this.centerx,
1178
+ centery = this.centery,
1179
+ scale = this.scale;
1180
+
1181
+ this.context.fillStyle = properties.textColor;
1182
+
1183
+ var textConf = RGraph.getTextConf({
1184
+ object: this,
1185
+ prefix: 'labelsAxes'
1186
+ });
1187
+
1188
+ // The "North" axis labels
1189
+ if (axes.indexOf('n') > -1) {
1190
+
1191
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1192
+ RGraph.text({
1193
+
1194
+ object: this,
1195
+
1196
+ font: textConf.font,
1197
+ size: textConf.size,
1198
+ color: textConf.color,
1199
+ bold: textConf.bold,
1200
+ italic: textConf.italic,
1201
+
1202
+ x: centerx + properties.labelsAxesOffsetx,
1203
+ y: centery - (r * ((i+1)/this.scale2.labels.length)) + properties.labelsAxesOffsety,
1204
+
1205
+ text: this.scale2.labels[i],
1206
+ valign: 'center',
1207
+ halign: 'center',
1208
+ bounding: boxed[i] || color,
1209
+ boundingFill: color,
1210
+ boundingStroke: 'rgba(0,0,0,0)',
1211
+ tag: 'scale'
1212
+ });
1213
+ }
1214
+
1215
+ drawzero = true;
1216
+ }
1217
+
1218
+ // The "South" axis labels
1219
+ if (axes.indexOf('s') > -1) {
1220
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1221
+ RGraph.text({
1222
+
1223
+ object: this,
1224
+
1225
+ font: textConf.font,
1226
+ size: textConf.size,
1227
+ color: textConf.color,
1228
+ bold: textConf.bold,
1229
+ italic: textConf.italic,
1230
+
1231
+ x: centerx + properties.labelsAxesOffsetx,
1232
+ y: centery + (r * ((i+1)/this.scale2.labels.length)) + properties.labelsAxesOffsety,
1233
+
1234
+ text: this.scale2.labels[i],
1235
+ valign: 'center',
1236
+ halign: 'center',
1237
+ bounding: boxed[i] || color,
1238
+ boundingFill: color,
1239
+ boundingStroke: 'rgba(0,0,0,0)',
1240
+ tag: 'scale'
1241
+ });
1242
+ }
1243
+
1244
+ drawzero = true;
1245
+ }
1246
+
1247
+ // The "East" axis labels
1248
+ if (axes.indexOf('e') > -1) {
1249
+
1250
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1251
+ RGraph.text({
1252
+
1253
+ object: this,
1254
+
1255
+ font: textConf.font,
1256
+ size: textConf.size,
1257
+ color: textConf.color,
1258
+ bold: textConf.bold,
1259
+ italic: textConf.italic,
1260
+
1261
+ x: centerx + (r * ((i+1)/this.scale2.labels.length)) + properties.labelsAxesOffsetx,
1262
+ y: centery + properties.labelsAxesOffsety,
1263
+
1264
+ text: this.scale2.labels[i],
1265
+ valign: 'center',
1266
+ halign: 'center',
1267
+ bounding: boxed[i]|| color,
1268
+ boundingFill: color,
1269
+ boundingStroke: 'rgba(0,0,0,0)',
1270
+ tag: 'scale'
1271
+ });
1272
+ }
1273
+
1274
+ drawzero = true;
1275
+ }
1276
+
1277
+ // The "West" axis labels
1278
+ if (axes.indexOf('w') > -1) {
1279
+
1280
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1281
+ RGraph.text({
1282
+
1283
+ object: this,
1284
+
1285
+ font: textConf.font,
1286
+ size: textConf.size,
1287
+ color: textConf.color,
1288
+ bold: textConf.bold,
1289
+ italic: textConf.italic,
1290
+
1291
+ x: centerx - (r * ((i+1)/this.scale2.labels.length)) + properties.labelsAxesOffsetx,
1292
+ y: centery + properties.labelsAxesOffsety,
1293
+
1294
+ text: this.scale2.labels[i],
1295
+ valign: 'center',
1296
+ halign: 'center',
1297
+ bounding: boxed[i]|| color,
1298
+ boundingFill: color,
1299
+ boundingStroke: 'rgba(0,0,0,0)',
1300
+ tag: 'scale'
1301
+ });
1302
+ }
1303
+
1304
+ drawzero = true;
1305
+ }
1306
+
1307
+ if (drawzero) {
1308
+ RGraph.text({
1309
+
1310
+ object: this,
1311
+
1312
+ font: textConf.font,
1313
+ size: textConf.size,
1314
+ color: textConf.color,
1315
+ bold: textConf.bold,
1316
+ italic: textConf.italic,
1317
+
1318
+ x: centerx + properties.labelsAxesOffsetx,
1319
+ y: centery + properties.labelsAxesOffsety,
1320
+
1321
+ text: RGraph.numberFormat({
1322
+ object: this,
1323
+ number: Number(0).toFixed(),
1324
+ unitspre: units_pre,
1325
+ unitspost: units_post
1326
+ }),
1327
+ valign: 'center',
1328
+ halign: 'center',
1329
+ bounding: color ? true : false,
1330
+ boundingFill: color,
1331
+ boundingStroke: 'rgba(0,0,0,0)',
1332
+ tag: 'scale'
1333
+ });
1334
+ }
1335
+ };
1336
+
1337
+
1338
+
1339
+
1340
+
1341
+
1342
+
1343
+
1344
+ //
1345
+ // Draws specific axis labels
1346
+ //
1347
+ this.drawSpecificAxisLabels = function ()
1348
+ {
1349
+ //
1350
+ // Specific axis labels
1351
+ //
1352
+ var labels = properties.labelsAxesSpecific;
1353
+ //var bold = RGraph.arrayPad(properties.labelsAxesBold,labels.length);
1354
+ var boxed = RGraph.arrayPad(properties.labelsAxesBoxed,labels.length);
1355
+ var reversed_labels = RGraph.arrayReverse(labels);
1356
+ //var reversed_bold = RGraph.arrayReverse(bold);
1357
+ var reversed_boxed = RGraph.arrayReverse(boxed);
1358
+ //var font = properties.textFont;
1359
+ var axes = properties.labelsAxes.toLowerCase();
1360
+
1361
+ //this.context.fillStyle = properties.textColor;
1362
+
1363
+ var textConf = RGraph.getTextConf({
1364
+ object: this,
1365
+ prefix: 'labelsAxes'
1366
+ });
1367
+
1368
+
1369
+ for (var i=0; i<labels.length; ++i) {
1370
+
1371
+ if (axes.indexOf('n') > -1) RGraph.text({object: this,font: textConf.font,size: textConf.size,color: textConf.color,bold: textConf.bold,italic: textConf.italic,tag: 'labels.axes.specific', x:this.centerx,y:this.centery - this.radius + ((this.radius / labels.length) * i),text:reversed_labels[i],valign:'center',halign:'center',bounding:reversed_boxed[i],boundingFill:'white'});
1372
+ if (axes.indexOf('s') > -1) RGraph.text({object: this,font: textConf.font,size: textConf.size,color: textConf.color,bold: textConf.bold,italic: textConf.italic,tag: 'labels.axes.specific', x:this.centerx,y:this.centery + ((this.radius / labels.length) * (i+1)),text:labels[i],valign:'center',halign:'center',bounding:boxed[i],boundingFill:'white'});
1373
+
1374
+ if (axes.indexOf('w') > -1) RGraph.text({object: this,font: textConf.font,size: textConf.size,color: textConf.color,bold: textConf.bold,italic: textConf.italic,tag: 'labels.axes.specific', x:this.centerx - this.radius + ((this.radius / labels.length) * i),y:this.centery,text:reversed_labels[i],valign:'center',halign:'center',bounding:reversed_boxed[i],boundingFill:'white'});
1375
+ if (axes.indexOf('e') > -1) RGraph.text({object: this,font: textConf.font,size: textConf.size,color: textConf.color,bold: textConf.bold,italic: textConf.italic,tag: 'labels.axes.specific', x:this.centerx + ((this.radius / labels.length) * (i+1)),y:this.centery,text:labels[i],valign:'center',halign:'center',bounding:boxed[i],boundingFill:'white'});
1376
+ }
1377
+ };
1378
+
1379
+
1380
+
1381
+
1382
+
1383
+
1384
+
1385
+
1386
+ //
1387
+ // This method eases getting the focussed point (if any)
1388
+ //
1389
+ // @param event e The event object
1390
+ //
1391
+ this.getShape = function (e)
1392
+ {
1393
+ for (var i=0; i<this.coords.length; ++i) {
1394
+
1395
+ if (RGraph.tooltipsHotspotIgnore(this, i)) {
1396
+ continue;
1397
+ }
1398
+
1399
+ var x = this.coords[i][0];
1400
+ var y = this.coords[i][1];
1401
+ var tooltips = properties.tooltips;
1402
+ var index = Number(i);
1403
+ var mouseXY = RGraph.getMouseXY(e);
1404
+ var mouseX = mouseXY[0];
1405
+ var mouseY = mouseXY[1];
1406
+
1407
+ if ( mouseX < (x + 5)
1408
+ && mouseX > (x - 5)
1409
+ && mouseY > (y - 5)
1410
+ && mouseY < (y + 5)
1411
+ ) {
1412
+
1413
+ if (RGraph.parseTooltipText) {
1414
+ var tooltip = RGraph.parseTooltipText(properties.tooltips, index);
1415
+ }
1416
+
1417
+ var indexes = RGraph.sequentialIndexToGrouped(index, this.data);
1418
+ var dataset = indexes[0];
1419
+ var idx = indexes[1];
1420
+
1421
+ return {
1422
+ object: this,
1423
+ x: x,
1424
+ y: y,
1425
+ index: idx,
1426
+ dataset: dataset,
1427
+ sequentialIndex: index,
1428
+ label: properties.labels && typeof properties.labels[idx] === 'string' ? properties.labels[idx] : null,
1429
+ tooltip: typeof tooltip === 'string' ? tooltip : null
1430
+ }
1431
+ }
1432
+ }
1433
+ };
1434
+
1435
+
1436
+
1437
+
1438
+
1439
+
1440
+
1441
+
1442
+ //
1443
+ // Each object type has its own Highlight() function which highlights the appropriate shape
1444
+ //
1445
+ // @param object shape The shape to highlight
1446
+ //
1447
+ this.highlight = function (shape)
1448
+ {
1449
+ if (typeof properties.highlightStyle === 'function') {
1450
+ (properties.highlightStyle)(shape);
1451
+
1452
+
1453
+
1454
+
1455
+
1456
+ // Inverted highlighting
1457
+ } else if (properties.highlightStyle === 'invert') {
1458
+
1459
+ var radius = 25;
1460
+
1461
+ this.path(
1462
+ 'b a % % % % % true',
1463
+ shape.x,shape.y,radius,4.72, -1.57
1464
+ );
1465
+
1466
+ for (var a=0; a<=360; a+=(360 / this.data[0].length)) {
1467
+ this.path('a % % % % % false');
1468
+ this.context.arc(
1469
+ this.centerx,
1470
+ this.centery,
1471
+ this.radius,
1472
+ RGraph.toRadians(a) - RGraph.HALFPI,
1473
+ RGraph.toRadians(a) + 0.001 - RGraph.HALFPI,
1474
+ false
1475
+ );
1476
+ }
1477
+
1478
+ // Go back to the top of the chart and then stroke/fill it
1479
+ this.path(
1480
+ 'a % % % % % false c f %',
1481
+ this.centerx,this.centery,this.radius,0 - RGraph.HALFPI,0.001 - RGraph.HALFPI,
1482
+ properties.highlightFill
1483
+ );
1484
+
1485
+ // Draw the stroke around the circular cutout
1486
+ this.path(
1487
+ 'b a % % % % % false s %',
1488
+ shape.x,shape.y,radius,0,6.29,
1489
+ properties.highlightStroke
1490
+ );
1491
+
1492
+
1493
+
1494
+
1495
+
1496
+
1497
+
1498
+ } else {
1499
+ RGraph.Highlight.point(this, shape);
1500
+ }
1501
+ };
1502
+
1503
+
1504
+
1505
+
1506
+
1507
+
1508
+
1509
+
1510
+ //
1511
+ // The getObjectByXY() worker method. Don't call this call:
1512
+ //
1513
+ // RGraph.ObjectRegistry.getObjectByXY(e)
1514
+ //
1515
+ // @param object e The event object
1516
+ //
1517
+ this.getObjectByXY = function (e)
1518
+ {
1519
+ var mouseXY = RGraph.getMouseXY(e);
1520
+ var extra = 5;
1521
+
1522
+ if (
1523
+ mouseXY[0] > (this.centerx - this.radius - extra)
1524
+ && mouseXY[0] < (this.centerx + this.radius + extra)
1525
+ && mouseXY[1] > (this.centery - this.radius - extra)
1526
+ && mouseXY[1] < (this.centery + this.radius + extra)
1527
+ ) {
1528
+
1529
+ return this;
1530
+ }
1531
+ };
1532
+
1533
+
1534
+
1535
+
1536
+
1537
+
1538
+
1539
+
1540
+ //
1541
+ // This draws highlights on the points
1542
+ //
1543
+ this.drawHighlights = function ()
1544
+ {
1545
+ if (properties.highlights) {
1546
+
1547
+ var sequentialIdx = 0;
1548
+ var dataset = 0;
1549
+ var index = 0;
1550
+ var radius = properties.highlightsRadius;
1551
+
1552
+
1553
+
1554
+ for (var dataset=0; dataset <this.data.length; ++dataset) {
1555
+ for (var index=0; index<this.data[dataset].length; ++index) {
1556
+ this.context.beginPath();
1557
+ this.context.strokeStyle = properties.highlightsStroke;
1558
+ this.context.fillStyle = properties.highlightsFill ? properties.highlightsFill : ((typeof properties.colorsStroke == 'object' && properties.colorsStroke[dataset]) ? properties.colorsStroke[dataset] : properties.colorsStroke);
1559
+ this.context.arc(this.coords[sequentialIdx][0], this.coords[sequentialIdx][1], radius, 0, RGraph.TWOPI, false);
1560
+ this.context.stroke();
1561
+ this.context.fill();
1562
+ ++sequentialIdx;
1563
+ }
1564
+ }
1565
+
1566
+ }
1567
+ };
1568
+
1569
+
1570
+
1571
+
1572
+
1573
+
1574
+
1575
+
1576
+ //
1577
+ // This function returns the radius (ie the distance from the center) for a particular
1578
+ // value. Note that if you want the angle for a point you can use getAngle(index)
1579
+ //
1580
+ // @param number value The value you want the radius for
1581
+ //
1582
+ this.getRadius = function (value)
1583
+ {
1584
+ if (value < 0 || value > this.max) {
1585
+ return null;
1586
+ }
1587
+
1588
+ // Radar doesn't support minimum value
1589
+ var radius = (value / this.max) * this.radius;
1590
+
1591
+ return radius;
1592
+ };
1593
+
1594
+
1595
+
1596
+
1597
+
1598
+
1599
+
1600
+
1601
+ //
1602
+ // This function returns the angle (in radians) for a particular index.
1603
+ //
1604
+ // @param number numitems The total number of items
1605
+ // @param number index The zero index number of the item to get the angle for
1606
+ //
1607
+ this.getAngle = function (numitems, index)
1608
+ {
1609
+ var angle = (RGraph.TWOPI / numitems) * index;
1610
+ angle -= RGraph.HALFPI;
1611
+
1612
+ return angle;
1613
+ };
1614
+
1615
+
1616
+
1617
+
1618
+
1619
+
1620
+
1621
+
1622
+ //
1623
+ // This allows for easy specification of gradients
1624
+ //
1625
+ this.parseColors = function ()
1626
+ {
1627
+ // Save the original colors so that they can be restored when the canvas is reset
1628
+ if (this.original_colors.length === 0) {
1629
+ this.original_colors.colors = RGraph.arrayClone(properties.colors);
1630
+ this.original_colors.keyColors = RGraph.arrayClone(properties.keyColors);
1631
+ this.original_colors.titleColor = RGraph.arrayClone(properties.titleColor);
1632
+ this.original_colors.textColor = RGraph.arrayClone(properties.textColor);
1633
+ this.original_colors.labelsColor = RGraph.arrayClone(properties.labelsColor);
1634
+ this.original_colors.labelsAxesColor = RGraph.arrayClone(properties.labelsAxesColor);
1635
+ this.original_colors.highlightStroke = RGraph.arrayClone(properties.highlightStroke);
1636
+ this.original_colors.highlightFill = RGraph.arrayClone(properties.highlightFill);
1637
+ this.original_colors.circleFill = RGraph.arrayClone(properties.circleFill);
1638
+ this.original_colors.circleStroke = RGraph.arrayClone(properties.circleStroke);
1639
+ this.original_colors.colorsStroke = RGraph.arrayClone(properties.colorsStroke);
1640
+ }
1641
+
1642
+ for (var i=0; i<properties.colors.length; ++i) {
1643
+ properties.colors[i] = this.parseSingleColorForGradient(properties.colors[i]);
1644
+ }
1645
+
1646
+ var keyColors = properties.keyColors;
1647
+
1648
+ if (typeof keyColors != 'null' && keyColors && keyColors.length) {
1649
+ for (var i=0; i<properties.keyColors.length; ++i) {
1650
+ properties.keyColors[i] = this.parseSingleColorForGradient(properties.keyColors[i]);
1651
+ }
1652
+ }
1653
+
1654
+ properties.titleColor = this.parseSingleColorForGradient(properties.titleColor);
1655
+ properties.textColor = this.parseSingleColorForGradient(properties.textColor);
1656
+ properties.labelsColor = this.parseSingleColorForGradient(properties.labelsColor);
1657
+ properties.labelsAxesColor= this.parseSingleColorForGradient(properties.labelsAxesColor);
1658
+ properties.highlightStroke = this.parseSingleColorForGradient(properties.highlightStroke);
1659
+ properties.highlightFill = this.parseSingleColorForGradient(properties.highlightFill);
1660
+ properties.circleFill = this.parseSingleColorForGradient(properties.circleFill);
1661
+ properties.circleStroke = this.parseSingleColorForGradient(properties.circleStroke);
1662
+
1663
+ // Strokestyle can be an array
1664
+ if (typeof properties.colorsStroke === 'object') {
1665
+ for (var i=0; i<properties.colorsStroke.length; ++i) {
1666
+ properties.colorsStroke[i] = this.parseSingleColorForGradient(properties.colorsStroke[i]);
1667
+ }
1668
+ } else {
1669
+ properties.colorsStroke = this.parseSingleColorForGradient(properties.colorsStroke);
1670
+ }
1671
+ };
1672
+
1673
+
1674
+
1675
+
1676
+
1677
+
1678
+
1679
+
1680
+ //
1681
+ // Use this function to reset the object to the post-constructor state. Eg reset colors if
1682
+ // need be etc
1683
+ //
1684
+ this.reset = function ()
1685
+ {
1686
+ };
1687
+
1688
+
1689
+
1690
+
1691
+
1692
+
1693
+
1694
+
1695
+ //
1696
+ // This parses a single color value
1697
+ //
1698
+ this.parseSingleColorForGradient = function (color)
1699
+ {
1700
+ if (!color || typeof color != 'string') {
1701
+ return color;
1702
+ }
1703
+
1704
+ if (color.match(/^gradient\((.*)\)$/i)) {
1705
+
1706
+ // Allow for JSON gradients
1707
+ if (color.match(/^gradient\(({.*})\)$/i)) {
1708
+ return RGraph.parseJSONGradient({object: this, def: RegExp.$1});
1709
+ }
1710
+
1711
+ var parts = RegExp.$1.split(':');
1712
+
1713
+ // Create the gradient
1714
+ var grad = this.context.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
1715
+
1716
+ var diff = 1 / (parts.length - 1);
1717
+
1718
+ grad.addColorStop(0, RGraph.trim(parts[0]));
1719
+
1720
+ for (var j=1; j<parts.length; ++j) {
1721
+ grad.addColorStop(j * diff, RGraph.trim(parts[j]));
1722
+ }
1723
+ }
1724
+
1725
+ return grad ? grad : color;
1726
+ };
1727
+
1728
+
1729
+
1730
+
1731
+
1732
+
1733
+
1734
+
1735
+ this.addFillListeners = function (e)
1736
+ {
1737
+ var obj = this;
1738
+
1739
+ var func = function (e)
1740
+ {
1741
+ //var canvas = e.target;
1742
+ //var context = canvas.getContext('2d');
1743
+ var coords = obj.coords;
1744
+ var coords2 = obj.coords2;
1745
+ var mouseXY = RGraph.getMouseXY(e);
1746
+ var dataset = 0;
1747
+
1748
+ if (e.type == 'mousemove' && properties.fillMousemoveRedraw) {
1749
+ RGraph.redrawCanvas(obj.canvas);
1750
+ }
1751
+
1752
+ for (var dataset=(obj.coords2.length-1); dataset>=0; --dataset) {
1753
+
1754
+ // Draw the path again so that it can be checked
1755
+ obj.context.beginPath();
1756
+ obj.context.moveTo(obj.coords2[dataset][0][0], obj.coords2[dataset][0][1]);
1757
+ for (var j=0; j<obj.coords2[dataset].length; ++j) {
1758
+ obj.context.lineTo(obj.coords2[dataset][j][0], obj.coords2[dataset][j][1]);
1759
+ }
1760
+
1761
+ // Draw a line back to the starting point
1762
+ obj.context.lineTo(obj.coords2[dataset][0][0], obj.coords2[dataset][0][1]);
1763
+
1764
+ // Go thru the previous datasets coords in reverse order
1765
+ if (properties.accumulative && dataset > 0) {
1766
+ obj.context.lineTo(obj.coords2[dataset - 1][0][0], obj.coords2[dataset - 1][0][1]);
1767
+ for (var j=(obj.coords2[dataset - 1].length - 1); j>=0; --j) {
1768
+ obj.context.lineTo(obj.coords2[dataset - 1][j][0], obj.coords2[dataset - 1][j][1]);
1769
+ }
1770
+ }
1771
+
1772
+ obj.context.closePath();
1773
+
1774
+ if (obj.context.isPointInPath(mouseXY[0], mouseXY[1])) {
1775
+ var inPath = true;
1776
+ break;
1777
+ }
1778
+ }
1779
+
1780
+ // Call the events
1781
+ if (inPath) {
1782
+
1783
+ var fillTooltips = properties.fillTooltips;
1784
+
1785
+ //
1786
+ // Click event
1787
+ //
1788
+ if (e.type == 'click') {
1789
+ if (properties.fillClick) {
1790
+ properties.fillClick(e, dataset);
1791
+ }
1792
+
1793
+ if (properties.fillTooltips && properties.fillTooltips[dataset]) {
1794
+ obj.datasetTooltip(e, dataset);
1795
+ }
1796
+ }
1797
+
1798
+
1799
+
1800
+ //
1801
+ // Mousemove event
1802
+ //
1803
+ if (e.type == 'mousemove') {
1804
+
1805
+ if (properties.fillMousemove) {
1806
+ properties.fillMousemove(e, dataset);
1807
+ }
1808
+
1809
+ if (!RGraph.isNull(fillTooltips)) {
1810
+ e.target.style.cursor = 'pointer';
1811
+ }
1812
+
1813
+ if (properties.fillTooltips && properties.fillTooltips[dataset]) {
1814
+ e.target.style.cursor = 'pointer';
1815
+ }
1816
+ }
1817
+
1818
+ e.stopPropagation();
1819
+
1820
+ } else if (e.type == 'mousemove') {
1821
+ obj.canvas.style.cursor = 'default';
1822
+ }
1823
+ };
1824
+
1825
+ //
1826
+ // Add the click listener
1827
+ //
1828
+ if (properties.fillClick || !RGraph.isNull(properties.fillTooltips)) {
1829
+ this.canvas.addEventListener('click', func, false);
1830
+ }
1831
+
1832
+ //
1833
+ // Add the mousemove listener
1834
+ //
1835
+ if (properties.fillMousemove || !RGraph.isNull(properties.fillTooltips)) {
1836
+ this.canvas.addEventListener('mousemove', func, false);
1837
+ }
1838
+ };
1839
+
1840
+
1841
+
1842
+
1843
+
1844
+
1845
+
1846
+
1847
+ //
1848
+ // This highlights a specific dataset on the chart
1849
+ //
1850
+ // @param number dataset The index of the dataset (which starts at zero)
1851
+ //
1852
+ this.highlightDataset = function (dataset)
1853
+ {
1854
+ this.context.beginPath();
1855
+ for (var j=0; j<this.coords2[dataset].length; ++j) {
1856
+ if (j == 0) {
1857
+ this.context.moveTo(this.coords2[dataset][0][0], this.coords2[dataset][0][1]);
1858
+ } else {
1859
+ this.context.lineTo(this.coords2[dataset][j][0], this.coords2[dataset][j][1]);
1860
+ }
1861
+ }
1862
+
1863
+ this.context.lineTo(this.coords2[dataset][0][0], this.coords2[dataset][0][1]);
1864
+
1865
+ if (properties.accumulative && dataset > 0) {
1866
+ this.context.lineTo(this.coords2[dataset - 1][0][0], this.coords2[dataset - 1][0][1]);
1867
+ for (var j=(this.coords2[dataset - 1].length - 1); j>=0; --j) {
1868
+ this.context.lineTo(this.coords2[dataset - 1][j][0], this.coords2[dataset - 1][j][1]);
1869
+ }
1870
+ }
1871
+
1872
+ this.context.strokeStyle = properties.fillHighlightStroke;
1873
+ this.context.fillStyle = properties.fillHighlightFill;
1874
+
1875
+ this.context.stroke();
1876
+ this.context.fill();
1877
+ };
1878
+
1879
+
1880
+
1881
+
1882
+
1883
+
1884
+
1885
+
1886
+ //
1887
+ // Shows a tooltip for a dataset (a "fill" tooltip), You can pecify these
1888
+ // with the fillTooltips option
1889
+ //
1890
+ this.datasetTooltip = function (e, dataset)
1891
+ {
1892
+ // Highlight the dataset
1893
+ this.highlightDataset(dataset);
1894
+
1895
+ // Use the First datapoints coords for the Y position of the tooltip NOTE The X position is changed in the
1896
+ // obj.positionTooltip() method so set the index to be the first one
1897
+ var text = properties.fillTooltips[dataset];
1898
+ var x = this.coords2[dataset][0][0] + RGraph.getCanvasXY(this.canvas)[0];
1899
+ var y = this.coords2[dataset][0][1] + RGraph.getCanvasXY(this.canvas)[1];
1900
+
1901
+
1902
+ // Show a tooltip
1903
+ RGraph.tooltip({
1904
+ object: this,
1905
+ text: text,
1906
+ x: x,
1907
+ y: y,
1908
+ index: 0,
1909
+ event: e
1910
+ });
1911
+ };
1912
+
1913
+
1914
+
1915
+
1916
+
1917
+
1918
+
1919
+
1920
+ //
1921
+ // This function handles highlighting an entire data-series for the interactive
1922
+ // key
1923
+ //
1924
+ // @param int index The index of the data series to be highlighted
1925
+ //
1926
+ this.interactiveKeyHighlight = function (index)
1927
+ {
1928
+ var coords = this.coords2[index];
1929
+
1930
+ if (coords) {
1931
+
1932
+ var pre_linewidth = this.context.lineWidth;
1933
+ var pre_linecap = this.context.lineCap;
1934
+
1935
+
1936
+
1937
+
1938
+ // ------------------------------------------ //
1939
+
1940
+ this.context.lineWidth = properties.linewidth + 10;
1941
+ this.context.lineCap = 'round';
1942
+ this.context.strokeStyle = properties.keyInteractiveHighlightChartStroke;
1943
+
1944
+
1945
+ this.context.beginPath();
1946
+ for (var i=0,len=coords.length; i<len; i+=1) {
1947
+ if (i == 0) {
1948
+ this.context.moveTo(coords[i][0], coords[i][1]);
1949
+ } else {
1950
+ this.context.lineTo(coords[i][0], coords[i][1]);
1951
+ }
1952
+ }
1953
+ this.context.closePath();
1954
+ this.context.stroke();
1955
+
1956
+ // ------------------------------------------ //
1957
+
1958
+
1959
+
1960
+
1961
+ // Reset the lineCap and lineWidth
1962
+ this.context.lineWidth = pre_linewidth;
1963
+ this.context.lineCap = pre_linecap;
1964
+ }
1965
+ };
1966
+
1967
+
1968
+
1969
+
1970
+
1971
+
1972
+
1973
+
1974
+ //
1975
+ // Using a function to add events makes it easier to facilitate method chaining
1976
+ //
1977
+ // @param string type The type of even to add
1978
+ // @param function func
1979
+ //
1980
+ this.on = function (type, func)
1981
+ {
1982
+ if (type.substr(0,2) !== 'on') {
1983
+ type = 'on' + type;
1984
+ }
1985
+
1986
+ if (typeof this[type] !== 'function') {
1987
+ this[type] = func;
1988
+ } else {
1989
+ RGraph.addCustomEventListener(this, type, func);
1990
+ }
1991
+
1992
+ return this;
1993
+ };
1994
+
1995
+
1996
+
1997
+
1998
+
1999
+
2000
+
2001
+
2002
+ //
2003
+ // This function runs once only
2004
+ // (put at the end of the file (before any effects))
2005
+ //
2006
+ this.firstDrawFunc = function ()
2007
+ {
2008
+ };
2009
+
2010
+
2011
+
2012
+
2013
+
2014
+
2015
+
2016
+
2017
+ //
2018
+ // Radar chart grow
2019
+ //
2020
+ // This effect gradually increases the magnitude of the points on the radar chart
2021
+ //
2022
+ // @param object Options for the effect
2023
+ // @param function An optional callback that is run when the effect is finished
2024
+ //
2025
+ this.grow = function ()
2026
+ {
2027
+ // Cancel any stop request if one is pending
2028
+ this.cancelStopAnimation();
2029
+
2030
+ var obj = this,
2031
+ callback = arguments[1] ? arguments[1] : function () {},
2032
+ opt = arguments[0] ? arguments[0] : {},
2033
+ frames = opt.frames ? opt.frames : 30,
2034
+ frame = 0,
2035
+ data = RGraph.arrayClone(this.unmodified_data);
2036
+
2037
+ function iterator ()
2038
+ {
2039
+ if (obj.stopAnimationRequested) {
2040
+
2041
+ // Reset the flag
2042
+ obj.stopAnimationRequested = false;
2043
+
2044
+ return;
2045
+ }
2046
+
2047
+ for (var i=0,len=data.length; i<len; ++i) {
2048
+
2049
+ for (var j=0,len2=data[i].length; j<len2; ++j) {
2050
+ obj.original_data[i][j] = (frame / frames) * data[i][j];
2051
+ }
2052
+ }
2053
+
2054
+ RGraph.clear(obj.canvas);
2055
+ RGraph.redrawCanvas(obj.canvas);
2056
+
2057
+ if (frame < frames) {
2058
+ frame++;
2059
+ RGraph.Effects.updateCanvas(iterator);
2060
+ } else {
2061
+ callback(obj);
2062
+ }
2063
+ }
2064
+
2065
+ iterator();
2066
+
2067
+ return this;
2068
+ };
2069
+
2070
+
2071
+
2072
+
2073
+
2074
+
2075
+
2076
+
2077
+ //
2078
+ // Trace (Radar chart)
2079
+ //
2080
+ // This is a Trace effect for the Radar chart
2081
+ //
2082
+ // @param object Options for the effect. Currently only "frames" is available.
2083
+ // @param function A function that is called when the ffect is complete
2084
+ //
2085
+ this.trace = function ()
2086
+ {
2087
+ // Cancel any stop request if one is pending
2088
+ this.cancelStopAnimation();
2089
+
2090
+ var obj = this,
2091
+ opt = arguments[0] || {},
2092
+ frames = opt.frames || 60,
2093
+ frame = 0,
2094
+ callback = arguments[1] || function () {};
2095
+
2096
+ this.original_data = RGraph.arrayClone(this.unmodified_data);
2097
+ this.set('animationTraceClip', 0);
2098
+
2099
+
2100
+ var iterator = function ()
2101
+ {
2102
+ if (obj.stopAnimationRequested) {
2103
+
2104
+ // Reset the flag
2105
+ obj.stopAnimationRequested = false;
2106
+
2107
+ return;
2108
+ }
2109
+
2110
+ if (frame < frames) {
2111
+
2112
+ obj.set('animationTraceClip', frame / frames);
2113
+
2114
+ frame++;
2115
+ RGraph.redrawCanvas(obj.canvas);
2116
+ RGraph.Effects.updateCanvas(iterator);
2117
+
2118
+ } else {
2119
+
2120
+ obj.set('animationTraceClip', 1);
2121
+ RGraph.redrawCanvas(obj.canvas);
2122
+ callback(obj);
2123
+ }
2124
+ };
2125
+
2126
+ iterator();
2127
+
2128
+ return this;
2129
+ };
2130
+
2131
+
2132
+
2133
+
2134
+
2135
+
2136
+
2137
+
2138
+ //
2139
+ // Couple of functions that allow you to control the
2140
+ // animation effect
2141
+ //
2142
+ this.stopAnimation = function ()
2143
+ {
2144
+ // Reset the clip
2145
+ this.set('animationTraceClip', 1);
2146
+
2147
+ this.stopAnimationRequested = true;
2148
+ };
2149
+
2150
+ this.cancelStopAnimation = function ()
2151
+ {
2152
+ this.stopAnimationRequested = false;
2153
+ };
2154
+
2155
+
2156
+
2157
+
2158
+
2159
+
2160
+
2161
+
2162
+ //
2163
+ // A worker function that handles Bar chart specific tooltip substitutions
2164
+ //
2165
+ this.tooltipSubstitutions = function (opt)
2166
+ {
2167
+ var indexes = RGraph.sequentialIndexToGrouped(opt.index, this.data);
2168
+
2169
+ // Create the values array which contains each datasets value
2170
+ for (var i=0,values=[]; i<this.data.length; ++i) {
2171
+ values.push(this.data[i][indexes[1]]);
2172
+ }
2173
+
2174
+ return {
2175
+ index: indexes[1],
2176
+ dataset: indexes[0],
2177
+ sequentialIndex: opt.index,
2178
+ value: this.data_arr[opt.index],
2179
+ values: values
2180
+ };
2181
+ };
2182
+
2183
+
2184
+
2185
+
2186
+
2187
+
2188
+
2189
+
2190
+ //
2191
+ // A worker function that returns the correct color/label/value
2192
+ //
2193
+ // @param object specific The indexes that are applicable
2194
+ // @param number index The appropriate index
2195
+ //
2196
+ this.tooltipsFormattedCustom = function (specific, index)
2197
+ {
2198
+ var color = ( !RGraph.isNull((properties.tooltipsFormattedKeyColors))
2199
+ && typeof properties.tooltipsFormattedKeyColors === 'object'
2200
+ && properties.tooltipsFormattedKeyColors[i])
2201
+ ? properties.tooltipsFormattedKeyColors[i]
2202
+ : '';
2203
+
2204
+ var label = ( !RGraph.isNull((properties.tooltipsFormattedKeyLabels))
2205
+ && typeof properties.tooltipsFormattedKeyLabels === 'object'
2206
+ && properties.tooltipsFormattedKeyLabels[index])
2207
+ ? properties.tooltipsFormattedKeyLabels[index]
2208
+ : '';
2209
+ return {
2210
+ label: label,
2211
+ color: color
2212
+ };
2213
+ };
2214
+
2215
+
2216
+
2217
+
2218
+
2219
+
2220
+
2221
+
2222
+ //
2223
+ // This allows for static tooltip positioning
2224
+ //
2225
+ this.positionTooltipStatic = function (args)
2226
+ {
2227
+ var obj = args.object,
2228
+ e = args.event,
2229
+ tooltip = args.tooltip,
2230
+ index = args.index,
2231
+ canvasXY = RGraph.getCanvasXY(obj.canvas)
2232
+ coords = this.coords[index];
2233
+
2234
+ // Position the tooltip in the X direction
2235
+ args.tooltip.style.left = (
2236
+ canvasXY[0] // The X coordinate of the canvas
2237
+ + coords[0] // The X coordinate of the point on the chart
2238
+ - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width
2239
+ + obj.properties.tooltipsOffsetx // Add any user defined offset
2240
+ ) + 'px';
2241
+
2242
+ args.tooltip.style.top = (
2243
+ canvasXY[1] // The Y coordinate of the canvas
2244
+ + coords[1] // The Y coordinate of the bar on the chart
2245
+ - tooltip.offsetHeight // The height of the tooltip
2246
+ + obj.properties.tooltipsOffsety // Add any user defined offset
2247
+ - 15
2248
+ - (properties.highlightsRadius - 4)
2249
+ ) + 'px';
2250
+ };
2251
+
2252
+
2253
+
2254
+
2255
+
2256
+
2257
+
2258
+
2259
+ //
2260
+ // This returns the relevant value for the formatted key
2261
+ // macro %{value}. THIS VALUE SHOULD NOT BE FORMATTED.
2262
+ //
2263
+ // @param number index The index in the dataset to get
2264
+ // the value for
2265
+ //
2266
+ this.getKeyValue = function (index)
2267
+ {
2268
+ if ( RGraph.isArray(this.properties.keyFormattedValueSpecific)
2269
+ && RGraph.isNumber(this.properties.keyFormattedValueSpecific[index])) {
2270
+
2271
+ return this.properties.keyFormattedValueSpecific[index];
2272
+
2273
+ } else {
2274
+ return RGraph.arraySum(this.original_data[index]);
2275
+ }
2276
+ };
2277
+
2278
+
2279
+
2280
+
2281
+
2282
+
2283
+
2284
+
2285
+ //
2286
+ // Returns how many data-points there should be when a string
2287
+ // based key property has been specified. For example, this:
2288
+ //
2289
+ // key: '%{property:_labels[%{index}]} %{value_formatted}'
2290
+ //
2291
+ // ...depending on how many bits of data ther is might get
2292
+ // turned into this:
2293
+ //
2294
+ // key: [
2295
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2296
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2297
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2298
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2299
+ // '%{property:_labels[%{index}]} %{value_formatted}',
2300
+ // ]
2301
+ //
2302
+ // ... ie in that case there would be 4 data-points so the
2303
+ // template is repeated 4 times.
2304
+ //
2305
+ this.getKeyNumDatapoints = function ()
2306
+ {
2307
+ return this.data.length;
2308
+ };
2309
+
2310
+
2311
+
2312
+
2313
+
2314
+
2315
+
2316
+
2317
+ //
2318
+ // Always register the object
2319
+ //
2320
+ RGraph.register(this);
2321
+
2322
+
2323
+
2324
+
2325
+
2326
+
2327
+
2328
+
2329
+ //
2330
+ // This is the 'end' of the constructor so if the first argument
2331
+ // contains configuration data - handle that.
2332
+ //
2333
+ RGraph.parseObjectStyleConfig(this, conf.options);
2334
+ };