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