rgraph-rails 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ };