rgraph-rails 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/license.txt +4 -16
  5. data/vendor/assets/javascripts/RGraph.bar.js +3734 -241
  6. data/vendor/assets/javascripts/RGraph.bipolar.js +2005 -115
  7. data/vendor/assets/javascripts/RGraph.common.annotate.js +395 -35
  8. data/vendor/assets/javascripts/RGraph.common.context.js +595 -30
  9. data/vendor/assets/javascripts/RGraph.common.core.js +5282 -405
  10. data/vendor/assets/javascripts/RGraph.common.csv.js +276 -19
  11. data/vendor/assets/javascripts/RGraph.common.deprecated.js +450 -35
  12. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1395 -86
  13. data/vendor/assets/javascripts/RGraph.common.effects.js +1545 -90
  14. data/vendor/assets/javascripts/RGraph.common.key.js +753 -54
  15. data/vendor/assets/javascripts/RGraph.common.resizing.js +563 -37
  16. data/vendor/assets/javascripts/RGraph.common.sheets.js +352 -29
  17. data/vendor/assets/javascripts/RGraph.common.tooltips.js +450 -32
  18. data/vendor/assets/javascripts/RGraph.common.zoom.js +219 -14
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +570 -35
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +544 -35
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +755 -52
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +645 -41
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +633 -37
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +514 -36
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +559 -39
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +548 -35
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +664 -36
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +812 -50
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +856 -51
  30. data/vendor/assets/javascripts/RGraph.fuel.js +964 -58
  31. data/vendor/assets/javascripts/RGraph.funnel.js +984 -55
  32. data/vendor/assets/javascripts/RGraph.gantt.js +1354 -77
  33. data/vendor/assets/javascripts/RGraph.gauge.js +1421 -87
  34. data/vendor/assets/javascripts/RGraph.hbar.js +2562 -146
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +1401 -80
  36. data/vendor/assets/javascripts/RGraph.line.js +4226 -244
  37. data/vendor/assets/javascripts/RGraph.meter.js +1280 -74
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +301 -19
  39. data/vendor/assets/javascripts/RGraph.odo.js +1264 -71
  40. data/vendor/assets/javascripts/RGraph.pie.js +2288 -137
  41. data/vendor/assets/javascripts/RGraph.radar.js +1847 -110
  42. data/vendor/assets/javascripts/RGraph.rose.js +1977 -108
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +1432 -80
  44. data/vendor/assets/javascripts/RGraph.scatter.js +3036 -168
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1120 -60
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +1067 -0
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +247 -0
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +3363 -0
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +277 -0
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1304 -0
  51. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +353 -0
  52. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +233 -0
  53. data/vendor/assets/javascripts/RGraph.svg.hbar.js +1141 -0
  54. data/vendor/assets/javascripts/RGraph.svg.line.js +1486 -0
  55. data/vendor/assets/javascripts/RGraph.svg.pie.js +781 -0
  56. data/vendor/assets/javascripts/RGraph.svg.radar.js +1326 -0
  57. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +817 -0
  58. data/vendor/assets/javascripts/RGraph.thermometer.js +1135 -62
  59. data/vendor/assets/javascripts/RGraph.vprogress.js +1470 -83
  60. data/vendor/assets/javascripts/RGraph.waterfall.js +1347 -80
  61. metadata +15 -3
@@ -1,111 +1,1848 @@
1
+ // version: 2017-01-02
2
+ /**
3
+ * o--------------------------------------------------------------------------------o
4
+ * | This file is part of the RGraph package - you can learn more at: |
5
+ * | |
6
+ * | http://www.rgraph.net |
7
+ * | |
8
+ * | RGraph is licensed under the Open Source MIT license. That means that it's |
9
+ * | totally free to use! |
10
+ * o--------------------------------------------------------------------------------o
11
+ */
1
12
 
2
- RGraph=window.RGraph||{isRGraph:true};RGraph.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;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.strokestyle':'#aaa','chart.gutter.left':25,'chart.gutter.right':25,'chart.gutter.top':25,'chart.gutter.bottom':25,'chart.linewidth':1,'chart.colors':['rgba(255,255,0,0.25)','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.color':null,'chart.labels.offset':10,'chart.labels.axes':'','chart.labels.background.fill':'white','chart.labels.boxed':false,'chart.labels.axes.bold':[],'chart.labels.axes.boxed':null,'chart.labels.axes.boxed.zero':true,'chart.labels.axes.boxed.background':'rgba(255,255,255,0.9)','chart.labels.specific':[],'chart.labels.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.size.scale':null,'chart.text.font':'Segoe UI, Arial, Verdana, sans-serif','chart.text.color':'black','chart.text.accessible':true,'chart.text.accessible.overflow':'visible','chart.text.accessible.pointerevents':true,'chart.title':'','chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.color':'black','chart.title.bold':true,'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.text.color':'black','chart.contextmenu':null,'chart.annotatable':false,'chart.annotate.color':'black','chart.zoom.factor':1.5,'chart.zoom.fade.in':true,'chart.zoom.fade.out':true,'chart.zoom.hdir':'right','chart.zoom.vdir':'down','chart.zoom.frames':25,'chart.zoom.delay':16.666,'chart.zoom.shadow':true,'chart.zoom.background':true,'chart.zoom.action':'zoom','chart.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.ymax':null,'chart.accumulative':false,'chart.radius':null,'chart.events.click':null,'chart.events.mousemove':null,'chart.scale.decimals':0,'chart.scale.point':'.','chart.scale.thousand':',','chart.units.pre':'','chart.units.post':'','chart.tooltips':null,'chart.tooltips.event':'onmousemove','chart.centerx':null,'chart.centery':null,'chart.radius':null,'chart.numxticks':5,'chart.numyticks':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
- if(name=='chart.text.diameter'){name='chart.text.size';}
17
- if(name=='chart.color'){this.properties['chart.colors']=[value];}
18
- prop[name]=value;return this;};this.get=this.Get=function(name)
19
- {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
20
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
21
- if(name=='chart.text.diameter'){name='chart.text.size';}
22
- return prop[name];};this.draw=this.Draw=function()
23
- {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');}}}
24
- if(RG.isNull(prop['chart.labels.axes.boxed'])){prop['chart.labels.axes.boxed']=[];for(var i=0;i<(prop['chart.labels.specific'].length||prop['chart.labels.count']||5);++i){prop['chart.labels.axes.boxed'][i]=false;}}
25
- this.gutterLeft=prop['chart.gutter.left'];this.gutterRight=prop['chart.gutter.right'];this.gutterTop=prop['chart.gutter.top'];this.gutterBottom=prop['chart.gutter.bottom'];this.centerx=((ca.width-this.gutterLeft-this.gutterRight)/2)+this.gutterLeft;this.centery=((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop;this.radius=Math.min(ca.width-this.gutterLeft-this.gutterRight,ca.height-this.gutterTop-this.gutterBottom)/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;}
26
- if(!prop['chart.ymax']){if(prop['chart.accumulative']){var accumulation=[];var len=this.original_data[0].length
27
- 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!');}
28
- 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]);}}}
29
- this.scale2=RG.getScale2(this,{'max':typeof(prop['chart.ymax'])=='number'?prop['chart.ymax']:this.max,'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'],'units.pre':prop['chart.units.pre'],'units.post':prop['chart.units.post'],'ylabels.count':prop['chart.labels.count']});this.max=this.scale2.max;}else{var ymax=prop['chart.ymax'];this.scale2=RG.getScale2(this,{'max':ymax,'min':0,'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'],'units.pre':prop['chart.units.pre'],'units.post':prop['chart.units.post'],'ylabels.count':prop['chart.labels.count']});this.max=this.scale2.max;}
30
- 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.gutterTop,null,prop['chart.title.diameter']?prop['chart.title.diameter']:null)}
31
- if(prop['chart.key']){RG.DrawKey(this,prop['chart.key'],prop['chart.colors']);}
32
- if(prop['chart.contextmenu']){RG.ShowContext(this);}
33
- if(prop['chart.resizable']){RG.AllowResizing(this);}
34
- RG.InstallEventListeners(this);if((prop['chart.fill.click']||prop['chart.fill.mousemove']||!RG.is_null(prop['chart.fill.tooltips']))&&!this.__fill_click_listeners_installed__){this.AddFillListeners();this.__fill_click_listeners_installed__=true;}
35
- if(this.firstDraw){RG.fireCustomEvent(this,'onfirstdraw');this.firstDraw=false;this.firstDrawFunc();}
36
- RGraph.FireCustomEvent(this,'ondraw');return this;};this.exec=function(func)
37
- {func(this);return this;};this.drawBackground=this.DrawBackground=function()
38
- {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.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);}
39
- 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
40
- 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();}
41
- co.strokeStyle=color;var numrings=typeof(prop['chart.background.circles.count'])=='number'?prop['chart.background.circles.count']:prop['chart.labels.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.degrees2Radians(a)-RG.HALFPI,RG.degrees2Radians(a)+0.001-RG.HALFPI,false);}
42
- co.closePath();co.stroke();}}};this.drawAxes=this.DrawAxes=function()
43
- {co.strokeStyle=prop['chart.axes.color'];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.numyticks'])){co.moveTo(this.centerx-3,Math.round(y));co.lineTo(this.centerx+3,Math.round(y));}
44
- 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.numxticks'])){co.moveTo(Math.round(x),this.centery-3);co.lineTo(Math.round(x),this.centery+3);}
45
- co.stroke();};this.drawChart=this.DrawChart=function()
46
- {var alpha=prop['chart.colors.alpha'];if(typeof(alpha)=='number'){var oldAlpha=co.globalAlpha;co.globalAlpha=alpha;}
47
- 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=[];}
48
- coords_dataset.push(coords);this.coords.push(coords);}
49
- this.coords2[dataset]=coords_dataset;co.strokeStyle=(typeof(prop['chart.strokestyle'])=='object'&&prop['chart.strokestyle'][dataset])?prop['chart.strokestyle'][dataset]:prop['chart.strokestyle'];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)';}
50
- 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]);}}
51
- 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]);}}
52
- var last_coords=coords_dataset;co.closePath();co.stroke();co.fill();}
53
- if(typeof(alpha)=='number'){co.globalAlpha=oldAlpha;}};this.getCoordinates=this.GetCoordinates=function(dataset,index)
54
- {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()
55
- {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']
56
- 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){RG.text2(this,{'color':color,'font':font,'size':size,'x':x,'y':y,'text':labels[i],'valign':'center','halign':halign,'bounding':bgBoxed,'boundingFill':bgFill,'bold':bold,'tag':'labels'});}}}};this.drawCircle=this.DrawCircle=function()
57
- {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()
58
- {if(RG.isArray(prop['chart.labels.specific'])&&prop['chart.labels.specific'].length){this.drawSpecificAxisLabels();return;}
59
- co.lineWidth=1;co.fillStyle='black';co.strokeStyle='black';var r=this.radius,font=prop['chart.text.font'],size=typeof(prop['chart.text.size.scale'])=='number'?prop['chart.text.size.scale']:prop['chart.text.size'],axes=prop['chart.labels.axes'].toLowerCase(),color=prop['chart.labels.axes.boxed.background'],drawzero=false,units_pre=prop['chart.units.pre'],units_post=prop['chart.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'];if(axes.indexOf('n')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.Text2(this,{'bold':bold[i],'font':font,'size':size,'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'});}
60
- drawzero=true;}
61
- if(axes.indexOf('s')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.Text2(this,{'bold':bold[i],'font':font,'size':size,'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'});}
62
- drawzero=true;}
63
- if(axes.indexOf('e')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.Text2(this,{'bold':bold[i],'font':font,'size':size,'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'});}
64
- drawzero=true;}
65
- if(axes.indexOf('w')>-1){for(var i=0;i<this.scale2.labels.length;++i){RG.Text2(this,{'bold':bold[i],'font':font,'size':size,'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'});}
66
- drawzero=true;}
67
- if(drawzero){RG.Text2(this,{font:font,size:size,x:centerx,y:centery,text:RG.numberFormat(this,Number(0).toFixed(),units_pre,units_post),valign:'center',halign:'center',bounding:prop['chart.labels.axes.boxed.zero'],boundingFill:color,boundingStroke:'rgba(0,0,0,0)',bold:prop['chart.labels.axes.bold.zero'],tag:'scale'});}};this.drawSpecificAxisLabels=this.DrawSpecificAxisLabels=function()
68
- {var labels=prop['chart.labels.specific'];var bold=RG.array_pad(prop['chart.labels.axes.bold'],labels.length);var boxed=RG.array_pad(prop['chart.labels.axes.boxed'],labels.length);var reversed_labels=RG.array_reverse(labels);var reversed_bold=RG.array_reverse(bold);var reversed_boxed=RG.array_reverse(boxed);var font=prop['chart.text.font'];var size=typeof(prop['chart.text.size.scale'])=='number'?prop['chart.text.size.scale']:prop['chart.text.size'];var axes=prop['chart.labels.axes'].toLowerCase();co.fillStyle=prop['chart.text.color'];for(var i=0;i<labels.length;++i){if(axes.indexOf('n')>-1)RG.Text2(this,{'tag':'labels.specific','bold':reversed_bold[i],'font':font,'size':size,'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,{'tag':'labels.specific','bold':bold[i],'font':font,'size':size,'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,{'tag':'labels.specific','bold':reversed_bold[i],'font':font,'size':size,'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,{'tag':'labels.specific','bold':bold[i],'font':font,'size':size,'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)
69
- {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)
70
- {if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else{RG.Highlight.Point(this,shape);}};this.getObjectByXY=function(e)
71
- {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()
72
- {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.strokestyle'])=='object'&&prop['chart.strokestyle'][dataset])?prop['chart.strokestyle'][dataset]:prop['chart.strokestyle']);co.arc(this.coords[sequentialIdx][0],this.coords[sequentialIdx][1],radius,0,RG.TWOPI,false);co.stroke();co.fill();++sequentialIdx;}}}};this.getRadius=function(value)
73
- {if(value<0||value>this.max){return null;}
74
- var radius=(value/this.max)*this.radius;return radius;};this.getAngle=function(numitems,index)
75
- {var angle=(RG.TWOPI/numitems)*index;angle-=RG.HALFPI;return angle;};this.parseColors=function()
76
- {if(this.original_colors.length===0){this.original_colors['chart.colors']=RG.array_clone(prop['chart.colors']);this.original_colors['chart.key.colors']=RG.array_clone(prop['chart.key.colors']);this.original_colors['chart.title.color']=RG.array_clone(prop['chart.title.color']);this.original_colors['chart.text.color']=RG.array_clone(prop['chart.text.color']);this.original_colors['chart.highlight.stroke']=RG.array_clone(prop['chart.highlight.stroke']);this.original_colors['chart.highlight.fill']=RG.array_clone(prop['chart.highlight.fill']);this.original_colors['chart.circle.fill']=RG.array_clone(prop['chart.circle.fill']);this.original_colors['chart.circle.stroke']=RG.array_clone(prop['chart.circle.stroke']);}
77
- for(var i=0;i<prop['chart.colors'].length;++i){prop['chart.colors'][i]=this.parseSingleColorForGradient(prop['chart.colors'][i]);}
78
- 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]);}}
79
- prop['chart.title.color']=this.parseSingleColorForGradient(prop['chart.title.color']);prop['chart.text.color']=this.parseSingleColorForGradient(prop['chart.text.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']);};this.reset=function()
80
- {};this.parseSingleColorForGradient=function(color)
81
- {if(!color||typeof(color)!='string'){return color;}
82
- if(color.match(/^gradient\((.*)\)$/i)){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]));}}
83
- return grad?grad:color;};this.addFillListeners=this.AddFillListeners=function(e)
84
- {var obj=this;var func=function(e)
85
- {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);}
86
- 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]);}
87
- 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]);}}
88
- co.closePath();if(co.isPointInPath(mouseXY[0],mouseXY[1])){var inPath=true;break;}}
89
- if(inPath){var fillTooltips=prop['chart.fill.tooltips'];if(e.type=='click'){if(prop['chart.fill.click']){prop['chart.fill.click'](e,dataset);}
90
- if(prop['chart.fill.tooltips']&&prop['chart.fill.tooltips'][dataset]){obj.DatasetTooltip(e,dataset);}}
91
- if(e.type=='mousemove'){if(prop['chart.fill.mousemove']){prop['chart.fill.mousemove'](e,dataset);}
92
- if(!RG.is_null(fillTooltips)){e.target.style.cursor='pointer';}
93
- if(prop['chart.fill.tooltips']&&prop['chart.fill.tooltips'][dataset]){e.target.style.cursor='pointer';}}
94
- 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);}
95
- if(prop['chart.fill.mousemove']||!RG.is_null(prop['chart.fill.tooltips'])){ca.addEventListener('mousemove',func,false);}};this.highlightDataset=this.HighlightDataset=function(dataset)
96
- {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]);}}
97
- 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]);}}
98
- 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)
99
- {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)
100
- {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]);}}
101
- co.closePath();co.stroke();co.lineWidth=pre_linewidth;co.lineCap=pre_linecap;}};this.on=function(type,func)
102
- {if(type.substr(0,2)!=='on'){type='on'+type;}
103
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
104
- return this;};this.firstDrawFunc=function()
105
- {};this.grow=function()
106
- {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()
107
- {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];}}
108
- RGraph.clear(obj.canvas);RGraph.redrawCanvas(obj.canvas);if(frame<frames){frame++;RGraph.Effects.updateCanvas(iterator);}else{callback(obj);}}
109
- iterator();return this;};this.trace=function()
110
- {var obj=this;var opt=arguments[0]||{};var frames=opt.frames||60;var frame=0;var callback=arguments[1]||function(){};obj.Set('animation.trace.clip',0);var iterator=function()
111
- {if(frame<frames){obj.Set('animation.trace.clip',frame/frames);frame++;RG.redrawCanvas(obj.canvas);RG.Effects.updateCanvas(iterator);}else{obj.Set('animation.trace.clip',1);RG.redrawCanvas(obj.canvas);callback(obj);}};iterator();return this;};RG.att(ca);RG.Register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};
13
+ RGraph = window.RGraph || {isRGraph: true};
14
+
15
+ /**
16
+ * The traditional radar chart constructor
17
+ *
18
+ * @param string id The ID of the canvas
19
+ * @param array data An array of data to represent
20
+ */
21
+ RGraph.Radar = function (conf)
22
+ {
23
+ /**
24
+ * Allow for object config style
25
+ */
26
+ if ( typeof conf === 'object'
27
+ && typeof conf.data === 'object'
28
+ && typeof conf.id === 'string') {
29
+
30
+ var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
31
+
32
+ // Turn conf.data into a multi-d array if it's not already
33
+ if (typeof conf.data[0] === 'number' || typeof conf.data[0] === 'string') {
34
+ conf.data = [conf.data];
35
+ }
36
+
37
+ } else {
38
+
39
+ var conf = {id: conf, data: []};
40
+
41
+ // Arguments style: var foo = new RGraph.Radar('cvs', [1,2,3], [1,2,3], [1,2,3]);
42
+ if (typeof arguments[1] === 'object' && typeof arguments[1][0] === 'number') {
43
+ for (var i=1; i<arguments.length; ++i) {
44
+ conf.data.push(RGraph.arrayClone(arguments[i]));
45
+ }
46
+
47
+ // Arguments style: var foo = new RGraph.Radar('cvs', [[1,2,3], [1,2,3], [1,2,3]]);
48
+ } else if ( typeof arguments[1] === 'object'
49
+ && typeof arguments[1][0] === 'object'
50
+ && typeof arguments[1][0][0] === 'number') {
51
+
52
+ conf.data = RGraph.arrayClone(arguments[1]);
53
+ }
54
+ }
55
+
56
+
57
+
58
+
59
+ this.id = conf.id;
60
+ this.canvas = document.getElementById(conf.id);
61
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
62
+ this.canvas.__object__ = this;
63
+ this.type = 'radar';
64
+ this.isRGraph = true;
65
+ this.data = [];
66
+ this.max = 0;
67
+ this.uid = RGraph.CreateUID();
68
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
69
+ this.colorsParsed = false;
70
+ this.coords = [];
71
+ this.coordsText = [];
72
+ this.original_data = [];
73
+ this.original_colors = [];
74
+ this.firstDraw = true; // After the first draw this will be false
75
+
76
+ /**
77
+ * Add the data to the .original_data array and work out the max value
78
+ *
79
+ * 2/5/14 Now also use this loop to ensure that the data pieces
80
+ * are numbers
81
+ */
82
+ for (var i=0,len=conf.data.length; i<len; ++i) {
83
+
84
+ // Convert strings to numbers
85
+ for (var j=0; j<conf.data[i].length; ++j) {
86
+ if (typeof conf.data[i][j] === 'string') {
87
+ conf.data[i][j] = parseFloat(conf.data[i][j]);
88
+ }
89
+ }
90
+
91
+ this.original_data.push(RGraph.arrayClone(conf.data[i]));
92
+ this.data.push(RGraph.arrayClone(conf.data[i]));
93
+ this.max = Math.max(this.max, RGraph.arrayMax(conf.data[i]));
94
+ }
95
+
96
+
97
+ this.properties =
98
+ {
99
+ 'chart.strokestyle': '#aaa',
100
+ 'chart.gutter.left': 25,
101
+ 'chart.gutter.right': 25,
102
+ 'chart.gutter.top': 25,
103
+ 'chart.gutter.bottom': 25,
104
+ 'chart.linewidth': 1,
105
+ 'chart.colors': ['rgba(255,255,0,0.25)','rgba(0,255,255,0.25)','rgba(255,0,0,0.5)', 'red', 'green', 'blue', 'pink', 'aqua','brown','orange','grey'],
106
+ 'chart.colors.alpha': null,
107
+ 'chart.circle': 0,
108
+ 'chart.circle.fill': 'red',
109
+ 'chart.circle.stroke': 'black',
110
+
111
+ 'chart.labels': [],
112
+ 'chart.labels.color': null,
113
+ 'chart.labels.offset': 10,
114
+ 'chart.labels.axes': '',
115
+ 'chart.labels.background.fill': 'white',
116
+ 'chart.labels.boxed': false,
117
+ 'chart.labels.axes.bold': [],
118
+ 'chart.labels.axes.boxed': null, // This defaults to true - but that's set in the Draw() method
119
+ 'chart.labels.axes.boxed.zero': true,
120
+ 'chart.labels.axes.boxed.background': 'rgba(255,255,255,0.9)',
121
+ 'chart.labels.specific': [],
122
+
123
+ 'chart.labels.count': 5,
124
+ 'chart.background.circles': true,
125
+ 'chart.background.circles.count': null,
126
+ 'chart.background.circles.color': '#ddd',
127
+ 'chart.background.circles.poly': true,
128
+ 'chart.background.circles.spokes': 24,
129
+ 'chart.text.size': 12,
130
+ 'chart.text.size.scale': null,
131
+ 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
132
+ 'chart.text.color': 'black',
133
+ 'chart.text.accessible': true,
134
+ 'chart.text.accessible.overflow': 'visible',
135
+ 'chart.text.accessible.pointerevents': true,
136
+ 'chart.title': '',
137
+ 'chart.title.background': null,
138
+ 'chart.title.hpos': null,
139
+ 'chart.title.vpos': null,
140
+ 'chart.title.color': 'black',
141
+ 'chart.title.bold': true,
142
+ 'chart.title.font': null,
143
+ 'chart.title.x': null,
144
+ 'chart.title.y': null,
145
+ 'chart.title.halign': null,
146
+ 'chart.title.valign': null,
147
+ 'chart.linewidth': 1,
148
+ 'chart.key': null,
149
+ 'chart.key.background': 'white',
150
+ 'chart.key.shadow': false,
151
+ 'chart.key.shadow.color': '#666',
152
+ 'chart.key.shadow.blur': 3,
153
+ 'chart.key.shadow.offsetx': 2,
154
+ 'chart.key.shadow.offsety': 2,
155
+ 'chart.key.position': 'graph',
156
+ 'chart.key.halign': 'right',
157
+ 'chart.key.position.gutter.boxed': false,
158
+ 'chart.key.position.x': null,
159
+ 'chart.key.position.y': null,
160
+ 'chart.key.color.shape': 'square',
161
+ 'chart.key.rounded': true,
162
+ 'chart.key.linewidth': 1,
163
+ 'chart.key.colors': null,
164
+ 'chart.key.interactive': false,
165
+ 'chart.key.interactive.highlight.chart.stroke': 'rgba(255,0,0,0.3)',
166
+ 'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
167
+ 'chart.key.text.color': 'black',
168
+ 'chart.contextmenu': null,
169
+ 'chart.annotatable': false,
170
+ 'chart.annotate.color': 'black',
171
+ 'chart.zoom.factor': 1.5,
172
+ 'chart.zoom.fade.in': true,
173
+ 'chart.zoom.fade.out': true,
174
+ 'chart.zoom.hdir': 'right',
175
+ 'chart.zoom.vdir': 'down',
176
+ 'chart.zoom.frames': 25,
177
+ 'chart.zoom.delay': 16.666,
178
+ 'chart.zoom.shadow': true,
179
+ 'chart.zoom.background': true,
180
+ 'chart.zoom.action': 'zoom',
181
+ 'chart.tooltips.effect': 'fade',
182
+ 'chart.tooltips.event': 'onmousemove',
183
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
184
+ 'chart.tooltips.highlight': true,
185
+ 'chart.highlight.stroke': 'gray',
186
+ 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
187
+ 'chart.highlight.point.radius': 2,
188
+ 'chart.resizable': false,
189
+ 'chart.resize.handle.adjust': [0,0],
190
+ 'chart.resize.handle.background': null,
191
+ 'chart.ymax': null,
192
+ 'chart.accumulative': false,
193
+ 'chart.radius': null,
194
+ 'chart.events.click': null,
195
+ 'chart.events.mousemove': null,
196
+ 'chart.scale.decimals': 0,
197
+ 'chart.scale.point': '.',
198
+ 'chart.scale.thousand': ',',
199
+ 'chart.units.pre': '',
200
+ 'chart.units.post': '',
201
+ 'chart.tooltips': null,
202
+ 'chart.tooltips.event': 'onmousemove',
203
+ 'chart.centerx': null,
204
+ 'chart.centery': null,
205
+ 'chart.radius': null,
206
+ 'chart.numxticks': 5,
207
+ 'chart.numyticks': 5,
208
+ 'chart.axes.color': 'rgba(0,0,0,0)',
209
+ 'chart.highlights': false,
210
+ 'chart.highlights.stroke': '#ddd',
211
+ 'chart.highlights.fill': null,
212
+ 'chart.highlights.radius': 3,
213
+ 'chart.fill.click': null,
214
+ 'chart.fill.mousemove': null,
215
+ 'chart.fill.tooltips': null,
216
+ 'chart.fill.highlight.fill': 'rgba(255,255,255,0.7)',
217
+ 'chart.fill.highlight.stroke': 'rgba(0,0,0,0)',
218
+ 'chart.fill.mousemove.redraw': false,
219
+ 'chart.animation.trace.clip': 1,
220
+ 'chart.clearto': 'rgba(0,0,0,0)'
221
+ }
222
+
223
+
224
+
225
+ // Must have at least 3 points
226
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
227
+ if (this.data[dataset].length < 3) {
228
+ alert('[RADAR] You must specify at least 3 data points');
229
+ return;
230
+ }
231
+ }
232
+
233
+
234
+ /**
235
+ * Linearize the data and then create the $ objects
236
+ */
237
+ var idx = 0;
238
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
239
+ for (var i=0,len=this.data[dataset].length; i<len; ++i) {
240
+ this['$' + (idx++)] = {};
241
+ }
242
+ }
243
+
244
+
245
+
246
+ /**
247
+ * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
248
+ * done already
249
+ */
250
+ if (!this.canvas.__rgraph_aa_translated__) {
251
+ this.context.translate(0.5,0.5);
252
+
253
+ this.canvas.__rgraph_aa_translated__ = true;
254
+ }
255
+
256
+
257
+
258
+
259
+ // Short variable names
260
+ var RG = RGraph,
261
+ ca = this.canvas,
262
+ co = ca.getContext('2d'),
263
+ prop = this.properties,
264
+ pa2 = RG.path2,
265
+ win = window,
266
+ doc = document,
267
+ ma = Math
268
+
269
+
270
+
271
+ /**
272
+ * "Decorate" the object with the generic effects if the effects library has been included
273
+ */
274
+ if (RG.Effects && typeof RG.Effects.decorate === 'function') {
275
+ RG.Effects.decorate(this);
276
+ }
277
+
278
+
279
+
280
+
281
+ /**
282
+ * A simple setter
283
+ *
284
+ * @param string name The name of the property to set
285
+ * @param string value The value of the property
286
+ */
287
+ this.set =
288
+ this.Set = function (name, value)
289
+ {
290
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
291
+
292
+ /**
293
+ * the number of arguments is only one and it's an
294
+ * object - parse it for configuration data and return.
295
+ */
296
+ if (arguments.length === 1 && typeof name === 'object') {
297
+ RG.parseObjectStyleConfig(this, name);
298
+ return this;
299
+ }
300
+
301
+
302
+
303
+
304
+
305
+ /**
306
+ * This should be done first - prepend the propertyy name with "chart." if necessary
307
+ */
308
+ if (name.substr(0,6) != 'chart.') {
309
+ name = 'chart.' + name;
310
+ }
311
+
312
+
313
+
314
+
315
+ // Convert uppercase letters to dot+lower case letter
316
+ while(name.match(/([A-Z])/)) {
317
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
318
+ }
319
+
320
+
321
+
322
+
323
+
324
+
325
+ if (name == 'chart.text.diameter') {
326
+ name = 'chart.text.size';
327
+ }
328
+
329
+ /**
330
+ * If the name is chart.color, set chart.colors too
331
+ */
332
+ if (name == 'chart.color') {
333
+ this.properties['chart.colors'] = [value];
334
+ }
335
+
336
+
337
+
338
+
339
+
340
+
341
+ prop[name] = value;
342
+
343
+ return this;
344
+ };
345
+
346
+
347
+
348
+
349
+
350
+ /**
351
+ * A simple getter
352
+ *
353
+ * @param string name The name of the property to get
354
+ */
355
+ this.get =
356
+ this.Get = function (name)
357
+ {
358
+ /**
359
+ * This should be done first - prepend the property name with "chart." if necessary
360
+ */
361
+ if (name.substr(0,6) != 'chart.') {
362
+ name = 'chart.' + name;
363
+ }
364
+
365
+ // Convert uppercase letters to dot+lower case letter
366
+ while(name.match(/([A-Z])/)) {
367
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
368
+ }
369
+
370
+ if (name == 'chart.text.diameter') {
371
+ name = 'chart.text.size';
372
+ }
373
+
374
+ return prop[name];
375
+ };
376
+
377
+
378
+
379
+
380
+ /**
381
+ * The draw method which does all the brunt of the work
382
+ */
383
+ this.draw =
384
+ this.Draw = function ()
385
+ {
386
+ /**
387
+ * Fire the onbeforedraw event
388
+ */
389
+ RG.FireCustomEvent(this, 'onbeforedraw');
390
+
391
+ // NB: Colors are parsed further down
392
+
393
+ // Reset the coords array to stop it growing
394
+ this.coords = [];
395
+ this.coords2 = [];
396
+ this.coordsText = [];
397
+
398
+ /**
399
+ * Reset the data to the original_data
400
+ */
401
+ this.data = RG.arrayClone(this.original_data);
402
+
403
+ // Loop thru the data array if chart.accumulative is enable checking to see if all the
404
+ // datasets have the same number of elements.
405
+ if (prop['chart.accumulative']) {
406
+ for (var i=0; i<this.data.length; ++i) {
407
+ if (this.data[i].length != this.data[0].length) {
408
+ alert('[RADAR] Error! When the radar has chart.accumulative set to true all the datasets must have the same number of elements');
409
+ }
410
+ }
411
+ }
412
+
413
+
414
+ /**
415
+ * This defaults to true, but needs to be an array with a size matching the number of
416
+ * labels.
417
+ */
418
+ if (RG.isNull(prop['chart.labels.axes.boxed'])) {
419
+ prop['chart.labels.axes.boxed'] = [];
420
+ for (var i=0; i<(prop['chart.labels.specific'].length || prop['chart.labels.count'] || 5); ++i) {
421
+ prop['chart.labels.axes.boxed'][i] = false;
422
+ }
423
+ }
424
+
425
+
426
+
427
+
428
+ /**
429
+ * This is new in May 2011 and facilitates indiviual gutter settings,
430
+ * eg chart.gutter.left
431
+ */
432
+ this.gutterLeft = prop['chart.gutter.left'];
433
+ this.gutterRight = prop['chart.gutter.right'];
434
+ this.gutterTop = prop['chart.gutter.top'];
435
+ this.gutterBottom = prop['chart.gutter.bottom'];
436
+
437
+ this.centerx = ((ca.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
438
+ this.centery = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
439
+ this.radius = Math.min(ca.width - this.gutterLeft - this.gutterRight, ca.height - this.gutterTop - this.gutterBottom) / 2;
440
+
441
+
442
+
443
+ /**
444
+ * Allow these to be set by hand
445
+ */
446
+ if (typeof prop['chart.centerx'] == 'number') this.centerx = 2 * prop['chart.centerx'];
447
+ if (typeof prop['chart.centery'] == 'number') this.centery = 2 * prop['chart.centery'];
448
+ if (typeof prop['chart.radius'] == 'number') this.radius = prop['chart.radius'];
449
+
450
+
451
+ /**
452
+ * Parse the colors for gradients. Its down here so that the center X/Y can be used
453
+ */
454
+ if (!this.colorsParsed) {
455
+
456
+ this.parseColors();
457
+
458
+ // Don't want to do this again
459
+ this.colorsParsed = true;
460
+ }
461
+
462
+
463
+
464
+ // Work out the maximum value and the sum
465
+ if (!prop['chart.ymax']) {
466
+
467
+ // this.max is calculated in the constructor
468
+
469
+ // Work out this.max again if the chart is (now) set to be accumulative
470
+ if (prop['chart.accumulative']) {
471
+
472
+ var accumulation = [];
473
+ var len = this.original_data[0].length
474
+
475
+ for (var i=1; i<this.original_data.length; ++i) {
476
+ if (this.original_data[i].length != len) {
477
+ alert('[RADAR] Error! Stacked Radar chart datasets must all be the same size!');
478
+ }
479
+
480
+ for (var j=0; j<this.original_data[i].length; ++j) {
481
+ this.data[i][j] += this.data[i - 1][j];
482
+ this.max = Math.max(this.max, this.data[i][j]);
483
+ }
484
+ }
485
+ }
486
+
487
+
488
+ this.scale2 = RG.getScale2(this, {'max':typeof(prop['chart.ymax']) == 'number' ? prop['chart.ymax'] : this.max,
489
+ 'min':0,
490
+ 'scale.decimals':Number(prop['chart.scale.decimals']),
491
+ 'scale.point':prop['chart.scale.point'],
492
+ 'scale.thousand':prop['chart.scale.thousand'],
493
+ 'scale.round':prop['chart.scale.round'],
494
+ 'units.pre':prop['chart.units.pre'],
495
+ 'units.post':prop['chart.units.post'],
496
+ 'ylabels.count':prop['chart.labels.count']
497
+ });
498
+ this.max = this.scale2.max;
499
+
500
+ } else {
501
+ var ymax = prop['chart.ymax'];
502
+
503
+ this.scale2 = RG.getScale2(this, {'max':ymax,
504
+ 'min':0,
505
+ 'strict':true,
506
+ 'scale.decimals':Number(prop['chart.scale.decimals']),
507
+ 'scale.point':prop['chart.scale.point'],
508
+ 'scale.thousand':prop['chart.scale.thousand'],
509
+ 'scale.round':prop['chart.scale.round'],
510
+ 'units.pre':prop['chart.units.pre'],
511
+ 'units.post':prop['chart.units.post'],
512
+ 'ylabels.count':prop['chart.labels.count']
513
+ });
514
+ this.max = this.scale2.max;
515
+ }
516
+
517
+ this.drawBackground();
518
+ this.drawAxes();
519
+ this.drawCircle();
520
+ this.drawLabels();
521
+
522
+
523
+ /**
524
+ * Allow clipping
525
+ */
526
+ co.save();
527
+ co.beginPath();
528
+ co.arc(this.centerx, this.centery, this.radius * 2, -RG.HALFPI, (RG.TWOPI * prop['chart.animation.trace.clip']) - RG.HALFPI, false);
529
+ co.lineTo(this.centerx, this.centery);
530
+ co.closePath();
531
+ co.clip();
532
+
533
+ this.DrawChart();
534
+ this.DrawHighlights();
535
+ co.restore();
536
+
537
+ //
538
+ // Draw the axis labels
539
+ //
540
+ this.drawAxisLabels();
541
+
542
+ // Draw the title
543
+ if (prop['chart.title']) {
544
+ RG.DrawTitle(this, prop['chart.title'], this.gutterTop, null, prop['chart.title.diameter'] ? prop['chart.title.diameter'] : null)
545
+ }
546
+
547
+ // Draw the key if necessary
548
+ // obj, key, colors
549
+ if (prop['chart.key']) {
550
+ RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
551
+ }
552
+
553
+ /**
554
+ * Show the context menu
555
+ */
556
+ if (prop['chart.contextmenu']) {
557
+ RG.ShowContext(this);
558
+ }
559
+
560
+
561
+ /**
562
+ * This function enables resizing
563
+ */
564
+ if (prop['chart.resizable']) {
565
+ RG.AllowResizing(this);
566
+ }
567
+
568
+
569
+ /**
570
+ * This installs the event listeners
571
+ */
572
+ RG.InstallEventListeners(this);
573
+
574
+ /**
575
+ * This installs the Radar chart specific area listener
576
+ */
577
+ if ( (prop['chart.fill.click'] || prop['chart.fill.mousemove'] || !RG.is_null(prop['chart.fill.tooltips'])) && !this.__fill_click_listeners_installed__) {
578
+ this.AddFillListeners();
579
+ this.__fill_click_listeners_installed__ = true;
580
+ }
581
+
582
+
583
+
584
+ /**
585
+ * Fire the onfirstdraw event
586
+ */
587
+ if (this.firstDraw) {
588
+ RG.fireCustomEvent(this, 'onfirstdraw');
589
+ this.firstDraw = false;
590
+ this.firstDrawFunc();
591
+ }
592
+
593
+
594
+
595
+
596
+ /**
597
+ * Fire the RGraph ondraw event
598
+ */
599
+ RGraph.FireCustomEvent(this, 'ondraw');
600
+
601
+ return this;
602
+ };
603
+
604
+
605
+
606
+ /**
607
+ * Used in chaining. Runs a function there and then - not waiting for
608
+ * the events to fire (eg the onbeforedraw event)
609
+ *
610
+ * @param function func The function to execute
611
+ */
612
+ this.exec = function (func)
613
+ {
614
+ func(this);
615
+
616
+ return this;
617
+ };
618
+
619
+
620
+
621
+
622
+ /**
623
+ * Draws the background circles
624
+ */
625
+ this.drawBackground =
626
+ this.DrawBackground = function ()
627
+ {
628
+ var color = prop['chart.background.circles.color'];
629
+ var poly = prop['chart.background.circles.poly'];
630
+ var spacing = prop['chart.background.circles.spacing'];
631
+ var spokes = prop['chart.background.circles.spokes'];
632
+
633
+
634
+
635
+
636
+ // Set the linewidth for the grid (so that repeated redrawing works OK)
637
+ co.lineWidth = 1;
638
+
639
+
640
+
641
+
642
+ /**
643
+ * Draws the background circles
644
+ */
645
+ if (prop['chart.background.circles'] && poly == false) {
646
+
647
+
648
+
649
+
650
+
651
+ // Draw the concentric circles
652
+ co.strokeStyle = color;
653
+ co.beginPath();
654
+
655
+ var numrings = typeof(prop['chart.background.circles.count']) == 'number' ? prop['chart.background.circles.count'] : prop['chart.labels.count'];
656
+
657
+ // TODO Currently set to 5 - needs changing
658
+ for (var r=0; r<=this.radius; r+=(this.radius / numrings)) {
659
+ co.moveTo(this.centerx, this.centery);
660
+ co.arc(this.centerx, this.centery,r, 0, RG.TWOPI, false);
661
+ }
662
+ co.stroke();
663
+
664
+
665
+
666
+
667
+
668
+ /**
669
+ * Draw the diagonals/spokes
670
+ */
671
+ co.strokeStyle = color;
672
+
673
+ for (var i=0; i<360; i+=(360 / spokes)) {
674
+ co.beginPath();
675
+ co.arc(this.centerx,
676
+ this.centery,
677
+ this.radius,
678
+ (i / 360) * RG.TWOPI,
679
+ ((i+0.001) / 360) * RG.TWOPI,
680
+ false); // The 0.01 avoids a bug in Chrome 6
681
+ co.lineTo(this.centerx, this.centery);
682
+ co.stroke();
683
+ }
684
+
685
+
686
+
687
+
688
+
689
+
690
+ /**
691
+ * The background"circles" are actually drawn as a poly based on how many points there are
692
+ * (ie hexagons if there are 6 points, squares if the are four etc)
693
+ */
694
+ } else if (prop['chart.background.circles'] && poly == true) {
695
+
696
+ /**
697
+ * Draw the diagonals/spokes
698
+ */
699
+ co.strokeStyle = color;
700
+ var increment = 360 / this.data[0].length
701
+
702
+ for (var i=0; i<360; i+=increment) {
703
+ co.beginPath();
704
+ co.arc(this.centerx,
705
+ this.centery,
706
+ this.radius,
707
+ ((i / 360) * RG.TWOPI) - RG.HALFPI,
708
+ (((i + 0.001) / 360) * RG.TWOPI) - RG.HALFPI,
709
+ false); // The 0.001 avoids a bug in Chrome 6
710
+ co.lineTo(this.centerx, this.centery);
711
+ co.stroke();
712
+ }
713
+
714
+
715
+ /**
716
+ * Draw the lines that go around the Radar chart
717
+ */
718
+ co.strokeStyle = color;
719
+
720
+ var numrings = typeof(prop['chart.background.circles.count']) == 'number' ? prop['chart.background.circles.count'] : prop['chart.labels.count'];
721
+
722
+ for (var r=0; r<=this.radius; r+=(this.radius / numrings)) {
723
+ co.beginPath();
724
+ for (var a=0; a<=360; a+=(360 / this.data[0].length)) {
725
+ co.arc(this.centerx,
726
+ this.centery,
727
+ r,
728
+ RG.degrees2Radians(a) - RG.HALFPI,
729
+ RG.degrees2Radians(a) + 0.001 - RG.HALFPI,
730
+ false);
731
+ }
732
+ co.closePath();
733
+ co.stroke();
734
+ }
735
+ }
736
+ };
737
+
738
+
739
+
740
+
741
+ /**
742
+ * Draws the axes
743
+ */
744
+ this.drawAxes =
745
+ this.DrawAxes = function ()
746
+ {
747
+ co.strokeStyle = prop['chart.axes.color'];
748
+
749
+ var halfsize = this.radius;
750
+
751
+ co.beginPath();
752
+ /**
753
+ * The Y axis
754
+ */
755
+ co.moveTo(Math.round(this.centerx), this.centery + this.radius);
756
+ co.lineTo(Math.round(this.centerx), this.centery - this.radius);
757
+
758
+
759
+ // Draw the bits at either end of the Y axis
760
+ co.moveTo(this.centerx - 5, Math.round(this.centery + this.radius));
761
+ co.lineTo(this.centerx + 5, Math.round(this.centery + this.radius));
762
+ co.moveTo(this.centerx - 5, Math.round(this.centery - this.radius));
763
+ co.lineTo(this.centerx + 5, Math.round(this.centery - this.radius));
764
+
765
+ // Draw Y axis tick marks
766
+ for (var y=(this.centery - this.radius); y<(this.centery + this.radius); y+=(this.radius/prop['chart.numyticks'])) {
767
+ co.moveTo(this.centerx - 3, Math.round(y));
768
+ co.lineTo(this.centerx + 3, Math.round(y));
769
+ }
770
+
771
+ /**
772
+ * The X axis
773
+ */
774
+ co.moveTo(this.centerx - this.radius, Math.round(this.centery));
775
+ co.lineTo(this.centerx + this.radius, Math.round(this.centery));
776
+
777
+ // Draw the bits at the end of the X axis
778
+ co.moveTo(Math.round(this.centerx - this.radius), this.centery - 5);
779
+ co.lineTo(Math.round(this.centerx - this.radius), this.centery + 5);
780
+ co.moveTo(Math.round(this.centerx + this.radius), this.centery - 5);
781
+ co.lineTo(Math.round(this.centerx + this.radius), this.centery + 5);
782
+
783
+ // Draw X axis tick marks
784
+ for (var x=(this.centerx - this.radius); x<(this.centerx + this.radius); x+=(this.radius/prop['chart.numxticks'])) {
785
+ co.moveTo(Math.round(x), this.centery - 3);
786
+ co.lineTo(Math.round(x), this.centery + 3);
787
+ }
788
+
789
+ // Stroke it
790
+ co.stroke();
791
+ };
792
+
793
+
794
+
795
+
796
+ /**
797
+ * The function which actually draws the radar chart
798
+ */
799
+ this.drawChart =
800
+ this.DrawChart = function ()
801
+ {
802
+ var alpha = prop['chart.colors.alpha'];
803
+
804
+ if (typeof(alpha) == 'number') {
805
+ var oldAlpha = co.globalAlpha;
806
+ co.globalAlpha = alpha;
807
+ }
808
+
809
+ var numDatasets = this.data.length;
810
+
811
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
812
+
813
+ co.beginPath();
814
+
815
+ var coords_dataset = [];
816
+
817
+ for (var i=0; i<this.data[dataset].length; ++i) {
818
+
819
+ var coords = this.GetCoordinates(dataset, i);
820
+
821
+ if (coords_dataset == null) {
822
+ coords_dataset = [];
823
+ }
824
+
825
+ coords_dataset.push(coords);
826
+ this.coords.push(coords);
827
+ }
828
+
829
+ this.coords2[dataset] = coords_dataset;
830
+
831
+
832
+ /**
833
+ * Now go through the coords and draw the chart itself
834
+ *
835
+ * 18/5/2012 - chart.strokestyle can now be an array of colors as well as a single color
836
+ */
837
+
838
+ co.strokeStyle = (typeof(prop['chart.strokestyle']) == 'object' && prop['chart.strokestyle'][dataset]) ? prop['chart.strokestyle'][dataset] : prop['chart.strokestyle'];
839
+ co.fillStyle = prop['chart.colors'][dataset] ? prop['chart.colors'][dataset] : 'rgba(0,0,0,0)';
840
+ if (co.fillStyle === 'transparent') {
841
+ co.fillStyle = 'rgba(0,0,0,0)';
842
+ }
843
+ co.lineWidth = prop['chart.linewidth'];
844
+
845
+ for (i=0; i<coords_dataset.length; ++i) {
846
+ if (i == 0) {
847
+ co.moveTo(coords_dataset[i][0], coords_dataset[i][1]);
848
+ } else {
849
+ co.lineTo(coords_dataset[i][0], coords_dataset[i][1]);
850
+ }
851
+ }
852
+
853
+
854
+ // If on the second or greater dataset, backtrack
855
+ if (prop['chart.accumulative'] && dataset > 0) {
856
+
857
+ // This goes back to the start coords of this particular dataset
858
+ co.lineTo(coords_dataset[0][0], coords_dataset[0][1]);
859
+
860
+ //Now move down to the end point of the previous dataset
861
+ co.moveTo(last_coords[0][0], last_coords[0][1]);
862
+
863
+ for (var i=coords_dataset.length - 1; i>=0; --i) {
864
+ co.lineTo(last_coords[i][0], last_coords[i][1]);
865
+ }
866
+ }
867
+
868
+ // This is used by the next iteration of the loop
869
+ var last_coords = coords_dataset;
870
+
871
+ co.closePath();
872
+
873
+ co.stroke();
874
+ co.fill();
875
+ }
876
+
877
+ // Reset the globalAlpha
878
+ if (typeof(alpha) == 'number') {
879
+ co.globalAlpha = oldAlpha;
880
+ }
881
+ };
882
+
883
+
884
+
885
+
886
+ /**
887
+ * Gets the coordinates for a particular mark
888
+ *
889
+ * @param number i The index of the data (ie which one it is)
890
+ * @return array A two element array of the coordinates
891
+ */
892
+ this.getCoordinates =
893
+ this.GetCoordinates = function (dataset, index)
894
+ {
895
+ // The number of data points
896
+ var len = this.data[dataset].length;
897
+
898
+ // The magnitude of the data (NOT the x/y coords)
899
+ var mag = (this.data[dataset][index] / this.max) * this.radius;
900
+
901
+ /**
902
+ * Get the angle
903
+ */
904
+ var angle = (RG.TWOPI / len) * index; // In radians
905
+ angle -= RG.HALFPI;
906
+
907
+
908
+ /**
909
+ * Work out the X/Y coordinates
910
+ */
911
+ var x = Math.cos(angle) * mag;
912
+ var y = Math.sin(angle) * mag;
913
+
914
+ /**
915
+ * Put the coordinate in the right quadrant
916
+ */
917
+ x = this.centerx + x;
918
+ y = this.centery + y;
919
+
920
+ return [x,y];
921
+ };
922
+
923
+
924
+
925
+
926
+ /**
927
+ * This function adds the labels to the chart
928
+ */
929
+ this.drawLabels =
930
+ this.DrawLabels = function ()
931
+ {
932
+ var labels = prop['chart.labels'];
933
+
934
+ if (labels && labels.length > 0) {
935
+
936
+ co.lineWidth = 1;
937
+ co.strokeStyle = 'gray';
938
+ co.fillStyle = prop['chart.labels.color'] || prop['chart.text.color'];
939
+
940
+ var bgFill = prop['chart.labels.background.fill'],
941
+ bold = prop['chart.labels.bold'],
942
+ bgBoxed = prop['chart.labels.boxed'],
943
+ offset = prop['chart.labels.offset'],
944
+ font = prop['chart.text.font'],
945
+ size = prop['chart.text.size'],
946
+ radius = this.radius,
947
+ color = prop['chart.labels.color'] || prop['chart.text.color']
948
+
949
+ for (var i=0; i<labels.length; ++i) {
950
+
951
+ var angle = (RG.TWOPI / prop['chart.labels'].length) * i;
952
+ angle -= RG.HALFPI;
953
+
954
+ var x = this.centerx + (ma.cos(angle) * (radius + offset));
955
+ var y = this.centery + (ma.sin(angle) * (radius + offset));
956
+
957
+ /**
958
+ * Horizontal alignment
959
+ */
960
+ var halign = x < this.centerx ? 'right' : 'left' ;
961
+ if (i == 0 || (i / labels.length) == 0.5) halign = 'center';
962
+
963
+ if (labels[i] && labels[i].length) {
964
+
965
+ RG.text2(this, {
966
+ 'color': color,
967
+ 'font':font,
968
+ 'size':size,
969
+ 'x':x,
970
+ 'y':y,
971
+ 'text':labels[i],
972
+ 'valign':'center',
973
+ 'halign':halign,
974
+ 'bounding':bgBoxed,
975
+ 'boundingFill':bgFill,
976
+ 'bold':bold,
977
+ 'tag': 'labels'
978
+ });
979
+ }
980
+ }
981
+ }
982
+ };
983
+
984
+
985
+
986
+
987
+ /**
988
+ * Draws the circle. No arguments as it gets the information from the object properties.
989
+ */
990
+ this.drawCircle =
991
+ this.DrawCircle = function ()
992
+ {
993
+ var circle = {};
994
+ circle.limit = prop['chart.circle'];
995
+ circle.fill = prop['chart.circle.fill'];
996
+ circle.stroke = prop['chart.circle.stroke'];
997
+
998
+ if (circle.limit) {
999
+
1000
+ var r = (circle.limit / this.max) * this.radius;
1001
+
1002
+ co.fillStyle = circle.fill;
1003
+ co.strokeStyle = circle.stroke;
1004
+
1005
+ co.beginPath();
1006
+ co.arc(this.centerx, this.centery, r, 0, RG.TWOPI, 0);
1007
+ co.fill();
1008
+ co.stroke();
1009
+ }
1010
+ };
1011
+
1012
+
1013
+
1014
+
1015
+ /**
1016
+ * Unsuprisingly, draws the labels
1017
+ */
1018
+ this.drawAxisLabels =
1019
+ this.DrawAxisLabels = function ()
1020
+ {
1021
+ /**
1022
+ * Draw specific axis labels
1023
+ */
1024
+ if (RG.isArray(prop['chart.labels.specific']) && prop['chart.labels.specific'].length) {
1025
+ this.drawSpecificAxisLabels();
1026
+ return;
1027
+ }
1028
+
1029
+ co.lineWidth = 1;
1030
+
1031
+ // Set the color to black
1032
+ co.fillStyle = 'black';
1033
+ co.strokeStyle = 'black';
1034
+
1035
+ var r = this.radius,
1036
+ font = prop['chart.text.font'],
1037
+ size = typeof(prop['chart.text.size.scale']) == 'number' ? prop['chart.text.size.scale'] : prop['chart.text.size'],
1038
+ axes = prop['chart.labels.axes'].toLowerCase(),
1039
+ color = prop['chart.labels.axes.boxed.background'],
1040
+ drawzero = false,
1041
+ units_pre = prop['chart.units.pre'],
1042
+ units_post = prop['chart.units.post'],
1043
+ decimals = prop['chart.scale.decimals'],
1044
+ bold = prop['chart.labels.axes.bold'],
1045
+ boxed = prop['chart.labels.axes.boxed'],
1046
+ centerx = this.centerx,
1047
+ centery = this.centery,
1048
+ scale = this.scale;
1049
+
1050
+ co.fillStyle = prop['chart.text.color'];
1051
+
1052
+ // The "North" axis labels
1053
+ if (axes.indexOf('n') > -1) {
1054
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1055
+ RG.Text2(this, {
1056
+ 'bold':bold[i],
1057
+ 'font':font,
1058
+ 'size':size,
1059
+ 'x':centerx,
1060
+ 'y':centery - (r * ((i+1)/this.scale2.labels.length)),
1061
+ 'text':this.scale2.labels[i],
1062
+ 'valign':'center',
1063
+ 'halign':'center',
1064
+ 'bounding':boxed[i] || color,
1065
+ 'boundingFill':color,
1066
+ 'boundingStroke':'rgba(0,0,0,0)',
1067
+ 'tag': 'scale'
1068
+ });
1069
+ }
1070
+
1071
+ drawzero = true;
1072
+ }
1073
+
1074
+ // The "South" axis labels
1075
+ if (axes.indexOf('s') > -1) {
1076
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1077
+ RG.Text2(this, {
1078
+ 'bold':bold[i],
1079
+ 'font':font,
1080
+ 'size':size,
1081
+ 'x':centerx,
1082
+ 'y':centery + (r * ((i+1)/this.scale2.labels.length)),
1083
+ 'text':this.scale2.labels[i],
1084
+ 'valign':'center',
1085
+ 'halign':'center',
1086
+ 'bounding':boxed[i] || color,
1087
+ 'boundingFill':color,
1088
+ 'boundingStroke':'rgba(0,0,0,0)',
1089
+ 'tag': 'scale'
1090
+ });
1091
+ }
1092
+
1093
+ drawzero = true;
1094
+ }
1095
+
1096
+ // The "East" axis labels
1097
+ if (axes.indexOf('e') > -1) {
1098
+
1099
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1100
+ RG.Text2(this, {
1101
+ 'bold':bold[i],
1102
+ 'font':font,
1103
+ 'size':size,
1104
+ 'x':centerx + (r * ((i+1)/this.scale2.labels.length)),
1105
+ 'y':centery,
1106
+ 'text':this.scale2.labels[i],
1107
+ 'valign':'center',
1108
+ 'halign':'center',
1109
+ 'bounding':boxed[i]|| color,
1110
+ 'boundingFill':color,
1111
+ 'boundingStroke':'rgba(0,0,0,0)',
1112
+ 'tag': 'scale'
1113
+ });
1114
+ }
1115
+
1116
+ drawzero = true;
1117
+ }
1118
+
1119
+ // The "West" axis labels
1120
+ if (axes.indexOf('w') > -1) {
1121
+
1122
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1123
+ RG.Text2(this, {
1124
+ 'bold':bold[i],
1125
+ 'font':font,
1126
+ 'size':size,
1127
+ 'x':centerx - (r * ((i+1)/this.scale2.labels.length)),
1128
+ 'y':centery,
1129
+ 'text':this.scale2.labels[i],
1130
+ 'valign':'center',
1131
+ 'halign':'center',
1132
+ 'bounding':boxed[i]|| color,
1133
+ 'boundingFill':color,
1134
+ 'boundingStroke':'rgba(0,0,0,0)',
1135
+ 'tag': 'scale'
1136
+ });
1137
+ }
1138
+
1139
+ drawzero = true;
1140
+ }
1141
+
1142
+ if (drawzero) {
1143
+ RG.Text2(this, {
1144
+ font:font,
1145
+ size:size,
1146
+ x:centerx,
1147
+ y:centery,
1148
+ text:RG.numberFormat(this, Number(0).toFixed(), units_pre, units_post),
1149
+ valign:'center',
1150
+ halign:'center',
1151
+ bounding:prop['chart.labels.axes.boxed.zero'],
1152
+ boundingFill:color,
1153
+ boundingStroke:'rgba(0,0,0,0)',
1154
+ bold:prop['chart.labels.axes.bold.zero'],
1155
+ tag: 'scale'
1156
+ });
1157
+ }
1158
+ };
1159
+
1160
+
1161
+
1162
+
1163
+ /**
1164
+ * Draws specific axis labels
1165
+ */
1166
+ this.drawSpecificAxisLabels =
1167
+ this.DrawSpecificAxisLabels = function ()
1168
+ {
1169
+ /**
1170
+ * Specific axis labels
1171
+ */
1172
+ var labels = prop['chart.labels.specific'];
1173
+ var bold = RG.array_pad(prop['chart.labels.axes.bold'],labels.length);
1174
+ var boxed = RG.array_pad(prop['chart.labels.axes.boxed'],labels.length);
1175
+ var reversed_labels = RG.array_reverse(labels);
1176
+ var reversed_bold = RG.array_reverse(bold);
1177
+ var reversed_boxed = RG.array_reverse(boxed);
1178
+ var font = prop['chart.text.font'];
1179
+ var size = typeof(prop['chart.text.size.scale']) == 'number' ? prop['chart.text.size.scale'] : prop['chart.text.size'];
1180
+ var axes = prop['chart.labels.axes'].toLowerCase();
1181
+
1182
+ co.fillStyle = prop['chart.text.color'];
1183
+
1184
+ for (var i=0; i<labels.length; ++i) {
1185
+
1186
+ if (axes.indexOf('n') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':reversed_bold[i],'font':font,'size':size,'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'});
1187
+ if (axes.indexOf('s') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':bold[i],'font':font,'size':size,'x':this.centerx,'y':this.centery + ((this.radius / labels.length) * (i+1)),'text':labels[i],'valign':'center','halign':'center','bounding':boxed[i],'boundingFill':'white'});
1188
+
1189
+ if (axes.indexOf('w') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':reversed_bold[i],'font':font,'size':size,'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'});
1190
+ if (axes.indexOf('e') > -1) RG.Text2(this, {'tag': 'labels.specific', 'bold':bold[i],'font':font,'size':size,'x':this.centerx + ((this.radius / labels.length) * (i+1)),'y':this.centery,'text':labels[i],'valign':'center','halign':'center','bounding':boxed[i],'boundingFill':'white'});
1191
+ }
1192
+ };
1193
+
1194
+
1195
+
1196
+
1197
+ /**
1198
+ * This method eases getting the focussed point (if any)
1199
+ *
1200
+ * @param event e The event object
1201
+ */
1202
+ this.getShape =
1203
+ this.getPoint = function (e)
1204
+ {
1205
+ for (var i=0; i<this.coords.length; ++i) {
1206
+
1207
+ var x = this.coords[i][0];
1208
+ var y = this.coords[i][1];
1209
+ var tooltips = prop['chart.tooltips'];
1210
+ var index = Number(i);
1211
+ var mouseXY = RG.getMouseXY(e);
1212
+ var mouseX = mouseXY[0];
1213
+ var mouseY = mouseXY[1];
1214
+
1215
+ if ( mouseX < (x + 5)
1216
+ && mouseX > (x - 5)
1217
+ && mouseY > (y - 5)
1218
+ && mouseY < (y + 5)
1219
+ ) {
1220
+
1221
+ var tooltip = RG.parseTooltipText(prop['chart.tooltips'], index);
1222
+
1223
+ return {0: this, 'object': this,
1224
+ 1: x, 'x': x,
1225
+ 2: y, 'y': y,
1226
+ 3: null, 'dataset': null,
1227
+ 4: index, 'index': i,
1228
+ 'tooltip': tooltip
1229
+ }
1230
+ }
1231
+ }
1232
+ };
1233
+
1234
+
1235
+
1236
+
1237
+ /**
1238
+ * Each object type has its own Highlight() function which highlights the appropriate shape
1239
+ *
1240
+ * @param object shape The shape to highlight
1241
+ */
1242
+ this.highlight =
1243
+ this.Highlight = function (shape)
1244
+ {
1245
+ if (typeof prop['chart.highlight.style'] === 'function') {
1246
+ (prop['chart.highlight.style'])(shape);
1247
+ } else {
1248
+ RG.Highlight.Point(this, shape);
1249
+ }
1250
+ };
1251
+
1252
+
1253
+
1254
+
1255
+ /**
1256
+ * The getObjectByXY() worker method. Don't call this call:
1257
+ *
1258
+ * RGraph.ObjectRegistry.getObjectByXY(e)
1259
+ *
1260
+ * @param object e The event object
1261
+ */
1262
+ this.getObjectByXY = function (e)
1263
+ {
1264
+ var mouseXY = RG.getMouseXY(e);
1265
+
1266
+ if (
1267
+ mouseXY[0] > (this.centerx - this.radius)
1268
+ && mouseXY[0] < (this.centerx + this.radius)
1269
+ && mouseXY[1] > (this.centery - this.radius)
1270
+ && mouseXY[1] < (this.centery + this.radius)
1271
+ ) {
1272
+
1273
+ return this;
1274
+ }
1275
+ };
1276
+
1277
+
1278
+
1279
+
1280
+ /**
1281
+ * This function positions a tooltip when it is displayed
1282
+ *
1283
+ * @param obj object The chart object
1284
+ * @param int x The X coordinate specified for the tooltip
1285
+ * @param int y The Y coordinate specified for the tooltip
1286
+ * @param objec tooltip The tooltips DIV element
1287
+ *
1288
+ this.positionTooltip = function (obj, x, y, tooltip, idx)
1289
+ {
1290
+ var dataset = tooltip.__dataset__;
1291
+ var index = tooltip.__index__;
1292
+ var coordX = this.coords[index][0];
1293
+ var coordY = this.coords[index][1];
1294
+ var canvasXY = RG.getCanvasXY(obj.canvas);
1295
+ var gutterLeft = this.gutterLeft;
1296
+ var gutterTop = this.gutterTop;
1297
+ var width = tooltip.offsetWidth;
1298
+ var height = tooltip.offsetHeight;
1299
+ var mouseXY = RG.getMouseXY(window.event);
1300
+
1301
+ // Set the top position
1302
+ tooltip.style.left = 0;
1303
+ tooltip.style.top = window.event.pageY - height - 5 + 'px';
1304
+
1305
+ // By default any overflow is hidden
1306
+ tooltip.style.overflow = '';
1307
+
1308
+ // Reposition the tooltip if at the edges:
1309
+
1310
+ // LEFT edge
1311
+ if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
1312
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + 'px';
1313
+
1314
+ // RIGHT edge
1315
+ } else if (canvasXY[0] + mouseXY[0] + (width / 2) > doc.body.offsetWidth) {
1316
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + 'px';
1317
+
1318
+ // Default positioning - CENTERED
1319
+ } else {
1320
+ tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + 'px';
1321
+ }
1322
+ };*/
1323
+
1324
+
1325
+
1326
+
1327
+ /**
1328
+ * This draws highlights on the points
1329
+ */
1330
+ this.drawHighlights =
1331
+ this.DrawHighlights = function ()
1332
+ {
1333
+ if (prop['chart.highlights']) {
1334
+
1335
+ var sequentialIdx = 0;
1336
+ var dataset = 0;
1337
+ var index = 0;
1338
+ var radius = prop['chart.highlights.radius'];
1339
+
1340
+
1341
+
1342
+ for (var dataset=0; dataset <this.data.length; ++dataset) {
1343
+ for (var index=0; index<this.data[dataset].length; ++index) {
1344
+ co.beginPath();
1345
+ co.strokeStyle = prop['chart.highlights.stroke'];
1346
+ co.fillStyle = prop['chart.highlights.fill'] ? prop['chart.highlights.fill'] : ((typeof(prop['chart.strokestyle']) == 'object' && prop['chart.strokestyle'][dataset]) ? prop['chart.strokestyle'][dataset] : prop['chart.strokestyle']);
1347
+ co.arc(this.coords[sequentialIdx][0], this.coords[sequentialIdx][1], radius, 0, RG.TWOPI, false);
1348
+ co.stroke();
1349
+ co.fill();
1350
+ ++sequentialIdx;
1351
+ }
1352
+ }
1353
+
1354
+ }
1355
+ };
1356
+
1357
+
1358
+
1359
+
1360
+ /**
1361
+ * This function returns the radius (ie the distance from the center) for a particular
1362
+ * value. Note that if you want the angle for a point you can use getAngle(index)
1363
+ *
1364
+ * @param number value The value you want the radius for
1365
+ */
1366
+ this.getRadius = function (value)
1367
+ {
1368
+ if (value < 0 || value > this.max) {
1369
+ return null;
1370
+ }
1371
+
1372
+ // Radar doesn't support minimum value
1373
+ var radius = (value / this.max) * this.radius;
1374
+
1375
+ return radius;
1376
+ };
1377
+
1378
+
1379
+
1380
+
1381
+ /**
1382
+ * This function returns the angle (in radians) for a particular index.
1383
+ *
1384
+ * @param number numitems The total number of items
1385
+ * @param number index The zero index number of the item to get the angle for
1386
+ */
1387
+ this.getAngle = function (numitems, index)
1388
+ {
1389
+ var angle = (RG.TWOPI / numitems) * index;
1390
+ angle -= RG.HALFPI;
1391
+
1392
+ return angle;
1393
+ };
1394
+
1395
+
1396
+
1397
+
1398
+ /**
1399
+ * This allows for easy specification of gradients
1400
+ */
1401
+ this.parseColors = function ()
1402
+ {
1403
+ // Save the original colors so that they can be restored when the canvas is reset
1404
+ if (this.original_colors.length === 0) {
1405
+ this.original_colors['chart.colors'] = RG.array_clone(prop['chart.colors']);
1406
+ this.original_colors['chart.key.colors'] = RG.array_clone(prop['chart.key.colors']);
1407
+ this.original_colors['chart.title.color'] = RG.array_clone(prop['chart.title.color']);
1408
+ this.original_colors['chart.text.color'] = RG.array_clone(prop['chart.text.color']);
1409
+ this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
1410
+ this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
1411
+ this.original_colors['chart.circle.fill'] = RG.array_clone(prop['chart.circle.fill']);
1412
+ this.original_colors['chart.circle.stroke'] = RG.array_clone(prop['chart.circle.stroke']);
1413
+ }
1414
+
1415
+ for (var i=0; i<prop['chart.colors'].length; ++i) {
1416
+ prop['chart.colors'][i] = this.parseSingleColorForGradient(prop['chart.colors'][i]);
1417
+ }
1418
+
1419
+ var keyColors = prop['chart.key.colors'];
1420
+
1421
+ if (typeof(keyColors) != 'null' && keyColors && keyColors.length) {
1422
+ for (var i=0; i<prop['chart.key.colors'].length; ++i) {
1423
+ prop['chart.key.colors'][i] = this.parseSingleColorForGradient(prop['chart.key.colors'][i]);
1424
+ }
1425
+ }
1426
+
1427
+ prop['chart.title.color'] = this.parseSingleColorForGradient(prop['chart.title.color']);
1428
+ prop['chart.text.color'] = this.parseSingleColorForGradient(prop['chart.text.color']);
1429
+ prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
1430
+ prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
1431
+ prop['chart.circle.fill'] = this.parseSingleColorForGradient(prop['chart.circle.fill']);
1432
+ prop['chart.circle.stroke'] = this.parseSingleColorForGradient(prop['chart.circle.stroke']);
1433
+ };
1434
+
1435
+
1436
+
1437
+
1438
+ /**
1439
+ * Use this function to reset the object to the post-constructor state. Eg reset colors if
1440
+ * need be etc
1441
+ */
1442
+ this.reset = function ()
1443
+ {
1444
+ };
1445
+
1446
+
1447
+
1448
+
1449
+ /**
1450
+ * This parses a single color value
1451
+ */
1452
+ this.parseSingleColorForGradient = function (color)
1453
+ {
1454
+ if (!color || typeof(color) != 'string') {
1455
+ return color;
1456
+ }
1457
+
1458
+ if (color.match(/^gradient\((.*)\)$/i)) {
1459
+
1460
+ var parts = RegExp.$1.split(':');
1461
+
1462
+ // Create the gradient
1463
+ var grad = co.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius);
1464
+
1465
+ var diff = 1 / (parts.length - 1);
1466
+
1467
+ grad.addColorStop(0, RG.trim(parts[0]));
1468
+
1469
+ for (var j=1; j<parts.length; ++j) {
1470
+ grad.addColorStop(j * diff, RG.trim(parts[j]));
1471
+ }
1472
+ }
1473
+
1474
+ return grad ? grad : color;
1475
+ };
1476
+
1477
+
1478
+
1479
+
1480
+ this.addFillListeners =
1481
+ this.AddFillListeners = function (e)
1482
+ {
1483
+ var obj = this;
1484
+
1485
+ var func = function (e)
1486
+ {
1487
+ //var canvas = e.target;
1488
+ //var context = canvas.getContext('2d');
1489
+ var coords = this.coords;
1490
+ var coords2 = this.coords2;
1491
+ var mouseXY = RG.getMouseXY(e);
1492
+ var dataset = 0;
1493
+
1494
+ if (e.type == 'mousemove' && prop['chart.fill.mousemove.redraw']) {
1495
+ RG.RedrawCanvas(ca);
1496
+ }
1497
+
1498
+ for (var dataset=(obj.coords2.length-1); dataset>=0; --dataset) {
1499
+
1500
+ // Draw the path again so that it can be checked
1501
+ co.beginPath();
1502
+ co.moveTo(obj.coords2[dataset][0][0], obj.coords2[dataset][0][1]);
1503
+ for (var j=0; j<obj.coords2[dataset].length; ++j) {
1504
+ co.lineTo(obj.coords2[dataset][j][0], obj.coords2[dataset][j][1]);
1505
+ }
1506
+
1507
+ // Draw a line back to the starting point
1508
+ co.lineTo(obj.coords2[dataset][0][0], obj.coords2[dataset][0][1]);
1509
+
1510
+ // Go thru the previous datasets coords in reverse order
1511
+ if (prop['chart.accumulative'] && dataset > 0) {
1512
+ co.lineTo(obj.coords2[dataset - 1][0][0], obj.coords2[dataset - 1][0][1]);
1513
+ for (var j=(obj.coords2[dataset - 1].length - 1); j>=0; --j) {
1514
+ co.lineTo(obj.coords2[dataset - 1][j][0], obj.coords2[dataset - 1][j][1]);
1515
+ }
1516
+ }
1517
+
1518
+ co.closePath();
1519
+
1520
+ if (co.isPointInPath(mouseXY[0], mouseXY[1])) {
1521
+ var inPath = true;
1522
+ break;
1523
+ }
1524
+ }
1525
+
1526
+ // Call the events
1527
+ if (inPath) {
1528
+
1529
+ var fillTooltips = prop['chart.fill.tooltips'];
1530
+
1531
+ /**
1532
+ * Click event
1533
+ */
1534
+ if (e.type == 'click') {
1535
+ if (prop['chart.fill.click']) {
1536
+ prop['chart.fill.click'](e, dataset);
1537
+ }
1538
+
1539
+ if (prop['chart.fill.tooltips'] && prop['chart.fill.tooltips'][dataset]) {
1540
+ obj.DatasetTooltip(e, dataset);
1541
+ }
1542
+ }
1543
+
1544
+
1545
+
1546
+ /**
1547
+ * Mousemove event
1548
+ */
1549
+ if (e.type == 'mousemove') {
1550
+
1551
+ if (prop['chart.fill.mousemove']) {
1552
+ prop['chart.fill.mousemove'](e, dataset);
1553
+ }
1554
+
1555
+ if (!RG.is_null(fillTooltips)) {
1556
+ e.target.style.cursor = 'pointer';
1557
+ }
1558
+
1559
+ if (prop['chart.fill.tooltips'] && prop['chart.fill.tooltips'][dataset]) {
1560
+ e.target.style.cursor = 'pointer';
1561
+ }
1562
+ }
1563
+
1564
+ e.stopPropagation();
1565
+
1566
+ } else if (e.type == 'mousemove') {
1567
+ ca.style.cursor = 'default';
1568
+ }
1569
+ };
1570
+
1571
+ /**
1572
+ * Add the click listener
1573
+ */
1574
+ if (prop['chart.fill.click'] || !RG.is_null(prop['chart.fill.tooltips'])) {
1575
+ ca.addEventListener('click', func, false);
1576
+ }
1577
+
1578
+ /**
1579
+ * Add the mousemove listener
1580
+ */
1581
+ if (prop['chart.fill.mousemove'] || !RG.is_null(prop['chart.fill.tooltips'])) {
1582
+ ca.addEventListener('mousemove', func, false);
1583
+ }
1584
+ };
1585
+
1586
+
1587
+
1588
+
1589
+ /**
1590
+ * This highlights a specific dataset on the chart
1591
+ *
1592
+ * @param number dataset The index of the dataset (which starts at zero)
1593
+ */
1594
+ this.highlightDataset =
1595
+ this.HighlightDataset = function (dataset)
1596
+ {
1597
+ co.beginPath();
1598
+ for (var j=0; j<this.coords2[dataset].length; ++j) {
1599
+ if (j == 0) {
1600
+ co.moveTo(this.coords2[dataset][0][0], this.coords2[dataset][0][1]);
1601
+ } else {
1602
+ co.lineTo(this.coords2[dataset][j][0], this.coords2[dataset][j][1]);
1603
+ }
1604
+ }
1605
+
1606
+ co.lineTo(this.coords2[dataset][0][0], this.coords2[dataset][0][1]);
1607
+
1608
+ if (prop['chart.accumulative'] && dataset > 0) {
1609
+ co.lineTo(this.coords2[dataset - 1][0][0], this.coords2[dataset - 1][0][1]);
1610
+ for (var j=(this.coords2[dataset - 1].length - 1); j>=0; --j) {
1611
+ co.lineTo(this.coords2[dataset - 1][j][0], this.coords2[dataset - 1][j][1]);
1612
+ }
1613
+ }
1614
+
1615
+ co.strokeStyle = prop['chart.fill.highlight.stroke'];
1616
+ co.fillStyle = prop['chart.fill.highlight.fill'];
1617
+
1618
+ co.stroke();
1619
+ co.fill();
1620
+ };
1621
+
1622
+
1623
+
1624
+
1625
+ /**
1626
+ * Shows a tooltip for a dataset (a "fill" tooltip), You can pecify these
1627
+ * with chart.fill.tooltips
1628
+ */
1629
+ this.datasetTooltip =
1630
+ this.DatasetTooltip = function (e, dataset)
1631
+ {
1632
+ // Highlight the dataset
1633
+ this.HighlightDataset(dataset);
1634
+
1635
+ // Use the First datapoints coords for the Y position of the tooltip NOTE The X position is changed in the
1636
+ // obj.positionTooltip() method so set the index to be the first one
1637
+ var text = prop['chart.fill.tooltips'][dataset];
1638
+ var x = 0;
1639
+ var y = this.coords2[dataset][0][1] + RG.getCanvasXY(ca)[1];
1640
+
1641
+
1642
+ // Show a tooltip
1643
+ RG.Tooltip(this, text, x, y, 0, e);
1644
+ };
1645
+
1646
+
1647
+
1648
+
1649
+ /**
1650
+ * This function handles highlighting an entire data-series for the interactive
1651
+ * key
1652
+ *
1653
+ * @param int index The index of the data series to be highlighted
1654
+ */
1655
+ this.interactiveKeyHighlight = function (index)
1656
+ {
1657
+ var coords = this.coords2[index];
1658
+
1659
+ if (coords) {
1660
+
1661
+ var pre_linewidth = co.lineWidth;
1662
+ var pre_linecap = co.lineCap;
1663
+
1664
+
1665
+
1666
+
1667
+ // ------------------------------------------ //
1668
+
1669
+ co.lineWidth = prop['chart.linewidth'] + 10;
1670
+ co.lineCap = 'round';
1671
+ co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
1672
+
1673
+
1674
+ co.beginPath();
1675
+ for (var i=0,len=coords.length; i<len; i+=1) {
1676
+ if (i == 0) {
1677
+ co.moveTo(coords[i][0], coords[i][1]);
1678
+ } else {
1679
+ co.lineTo(coords[i][0], coords[i][1]);
1680
+ }
1681
+ }
1682
+ co.closePath();
1683
+ co.stroke();
1684
+
1685
+ // ------------------------------------------ //
1686
+
1687
+
1688
+
1689
+
1690
+ // Reset the lineCap and lineWidth
1691
+ co.lineWidth = pre_linewidth;
1692
+ co.lineCap = pre_linecap;
1693
+ }
1694
+ };
1695
+
1696
+
1697
+
1698
+
1699
+ /**
1700
+ * Using a function to add events makes it easier to facilitate method chaining
1701
+ *
1702
+ * @param string type The type of even to add
1703
+ * @param function func
1704
+ */
1705
+ this.on = function (type, func)
1706
+ {
1707
+ if (type.substr(0,2) !== 'on') {
1708
+ type = 'on' + type;
1709
+ }
1710
+
1711
+ if (typeof this[type] !== 'function') {
1712
+ this[type] = func;
1713
+ } else {
1714
+ RG.addCustomEventListener(this, type, func);
1715
+ }
1716
+
1717
+ return this;
1718
+ };
1719
+
1720
+
1721
+
1722
+
1723
+ /**
1724
+ * This function runs once only
1725
+ * (put at the end of the file (before any effects))
1726
+ */
1727
+ this.firstDrawFunc = function ()
1728
+ {
1729
+ };
1730
+
1731
+
1732
+
1733
+
1734
+ /**
1735
+ * Radar chart grow
1736
+ *
1737
+ * This effect gradually increases the magnitude of the points on the radar chart
1738
+ *
1739
+ * @param object Options for the effect
1740
+ * @param function An optional callback that is run when the effect is finished
1741
+ */
1742
+ this.grow = function ()
1743
+ {
1744
+ var obj = this;
1745
+ var callback = arguments[1] ? arguments[1] : function () {};
1746
+ var opt = arguments[0] ? arguments[0] : {};
1747
+ var frames = opt.frames ? opt.frames : 30;
1748
+ var frame = 0;
1749
+ var data = RG.array_clone(obj.data);
1750
+
1751
+ function iterator ()
1752
+ {
1753
+ for (var i=0,len=data.length; i<len; ++i) {
1754
+
1755
+ //if (obj.original_data[i] == null) {
1756
+ // obj.original_data[i] = [];
1757
+ //}
1758
+
1759
+ for (var j=0,len2=data[i].length; j<len2; ++j) {
1760
+ obj.original_data[i][j] = (frame / frames) * data[i][j];
1761
+ }
1762
+ }
1763
+
1764
+ RGraph.clear(obj.canvas);
1765
+ RGraph.redrawCanvas(obj.canvas);
1766
+
1767
+ if (frame < frames) {
1768
+ frame++;
1769
+ RGraph.Effects.updateCanvas(iterator);
1770
+ } else {
1771
+ callback(obj);
1772
+ }
1773
+ }
1774
+
1775
+ iterator();
1776
+
1777
+ return this;
1778
+ };
1779
+
1780
+
1781
+
1782
+
1783
+ /**
1784
+ * Trace (Radar chart)
1785
+ *
1786
+ * This is a Trace effect for the Radar chart
1787
+ *
1788
+ * @param object Options for the effect. Currently only "frames" is available.
1789
+ * @param function A function that is called when the ffect is complete
1790
+ */
1791
+ this.trace = function ()
1792
+ {
1793
+ var obj = this;
1794
+ var opt = arguments[0] || {};
1795
+ var frames = opt.frames || 60;
1796
+ var frame = 0;
1797
+ var callback = arguments[1] || function () {};
1798
+
1799
+ obj.Set('animation.trace.clip', 0);
1800
+
1801
+
1802
+ var iterator = function ()
1803
+ {
1804
+ if (frame < frames) {
1805
+
1806
+ obj.Set('animation.trace.clip', frame / frames);
1807
+
1808
+ frame++;
1809
+ RG.redrawCanvas(obj.canvas);
1810
+ RG.Effects.updateCanvas(iterator);
1811
+
1812
+ } else {
1813
+
1814
+ obj.Set('animation.trace.clip', 1);
1815
+ RG.redrawCanvas(obj.canvas);
1816
+ callback(obj);
1817
+ }
1818
+ };
1819
+
1820
+ iterator();
1821
+
1822
+ return this;
1823
+ };
1824
+
1825
+
1826
+
1827
+
1828
+ RG.att(ca);
1829
+
1830
+
1831
+
1832
+
1833
+ /**
1834
+ * Always register the object
1835
+ */
1836
+ RG.Register(this);
1837
+
1838
+
1839
+
1840
+
1841
+ /**
1842
+ * This is the 'end' of the constructor so if the first argument
1843
+ * contains configuration data - handle that.
1844
+ */
1845
+ if (parseConfObjectForOptions) {
1846
+ RG.parseObjectStyleConfig(this, conf.options);
1847
+ }
1848
+ };