rgraph-rails 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/license.txt +4 -16
  5. data/vendor/assets/javascripts/RGraph.bar.js +3734 -241
  6. data/vendor/assets/javascripts/RGraph.bipolar.js +2005 -115
  7. data/vendor/assets/javascripts/RGraph.common.annotate.js +395 -35
  8. data/vendor/assets/javascripts/RGraph.common.context.js +595 -30
  9. data/vendor/assets/javascripts/RGraph.common.core.js +5282 -405
  10. data/vendor/assets/javascripts/RGraph.common.csv.js +276 -19
  11. data/vendor/assets/javascripts/RGraph.common.deprecated.js +450 -35
  12. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1395 -86
  13. data/vendor/assets/javascripts/RGraph.common.effects.js +1545 -90
  14. data/vendor/assets/javascripts/RGraph.common.key.js +753 -54
  15. data/vendor/assets/javascripts/RGraph.common.resizing.js +563 -37
  16. data/vendor/assets/javascripts/RGraph.common.sheets.js +352 -29
  17. data/vendor/assets/javascripts/RGraph.common.tooltips.js +450 -32
  18. data/vendor/assets/javascripts/RGraph.common.zoom.js +219 -14
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +570 -35
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +544 -35
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +755 -52
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +645 -41
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +633 -37
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +514 -36
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +559 -39
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +548 -35
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +664 -36
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +812 -50
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +856 -51
  30. data/vendor/assets/javascripts/RGraph.fuel.js +964 -58
  31. data/vendor/assets/javascripts/RGraph.funnel.js +984 -55
  32. data/vendor/assets/javascripts/RGraph.gantt.js +1354 -77
  33. data/vendor/assets/javascripts/RGraph.gauge.js +1421 -87
  34. data/vendor/assets/javascripts/RGraph.hbar.js +2562 -146
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +1401 -80
  36. data/vendor/assets/javascripts/RGraph.line.js +4226 -244
  37. data/vendor/assets/javascripts/RGraph.meter.js +1280 -74
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +301 -19
  39. data/vendor/assets/javascripts/RGraph.odo.js +1264 -71
  40. data/vendor/assets/javascripts/RGraph.pie.js +2288 -137
  41. data/vendor/assets/javascripts/RGraph.radar.js +1847 -110
  42. data/vendor/assets/javascripts/RGraph.rose.js +1977 -108
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +1432 -80
  44. data/vendor/assets/javascripts/RGraph.scatter.js +3036 -168
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1120 -60
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +1067 -0
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +247 -0
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +3363 -0
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +277 -0
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1304 -0
  51. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +353 -0
  52. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +233 -0
  53. data/vendor/assets/javascripts/RGraph.svg.hbar.js +1141 -0
  54. data/vendor/assets/javascripts/RGraph.svg.line.js +1486 -0
  55. data/vendor/assets/javascripts/RGraph.svg.pie.js +781 -0
  56. data/vendor/assets/javascripts/RGraph.svg.radar.js +1326 -0
  57. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +817 -0
  58. data/vendor/assets/javascripts/RGraph.thermometer.js +1135 -62
  59. data/vendor/assets/javascripts/RGraph.vprogress.js +1470 -83
  60. data/vendor/assets/javascripts/RGraph.waterfall.js +1347 -80
  61. metadata +15 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b971479430e7a10bed7531bb4f508513757e8ea3
4
- data.tar.gz: 504eb2e14f1a3d4965d38e0f4e43d1be6abd08d7
3
+ metadata.gz: 9e47ecbb8906997092674ef40da68bf4368c58d8
4
+ data.tar.gz: 26702943c799c8e73c66da14ea8c932a96fa82ed
5
5
  SHA512:
6
- metadata.gz: 683a12369e6a5171d1a83d0f6eb4d91fb275fcb94650fc0f0e20d83c9dc9e0222e631767757f730aae4cccb8cbd7e975a47045ef0c801d79bb43a853bd4c7035
7
- data.tar.gz: 7292427cbd7e17259150de17aa0f3a14271a8e8a90f76a8c79bf289ca76e4bca77934eedeb80e786ec70edb653f7de0c77cadb968ba4df30ac6ed1c292d62646
6
+ metadata.gz: 07aa2605e50bbc47bf7d6ed1903ee0b81b99b3307f015ca218a2b5024586caba6c2ee55abe165776bbe73991982f1f84d4cd1b46ef230499d371a32e4f256e89
7
+ data.tar.gz: 875d758e1835599237b279edd026f2ad19706f19d18888f86b1c8a99cfde00a6066ec624c2d956765cf05f8998f7815311c2b5f59dd0eec80b7eb1c948e1ec47
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/dsgriffin/rgraph-rails.svg?branch=master)](https://travis-ci.org/dsgriffin/rgraph-rails)
4
4
  [![Gem Version](https://badge.fury.io/rb/rgraph-rails.svg)](https://badge.fury.io/rb/rgraph-rails)
5
- ![](https://img.shields.io/badge/rgraph-4.58-red.svg)
5
+ ![](https://img.shields.io/badge/rgraph-4.60-red.svg)
6
6
 
7
7
  Use the [rgraph](http://www.rgraph.net/) chart/graph library with the Rails asset pipeline.
8
8
 
@@ -11,7 +11,7 @@ Use the [rgraph](http://www.rgraph.net/) chart/graph library with the Rails asse
11
11
  Include the gem in your application's Gemfile:
12
12
 
13
13
  ```ruby
14
- gem 'rgraph-rails', '~> 1.0.7'
14
+ gem 'rgraph-rails', '~> 1.0.8'
15
15
  ```
16
16
 
17
17
  And then execute:
@@ -77,5 +77,5 @@ Bug reports and pull requests are welcome. This project is intended to be a safe
77
77
 
78
78
  ## License
79
79
 
80
- See `license.txt`
80
+ MIT
81
81
 
@@ -1,3 +1,3 @@
1
1
  module RgraphRails
2
- VERSION = "1.0.7"
2
+ VERSION = "1.0.8"
3
3
  end
data/license.txt CHANGED
@@ -1,19 +1,7 @@
1
+ Copyright (c) 2016 Daniel Griffin
1
2
 
2
- The RGraph license
3
- ==================
4
- RGraph is dual licensed under the Open Source GPL (General Public License) v2.0. If you want to use it under the
5
- terms of the GPL then you are free to do so. If you don't wish to be bound by the terms of the GPL then there is
6
- a commercial licensing scheme available which you can read about here:
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
4
 
8
- http://www.rgraph.net/license
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
6
 
10
- For commercial license there is a one-time fee of �99 that covers most use cases. The only exception is for OEM
11
- use - the OEM license is described on the website and costs �499. You can read the details about RGraph
12
- licensing here:
13
-
14
- http://www.rgraph.net/license
15
-
16
-
17
- Support
18
- =======
19
- Support is available using the support forum: http://www.rgraph.net/forum
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,242 +1,3735 @@
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.Bar=function(conf)
3
- {if(typeof conf==='object'&&typeof conf.data==='object'&&typeof conf.id==='string'){var id=conf.id,canvas=document.getElementById(id),data=conf.data,parseConfObjectForOptions=true}else{var id=conf,canvas=document.getElementById(id),data=arguments[1]}
4
- this.id=id;this.canvas=canvas;this.context=this.canvas.getContext('2d');this.canvas.__object__=this;this.type='bar';this.max=0;this.stackedOrGrouped=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.cachedBackgroundCanvas=null;this.firstDraw=true;this.properties={'chart.background.barcolor1':'rgba(0,0,0,0)','chart.background.barcolor2':'rgba(0,0,0,0)','chart.background.grid':true,'chart.background.grid.color':'#ddd','chart.background.grid.width':1,'chart.background.grid.hsize':20,'chart.background.grid.vsize':20,'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':20,'chart.background.grid.dashed':false,'chart.background.grid.dotted':false,'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.background.hbars':null,'chart.numyticks':10,'chart.hmargin':5,'chart.hmargin.grouped':1,'chart.strokecolor':'rgba(0,0,0,0)','chart.axis.color':'black','chart.axis.linewidth':1,'chart.gutter.top':25,'chart.gutter.bottom':30,'chart.gutter.left':25,'chart.gutter.right':25,'chart.labels':null,'chart.labels.bold':false,'chart.labels.color':null,'chart.labels.ingraph':null,'chart.labels.above':false,'chart.labels.above.decimals':0,'chart.labels.above.size':null,'chart.labels.above.color':null,'chart.labels.above.background':'rgba(0,0,0,0)','chart.labels.above.angle':null,'chart.labels.above.offset':4,'chart.labels.above.units.pre':'','chart.labels.above.units.post':'','chart.ylabels':true,'chart.ylabels.count':5,'chart.ylabels.inside':false,'chart.ylabels.offsetx':0,'chart.ylabels.offsety':0,'chart.labels.offsetx':0,'chart.labels.offsety':0,'chart.xaxispos':'bottom','chart.yaxispos':'left','chart.text.angle':0,'chart.text.color':'black','chart.text.size':12,'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.font':null,'chart.title.background':null,'chart.title.hpos':null,'chart.title.vpos':null,'chart.title.bold':true,'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.colors':['red','#0f0','blue','pink','orange','cyan','black','white','green','magenta'],'chart.colors.sequential':false,'chart.colors.reverse':false,'chart.grouping':'grouped','chart.variant':'bar','chart.variant.sketch.verticals':true,'chart.variant.threed.xaxis':true,'chart.variant.threed.yaxis':true,'chart.variant.threed.angle':0.1,'chart.variant.threed.offsetx':10,'chart.variant.threed.offsety':5,'chart.shadow':false,'chart.shadow.color':'#aaa','chart.shadow.offsetx':0,'chart.shadow.offsety':0,'chart.shadow.blur':15,'chart.tooltips':null,'chart.tooltips.effect':'fade','chart.tooltips.css.class':'RGraph_tooltip','chart.tooltips.event':'onclick','chart.tooltips.highlight':true,'chart.tooltips.hotspot.xonly':false,'chart.highlight.stroke':'rgba(0,0,0,0)','chart.highlight.fill':'rgba(255,255,255,0.7)','chart.key':null,'chart.key.background':'white','chart.key.position':'graph','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.interactive':false,'chart.key.interactive.highlight.chart.stroke':'black','chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)','chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)','chart.key.halign':'right','chart.key.color.shape':'square','chart.key.rounded':true,'chart.key.text.size':10,'chart.key.linewidth':1,'chart.key.colors':null,'chart.key.text.color':'black','chart.contextmenu':null,'chart.units.pre':'','chart.units.post':'','chart.scale.decimals':0,'chart.scale.point':'.','chart.scale.thousand':',','chart.scale.round':false,'chart.scale.zerostart':true,'chart.crosshairs':false,'chart.crosshairs.color':'#333','chart.crosshairs.hline':true,'chart.crosshairs.vline':true,'chart.linewidth':1,'chart.annotatable':false,'chart.annotate.color':'black','chart.zoom.factor':1.5,'chart.zoom.fade.in':true,'chart.zoom.fade.out':true,'chart.zoom.hdir':'right','chart.zoom.vdir':'down','chart.zoom.frames':25,'chart.zoom.delay':16.666,'chart.zoom.shadow':true,'chart.zoom.background':true,'chart.resizable':false,'chart.resize.handle.background':null,'chart.adjustable':false,'chart.adjustable.only':null,'chart.noaxes':false,'chart.noxaxis':false,'chart.noyaxis':false,'chart.events.click':null,'chart.events.mousemove':null,'chart.numxticks':null,'chart.bevel':false,'chart.errorbars':false,'chart.errorbars.color':'black','chart.errorbars.capped':true,'chart.errorbars.capped.width':14,'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
- if(!this.canvas){alert('[BAR] No canvas support');return;}
6
- for(var i=0;i<data.length;++i){if(typeof data[i]==='string'){data[i]=parseFloat(data[i]);}else if(typeof data[i]==='object'&&data[i]){for(var j=0;j<data[i].length;++j){if(typeof data[i][j]==='string'){data[i][j]=parseFloat(data[i][j]);}}}else if(typeof data[i]==='undefined'){data[i]=null;}}
7
- for(var i=0;i<data.length;++i){if(typeof data[i]==='object'&&!RGraph.is_null(data[i])){this.stackedOrGrouped=true;}}
8
- var linear_data=RGraph.arrayLinearize(data);for(var i=0;i<linear_data.length;++i){this['$'+i]={};}
9
- this.data=data;this.original_data=RGraph.arrayClone(data);this.coords=[];this.coords2=[];this.coordsText=[];this.data_arr=RGraph.arrayLinearize(this.data);if(!this.canvas.__rgraph_aa_translated__){this.context.translate(0.5,0.5);this.canvas.__rgraph_aa_translated__=true;}
10
- var RG=RGraph,ca=this.canvas,co=ca.getContext('2d'),prop=this.properties,pa2=RG.path2,win=window,doc=document,ma=Math
11
- if(RG.Effects&&typeof RG.Effects.decorate==='function'){RG.Effects.decorate(this);}
12
- this.set=this.Set=function(name)
13
- {var value=typeof arguments[1]==='undefined'?null:arguments[1];if(arguments.length===1&&typeof arguments[0]==='object'){RG.parseObjectStyleConfig(this,arguments[0]);return this;}
14
- if(name.substr(0,6)!='chart.'){name='chart.'+name;}
15
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
16
- if(name==='chart.xlabels.offset'){name='chart.labels.offsety';}
17
- if(name=='chart.labels.abovebar'){name='chart.labels.above';}
18
- if(name=='chart.strokestyle'){name='chart.strokecolor';}
19
- if(name=='chart.xaxispos'){if(value!='bottom'&&value!='center'&&value!='top'){alert('[BAR] ('+this.id+') chart.xaxispos should be top, center or bottom. Tried to set it to: '+value+' Changing it to center');value='center';}
20
- if(value=='top'){for(var i=0;i<this.data.length;++i){if(typeof(this.data[i])=='number'&&this.data[i]>0){alert('[BAR] The data element with index '+i+' should be negative');}}}}
21
- if(name.toLowerCase()=='chart.linewidth'&&value==0){value=0.0001;}
22
- prop[name]=value;return this;};this.get=this.Get=function(name)
23
- {if(name.substr(0,6)!='chart.'){name='chart.'+name;}
24
- while(name.match(/([A-Z])/)){name=name.replace(/([A-Z])/,'.'+RegExp.$1.toLowerCase());}
25
- return prop[name];};this.draw=this.Draw=function()
26
- {if(typeof(prop['chart.background.image'])=='string'){RG.DrawBackgroundImage(this);}
27
- RG.FireCustomEvent(this,'onbeforedraw');if(prop['chart.variant']==='3d'){if(prop['chart.text.accessible']){}else{co.setTransform(1,prop['chart.variant.threed.angle'],0,1,0.5,0.5);}}
28
- if(!this.colorsParsed){this.parseColors();this.colorsParsed=true;}
29
- this.gutterLeft=prop['chart.gutter.left'];this.gutterRight=prop['chart.gutter.right'];this.gutterTop=prop['chart.gutter.top'];this.gutterBottom=prop['chart.gutter.bottom'];if((prop['chart.variant']=='pyramid'||prop['chart.variant']=='dot')&&typeof(prop['chart.tooltips'])=='object'&&prop['chart.tooltips']&&prop['chart.tooltips'].length>0){alert('[BAR] ('+this.id+') Sorry, tooltips are not supported with dot or pyramid charts');}
30
- this.coords=[];this.coords2=[];this.coordsText=[];this.max=0;this.grapharea=ca.height-this.gutterTop-this.gutterBottom;this.halfgrapharea=this.grapharea/2;this.halfTextHeight=prop['chart.text.size']/2;RG.background.Draw(this);this.drawbars();this.drawAxes();this.DrawLabels();if(prop['chart.bevel']||prop['chart.bevelled']){this.DrawBevel();}
31
- if(prop['chart.key']&&prop['chart.key'].length){RG.DrawKey(this,prop['chart.key'],prop['chart.colors']);}
32
- if(prop['chart.contextmenu']){RG.ShowContext(this);}
33
- if(prop['chart.errorbars']){this.drawErrorbars();}
34
- if(prop['chart.labels.ingraph']){RG.DrawInGraphLabels(this);}
35
- if(prop['chart.resizable']){RG.AllowResizing(this);}
36
- RG.InstallEventListeners(this);if(this.firstDraw){RG.fireCustomEvent(this,'onfirstdraw');this.firstDraw=false;this.firstDrawFunc();}
37
- RG.fireCustomEvent(this,'ondraw');return this;};this.exec=function(func)
38
- {func(this);return this;};this.drawAxes=this.DrawAxes=function()
39
- {if(prop['chart.noaxes']){return;}
40
- var xaxispos=prop['chart.xaxispos'];var yaxispos=prop['chart.yaxispos'];var isSketch=prop['chart.variant']=='sketch';co.beginPath();co.strokeStyle=prop['chart.axis.color'];co.lineWidth=prop['chart.axis.linewidth']+0.001;if(RG.ISSAFARI==-1){co.lineCap='square';}
41
- if(prop['chart.noyaxis']==false){if(yaxispos=='right'){co.moveTo(ca.width-this.gutterRight+(isSketch?3:0),this.gutterTop-(isSketch?3:0));co.lineTo(ca.width-this.gutterRight-(isSketch?2:0),ca.height-this.gutterBottom+(isSketch?5:0));}else{co.moveTo(this.gutterLeft-(isSketch?2:0),this.gutterTop-(isSketch?5:0));co.lineTo(this.gutterLeft-(isSketch?1:0),ca.height-this.gutterBottom+(isSketch?5:0));}}
42
- if(prop['chart.noxaxis']==false){if(xaxispos=='center'){co.moveTo(this.gutterLeft-(isSketch?5:0),Math.round(((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop+(isSketch?2:0)));co.lineTo(ca.width-this.gutterRight+(isSketch?5:0),Math.round(((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop-(isSketch?2:0)));}else if(xaxispos=='top'){co.moveTo(this.gutterLeft-(isSketch?3:0),this.gutterTop-(isSketch?3:0));co.lineTo(ca.width-this.gutterRight+(isSketch?5:0),this.gutterTop+(isSketch?2:0));}else{co.moveTo(this.gutterLeft-(isSketch?5:0),ma.round(this.getYCoord(0)-(isSketch?2:0)));co.lineTo(ca.width-this.gutterRight+(isSketch?8:0),ma.round(this.getYCoord(0)+(isSketch?2:0)));}}
43
- var numYTicks=prop['chart.numyticks'];if(prop['chart.noyaxis']==false&&!isSketch){var yTickGap=(ca.height-this.gutterTop-this.gutterBottom)/numYTicks;var xpos=yaxispos=='left'?this.gutterLeft:ca.width-this.gutterRight;if(this.properties['chart.numyticks']>0){for(y=this.gutterTop;xaxispos=='center'?y<=(ca.height-this.gutterBottom):y<(ca.height-this.gutterBottom+(xaxispos=='top'?1:0));y+=yTickGap){if(xaxispos=='center'&&y==(this.gutterTop+(this.grapharea/2))){continue;}
44
- if(xaxispos=='top'&&y==this.gutterTop){continue;}
45
- co.moveTo(xpos+(yaxispos=='left'?0:0),ma.round(y));co.lineTo(xpos+(yaxispos=='left'?-3:3),ma.round(y));}
46
- if(xaxispos==='bottom'&&prop['chart.ymin']!==0){co.moveTo(xpos+(yaxispos=='left'?0:0),ma.round(ca.height-prop['chart.gutter.bottom']));co.lineTo(xpos+(yaxispos=='left'?-3:3),ma.round(ca.height-prop['chart.gutter.bottom']));}}
47
- if(prop['chart.noxaxis']){if(xaxispos=='center'){co.moveTo(xpos+(yaxispos=='left'?-3:3),Math.round(ca.height/2));co.lineTo(xpos,Math.round(ca.height/2));}else if(xaxispos=='top'){co.moveTo(xpos+(yaxispos=='left'?-3:3),Math.round(this.gutterTop));co.lineTo(xpos,Math.round(this.gutterTop));}else{co.moveTo(xpos+(yaxispos=='left'?-3:3),Math.round(ca.height-this.gutterBottom));co.lineTo(xpos,Math.round(ca.height-this.gutterBottom));}}}
48
- if(prop['chart.noxaxis']==false&&!isSketch){if(typeof(prop['chart.numxticks'])=='number'){var xTickGap=(ca.width-this.gutterLeft-this.gutterRight)/prop['chart.numxticks'];}else{var xTickGap=(ca.width-this.gutterLeft-this.gutterRight)/this.data.length;}
49
- if(xaxispos=='bottom'){yStart=prop['chart.ymin']<0?this.getYCoord(0)-3:this.getYCoord(0);yEnd=this.getYCoord(0)+3;}else if(xaxispos=='top'){yStart=this.gutterTop-3;yEnd=this.gutterTop;}else if(xaxispos=='center'){yStart=((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop+3;yEnd=((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop-3;}
50
- var noEndXTick=prop['chart.noendxtick'];for(x=this.gutterLeft+(yaxispos=='left'?xTickGap:0),len=(ca.width-this.gutterRight+(yaxispos=='left'?5:0));x<len;x+=xTickGap){if(yaxispos=='left'&&!noEndXTick&&x>this.gutterLeft){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}else if(yaxispos=='left'&&noEndXTick&&x>this.gutterLeft&&x<(ca.width-this.gutterRight)){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}else if(yaxispos=='right'&&x<(ca.width-this.gutterRight)&&!noEndXTick){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}else if(yaxispos=='right'&&x<(ca.width-this.gutterRight)&&x>(this.gutterLeft)&&noEndXTick){co.moveTo(ma.round(x),yStart);co.lineTo(ma.round(x),yEnd);}}
51
- if(prop['chart.noyaxis']||prop['chart.numxticks']==null){if(typeof(prop['chart.numxticks'])=='number'&&prop['chart.numxticks']>0){co.moveTo(Math.round(this.gutterLeft),yStart);co.lineTo(Math.round(this.gutterLeft),yEnd);}}}
52
- if(prop['chart.noyaxis']&&prop['chart.noxaxis']==false&&prop['chart.numxticks']==null){if(xaxispos=='center'){co.moveTo(ma.round(this.gutterLeft),(ca.height/2)-3);co.lineTo(ma.round(this.gutterLeft),(ca.height/2)+3);}else{co.moveTo(ma.round(this.gutterLeft),ca.height-this.gutterBottom);co.lineTo(ma.round(this.gutterLeft),ca.height-this.gutterBottom+3);}}
53
- co.stroke();};this.drawbars=this.Drawbars=function()
54
- {co.lineWidth=prop['chart.linewidth'];co.strokeStyle=prop['chart.strokecolor'];co.fillStyle=prop['chart.colors'][0];var prevX=0,prevY=0,decimals=prop['chart.scale.decimals'];if(prop['chart.ymax']){this.scale2=RG.getScale2(this,{'max':prop['chart.ymax'],'strict':prop['chart.scale.round']?false:true,'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']});}else{var errorbars=prop['chart.errorbars'];if(typeof errorbars==='number'){var value=errorbars;prop['chart.errorbars']=[];for(var i=0;i<this.data.length;++i){if(typeof this.data[i]==='number'){prop['chart.errorbars'].push([value,null]);}else if(typeof this.data[i]==='object'&&!RG.isNull(this.data[i])){for(var j=0;j<this.data[i].length;++j){prop['chart.errorbars'].push([value,null]);}}}
55
- errorbars=prop['chart.errorbars'];}
56
- for(i=0;i<this.data.length;++i){if(typeof(this.data[i])=='object'){var value=prop['chart.grouping']==='grouped'?Number(RG.arrayMax(this.data[i],true)):Number(RG.array_sum(this.data[i]));}else{var value=Number(this.data[i]);}
57
- this.max=ma.max(ma.abs(this.max),ma.abs(value)+
58
- Number((typeof prop['chart.errorbars']==='object'&&typeof prop['chart.errorbars'][i]==='object'&&!RG.isNull(prop['chart.errorbars'][i])&&typeof prop['chart.errorbars'][i][0]==='number')?prop['chart.errorbars'][i][0]:0));}
59
- this.scale2=RGraph.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;}
60
- if(prop['chart.adjustable']&&!prop['chart.ymax']){this.Set('chart.ymax',this.scale2.max);}
61
- if(prop['chart.background.hbars']&&prop['chart.background.hbars'].length>0){RGraph.DrawBars(this);}
62
- var variant=prop['chart.variant'];if(variant==='3d'){RG.draw3DAxes(this);}
63
- var xaxispos=prop['chart.xaxispos'],width=(ca.width-this.gutterLeft-this.gutterRight)/this.data.length,orig_height=height,hmargin=prop['chart.hmargin'],shadow=prop['chart.shadow'],shadowColor=prop['chart.shadow.color'],shadowBlur=prop['chart.shadow.blur'],shadowOffsetX=prop['chart.shadow.offsetx'],shadowOffsetY=prop['chart.shadow.offsety'],strokeStyle=prop['chart.strokecolor'],colors=prop['chart.colors'],sequentialColorIndex=0
64
- var height;for(i=0,len=this.data.length;i<len;i+=1){if(RG.arraySum(this.data[i])<0){var height=(RG.arraySum(this.data[i])+this.scale2.min)/(this.scale2.max-this.scale2.min);}else{var height=(RG.arraySum(this.data[i])-this.scale2.min)/(this.scale2.max-this.scale2.min);}
65
- height*=ma.abs(this.getYCoord(this.scale2.max)-this.getYCoord(this.scale2.min));var x=(i*width)+this.gutterLeft;var y=xaxispos=='center'?((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop-height:ca.height-height-this.gutterBottom;if(xaxispos=='top'){y=this.gutterTop+ma.abs(height);}
66
- if(height<0){y+=height;height=ma.abs(height);}
67
- if(shadow){co.shadowColor=shadowColor;co.shadowBlur=shadowBlur;co.shadowOffsetX=shadowOffsetX;co.shadowOffsetY=shadowOffsetY;}
68
- co.beginPath();if(typeof this.data[i]=='number'){if(xaxispos==='bottom'&&prop['chart.ymin']<0){if(this.data[i]>=0){height=ma.abs(this.getYCoord(0)-this.getYCoord(this.data[i]));}else{y=this.getYCoord(0);height=ma.abs(this.getYCoord(0)-this.getYCoord(this.data[i]));}}
69
- var barWidth=width-(2*hmargin);if(barWidth<0){alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');}
70
- co.strokeStyle=strokeStyle;co.fillStyle=colors[0];if(prop['chart.colors.sequential']){co.fillStyle=colors[i];}
71
- if(variant=='sketch'){co.lineCap='round';var sketchOffset=3;co.beginPath();co.strokeStyle=colors[0];if(prop['chart.colors.sequential']){co.strokeStyle=colors[i];}
72
- co.moveTo(x+hmargin+2,y+height-2);co.lineTo(x+hmargin-1,y-4);co.moveTo(x+hmargin-3,y+ -2+(this.data[i]<0?height:0));co.bezierCurveTo(x+((hmargin+width)*0.33),y+15+(this.data[i]<0?height-10:0),x+((hmargin+width)*0.66),y+5+(this.data[i]<0?height-10:0),x+hmargin+width+ -1,y+0+(this.data[i]<0?height:0));co.moveTo(x+hmargin+width-5,y-5);co.lineTo(x+hmargin+width-3,y+height-3);if(prop['chart.variant.sketch.verticals']){for(var r=0.2;r<=0.8;r+=0.2){co.moveTo(x+hmargin+width+(r>0.4?-1:3)-(r*width),y-1);co.lineTo(x+hmargin+width-(r>0.4?1:-1)-(r*width),y+height+(r==0.2?1:-2));}}
73
- co.stroke();}else if(variant=='bar'||variant=='3d'||variant=='glass'||variant=='bevel'){if(RGraph.ISOLD&&shadow){this.DrawIEShadow([x+hmargin,y,barWidth,height]);}
74
- if(variant=='glass'){RGraph.filledCurvyRect(co,x+hmargin,y,barWidth,height,3,this.data[i]>0,this.data[i]>0,this.data[i]<0,this.data[i]<0);RGraph.strokedCurvyRect(co,x+hmargin,y,barWidth,height,3,this.data[i]>0,this.data[i]>0,this.data[i]<0,this.data[i]<0);}else{co.beginPath();co.rect(x+hmargin,y,barWidth,height);co.fill();RG.NoShadow(this);co.beginPath();co.rect(x+hmargin,y,barWidth,height);co.stroke();}
75
- if(variant=='3d'){var prevStrokeStyle=co.strokeStyle;var prevFillStyle=co.fillStyle;if(this.data[i]>=0){co.beginPath();co.moveTo(x+hmargin,y);co.lineTo(x+hmargin+prop['chart.variant.threed.offsetx'],y-prop['chart.variant.threed.offsety']);co.lineTo(x+hmargin+prop['chart.variant.threed.offsetx']+barWidth,y-prop['chart.variant.threed.offsety']);co.lineTo(x+hmargin+barWidth,y);co.closePath();co.stroke();co.fill();}
76
- co.beginPath();co.moveTo(x+hmargin+barWidth,y);co.lineTo(x+hmargin+barWidth+prop['chart.variant.threed.offsetx'],this.data[i]<0&&xaxispos==='bottom'?this.getYCoord(0):(this.data[i]<0&&(y-prop['chart.variant.threed.offsety'])<(this.gutterTop+this.halfgrapharea)?(this.gutterTop+this.halfgrapharea):(y-prop['chart.variant.threed.offsety'])));co.lineTo(x+hmargin+barWidth+prop['chart.variant.threed.offsetx'],this.data[i]<0&&(y-prop['chart.variant.threed.offsety']+height)<(this.gutterTop+this.getYCoord(0))?this.getYCoord(this.data[i])-prop['chart.variant.threed.offsety']:(this.data[i]>0?y-prop['chart.variant.threed.offsety']+height:ma.min(y-prop['chart.variant.threed.offsety']+height,ca.height-this.gutterBottom)));co.lineTo(x+hmargin+barWidth,y+height);co.closePath();co.stroke();co.fill();if(this.data[i]>0){co.beginPath();co.fillStyle='rgba(255,255,255,0.5)';co.moveTo(x+hmargin,y);co.lineTo(x+hmargin+prop['chart.variant.threed.offsetx'],y-prop['chart.variant.threed.offsety']);co.lineTo(x+hmargin+prop['chart.variant.threed.offsetx']+barWidth,y-prop['chart.variant.threed.offsety']);co.lineTo(x+hmargin+barWidth,y);co.lineTo(x+hmargin,y);co.closePath();co.stroke();co.fill();}
77
- co.beginPath();co.fillStyle='rgba(0,0,0,0.4)';co.moveTo(x+hmargin+barWidth,y);co.lineTo(x+hmargin+barWidth+prop['chart.variant.threed.offsetx'],this.data[i]<0&&xaxispos==='bottom'?this.getYCoord(0):(this.data[i]<0&&(y-prop['chart.variant.threed.offsety'])<(this.gutterTop+this.halfgrapharea)?(this.gutterTop+this.halfgrapharea):y-prop['chart.variant.threed.offsety']));co.lineTo(x+hmargin+barWidth+prop['chart.variant.threed.offsetx'],this.data[i]<0&&(y-prop['chart.variant.threed.offsety']+height)<this.getYCoord(0)?this.getYCoord(0):this.data[i]>0?y-prop['chart.variant.threed.offsety']+height:ma.min(y-prop['chart.variant.threed.offsety']+height,ca.height-this.gutterBottom));co.lineTo(x+hmargin+barWidth,y+height);co.lineTo(x+hmargin+barWidth,y);co.closePath();co.stroke();co.fill();co.strokeStyle=prevStrokeStyle;co.fillStyle=prevFillStyle;}else if(variant=='glass'){var grad=co.createLinearGradient(x+hmargin,y,x+hmargin+(barWidth/2),y);grad.addColorStop(0,'rgba(255,255,255,0.9)');grad.addColorStop(1,'rgba(255,255,255,0.5)');co.beginPath();co.fillStyle=grad;co.fillRect(x+hmargin+2,y+(this.data[i]>0?2:0),(barWidth/2)-2,height-2);co.fill();}}else if(variant=='dot'){co.beginPath();co.moveTo(x+(width/2),y);co.lineTo(x+(width/2),y+height);co.stroke();co.beginPath();co.fillStyle=this.properties['chart.colors'][i];co.arc(x+(width/2),y+(this.data[i]>0?0:height),2,0,6.28,0);co.fillStyle=prop['chart.colors'][0];if(prop['chart.colors.sequential']){co.fillStyle=colors[i];}
78
- co.stroke();co.fill();}else{alert('[BAR] Warning! Unknown chart.variant: '+variant);}
79
- this.coords.push([x+hmargin,y,width-(2*hmargin),height]);if(typeof this.coords2[i]=='undefined'){this.coords2[i]=[];}
80
- this.coords2[i].push([x+hmargin,y,width-(2*hmargin),height]);}else if(this.data[i]&&typeof(this.data[i])=='object'&&prop['chart.grouping']=='stacked'){if(this.scale2.min){alert("[ERROR] Stacked Bar charts with a Y min are not supported");}
81
- var barWidth=width-(2*hmargin);var redrawCoords=[];var startY=0;var dataset=this.data[i];if(barWidth<0){alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');}
82
- for(j=0;j<dataset.length;++j){if(xaxispos=='center'){alert("[BAR] It's pointless having the X axis position at the center on a stacked bar chart.");return;}
83
- if(this.data[i][j]<0){alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');return;}
84
- co.strokeStyle=strokeStyle
85
- co.fillStyle=colors[j];if(prop['chart.colors.reverse']){co.fillStyle=colors[this.data[i].length-j-1];}
86
- if(prop['chart.colors.sequential']&&colors[sequentialColorIndex]){co.fillStyle=colors[sequentialColorIndex++];}else if(prop['chart.colors.sequential']){co.fillStyle=colors[sequentialColorIndex-1];}
87
- var height=(dataset[j]/this.scale2.max)*(ca.height-this.gutterTop-this.gutterBottom);if(xaxispos=='center'){height/=2;}
88
- var totalHeight=(RGraph.array_sum(dataset)/this.scale2.max)*(ca.height-hmargin-this.gutterTop-this.gutterBottom);this.coords.push([x+hmargin,y,width-(2*hmargin),height]);if(typeof this.coords2[i]=='undefined'){this.coords2[i]=[];}
89
- this.coords2[i].push([x+hmargin,y,width-(2*hmargin),height]);if(RGraph.ISOLD&&shadow){this.DrawIEShadow([x+hmargin,y,width-(2*hmargin),height+1]);}
90
- if(height>0){co.strokeRect(x+hmargin,y,width-(2*hmargin),height);co.fillRect(x+hmargin,y,width-(2*hmargin),height);}
91
- if(j==0){var startY=y;var startX=x;}
92
- if(shadow){redrawCoords.push([x+hmargin,y,width-(2*hmargin),height,co.fillStyle]);}
93
- if(variant=='3d'){var prevFillStyle=co.fillStyle;var prevStrokeStyle=co.strokeStyle;if(j==0){co.beginPath();co.moveTo(startX+hmargin,y);co.lineTo(startX+prop['chart.variant.threed.offsetx']+hmargin,y-prop['chart.variant.threed.offsety']);co.lineTo(startX+prop['chart.variant.threed.offsetx']+barWidth+hmargin,y-prop['chart.variant.threed.offsety']);co.lineTo(startX+barWidth+hmargin,y);co.closePath();co.fill();co.stroke();}
94
- co.beginPath();co.moveTo(startX+barWidth+hmargin,y);co.lineTo(startX+barWidth+hmargin+prop['chart.variant.threed.offsetx'],y-prop['chart.variant.threed.offsety']);co.lineTo(startX+barWidth+hmargin+prop['chart.variant.threed.offsetx'],y-prop['chart.variant.threed.offsety']+height);co.lineTo(startX+barWidth+hmargin,y+height);co.closePath();co.fill();co.stroke();if(j==0){co.fillStyle='rgba(255,255,255,0.5)';co.beginPath();co.moveTo(startX+hmargin,y);co.lineTo(startX+prop['chart.variant.threed.offsetx']+hmargin,y-prop['chart.variant.threed.offsety']);co.lineTo(startX+prop['chart.variant.threed.offsetx']+barWidth+hmargin,y-prop['chart.variant.threed.offsety']);co.lineTo(startX+barWidth+hmargin,y);co.closePath();co.fill();co.stroke();}
95
- co.fillStyle='rgba(0,0,0,0.4)';co.beginPath();co.moveTo(startX+barWidth+hmargin,y);co.lineTo(startX+barWidth+hmargin+prop['chart.variant.threed.offsetx'],y-prop['chart.variant.threed.offsety']);co.lineTo(startX+barWidth+hmargin+prop['chart.variant.threed.offsetx'],y-prop['chart.variant.threed.offsety']+height);co.lineTo(startX+barWidth+hmargin,y+height);co.closePath();co.fill();co.stroke();co.strokeStyle=prevStrokeStyle;co.fillStyle=prevFillStyle;}
96
- y+=height;}
97
- if(shadow){RGraph.NoShadow(this);for(k=0;k<redrawCoords.length;++k){co.strokeStyle=strokeStyle;co.fillStyle=redrawCoords[k][4];co.strokeRect(redrawCoords[k][0],redrawCoords[k][1],redrawCoords[k][2],redrawCoords[k][3]);co.fillRect(redrawCoords[k][0],redrawCoords[k][1],redrawCoords[k][2],redrawCoords[k][3]);co.stroke();co.fill();}
98
- redrawCoords=[];}}else if(this.data[i]&&typeof(this.data[i])=='object'&&prop['chart.grouping']=='grouped'){var redrawCoords=[];co.lineWidth=prop['chart.linewidth'];for(j=0;j<this.data[i].length;++j){co.strokeStyle=strokeStyle;co.fillStyle=colors[j];if(prop['chart.colors.sequential']&&colors[sequentialColorIndex]){co.fillStyle=colors[sequentialColorIndex++];}else if(prop['chart.colors.sequential']){co.fillStyle=colors[sequentialColorIndex-1];}
99
- var individualBarWidth=(width-(2*hmargin))/this.data[i].length;var height=((this.data[i][j]+(this.data[i][j]<0?this.scale2.min:(-1*this.scale2.min)))/(this.scale2.max-this.scale2.min))*(ca.height-this.gutterTop-this.gutterBottom);var groupedMargin=prop['chart.hmargin.grouped'];var startX=x+hmargin+(j*individualBarWidth);if(individualBarWidth<0){alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');}
100
- if(xaxispos=='center'){height/=2;}
101
- if(xaxispos=='top'){var startY=this.gutterTop;var height=Math.abs(height);}else if(xaxispos=='center'){var startY=this.gutterTop+(this.grapharea/2)-height;}else{var startY=this.getYCoord(0);var height=ma.abs(ma.abs(this.getYCoord(this.data[i][j]))-this.getYCoord(0));if(this.data[i][j]>=0){startY-=height;}}
102
- co.strokeRect(startX+groupedMargin,startY,individualBarWidth-(2*groupedMargin),height);co.fillRect(startX+groupedMargin,startY,individualBarWidth-(2*groupedMargin),height);y+=height;if(variant=='3d'){var prevFillStyle=co.fillStyle;var prevStrokeStyle=co.strokeStyle;var hmarginGrouped=prop['chart.hmargin.grouped'];if(this.data[i][j]>=0){co.beginPath();co.moveTo(startX+hmarginGrouped,startY);co.lineTo(startX+hmarginGrouped+prop['chart.variant.threed.offsetx'],startY-prop['chart.variant.threed.offsety']);co.lineTo(startX+prop['chart.variant.threed.offsetx']+individualBarWidth-hmarginGrouped,startY-prop['chart.variant.threed.offsety']);co.lineTo(startX+individualBarWidth-hmarginGrouped,startY);co.closePath();co.fill();co.stroke();}
103
- co.beginPath();co.moveTo(startX+individualBarWidth-hmarginGrouped-1,startY);co.lineTo(startX+individualBarWidth-hmarginGrouped+prop['chart.variant.threed.offsetx'],this.data[i][j]<0?(this.getYCoord(0)+ma.abs(height)-prop['chart.variant.threed.offsety']):this.getYCoord(0)-height-prop['chart.variant.threed.offsety']);co.lineTo(startX+individualBarWidth-hmarginGrouped+prop['chart.variant.threed.offsetx'],this.data[i][j]<0&&(startY+height-prop['chart.variant.threed.offsety'])<(this.gutterTop+this.halfgrapharea)?(this.gutterTop+this.halfgrapharea):(startY+height-prop['chart.variant.threed.offsety']));co.lineTo(startX+individualBarWidth-hmarginGrouped-1,startY+height);co.closePath();co.fill();co.stroke();if(this.data[i][j]>=0){co.fillStyle='rgba(255,255,255,0.5)';co.beginPath();co.moveTo(startX+hmarginGrouped,startY);co.lineTo(startX+hmarginGrouped+prop['chart.variant.threed.offsetx'],startY-prop['chart.variant.threed.offsety']);co.lineTo(startX+prop['chart.variant.threed.offsetx']+individualBarWidth-hmarginGrouped,startY-prop['chart.variant.threed.offsety']);co.lineTo(startX+individualBarWidth-hmarginGrouped,startY);co.closePath();co.fill();co.stroke();}
104
- co.fillStyle='rgba(0,0,0,0.4)';co.beginPath();co.moveTo(startX+individualBarWidth-hmarginGrouped,startY);co.lineTo(startX+individualBarWidth+prop['chart.variant.threed.offsetx']-hmarginGrouped,this.data[i][j]<0?(this.getYCoord(0)+ma.abs(height)-prop['chart.variant.threed.offsety']):this.getYCoord(0)-height-prop['chart.variant.threed.offsety']);co.lineTo(startX+individualBarWidth+prop['chart.variant.threed.offsetx']-hmarginGrouped,this.data[i][j]<0&&(startY+height-5)<(this.gutterTop+this.halfgrapharea)?(this.gutterTop+this.halfgrapharea):(startY+height-prop['chart.variant.threed.offsety']));co.lineTo(startX+individualBarWidth-hmarginGrouped,startY+height);co.closePath();co.fill();co.stroke();co.strokeStyle=prevStrokeStyle;co.fillStyle=prevFillStyle;}
105
- if(height<0){height=Math.abs(height);startY=startY-height;}
106
- this.coords.push([startX+groupedMargin,startY,individualBarWidth-(2*groupedMargin),height]);if(typeof this.coords2[i]=='undefined'){this.coords2[i]=[];}
107
- this.coords2[i].push([startX+groupedMargin,startY,individualBarWidth-(2*groupedMargin),height]);if(prop['chart.shadow']){redrawCoords.push([startX+groupedMargin,startY,individualBarWidth-(2*groupedMargin),height,co.fillStyle]);}}
108
- if(redrawCoords.length){RGraph.NoShadow(this);co.lineWidth=prop['chart.linewidth'];co.beginPath();for(var j=0;j<redrawCoords.length;++j){co.fillStyle=redrawCoords[j][4];co.strokeStyle=prop['chart.strokecolor'];co.fillRect(redrawCoords[j][0],redrawCoords[j][1],redrawCoords[j][2],redrawCoords[j][3]);co.strokeRect(redrawCoords[j][0],redrawCoords[j][1],redrawCoords[j][2],redrawCoords[j][3]);}
109
- co.fill();co.stroke();redrawCoords=[];}}else{this.coords.push([]);}
110
- co.closePath();}
111
- if(prop['chart.variant']==='3d'&&prop['chart.yaxispos']==='right'){RG.draw3DYAxis(this);}
112
- RGraph.noShadow(this);};this.drawLabels=this.DrawLabels=function()
113
- {var context=co;var text_angle=prop['chart.text.angle'],text_size=prop['chart.text.size'],labels=prop['chart.labels']
114
- if(prop['chart.ylabels']){if(prop['chart.xaxispos']=='top')this.Drawlabels_top();if(prop['chart.xaxispos']=='center')this.Drawlabels_center();if(prop['chart.xaxispos']=='bottom')this.Drawlabels_bottom();}
115
- if(typeof(labels)=='object'&&labels){var yOffset=Number(prop['chart.labels.offsety']),xOffset=Number(prop['chart.labels.offsetx']),bold=prop['chart.labels.bold']
116
- if(prop['chart.text.angle']!=0){var valign='center';var halign='right';var angle=0-prop['chart.text.angle'];}else{var valign='top';var halign='center';var angle=0;}
117
- co.fillStyle=prop['chart.labels.color']||prop['chart.text.color'];var barWidth=(ca.width-this.gutterRight-this.gutterLeft)/labels.length;xTickGap=(ca.width-this.gutterRight-this.gutterLeft)/labels.length
118
- var i=0;var font=prop['chart.text.font'];for(x=this.gutterLeft+(xTickGap/2);x<=ca.width-this.gutterRight;x+=xTickGap){RG.text2(this,{'font':font,'size':text_size,'x':x+xOffset,'y':prop['chart.xaxispos']=='top'?this.gutterTop+yOffset-5:(ca.height-this.gutterBottom)+yOffset+3,'bold':bold,'text':String(labels[i++]),'valign':prop['chart.xaxispos']=='top'?'bottom':valign,'halign':halign,'tag':'label','marker':false,'angle':angle,'tag':'labels'});}}
119
- this.drawAboveLabels();};this.drawlabels_top=this.Drawlabels_top=function()
120
- {var ca=this.canvas;var co=this.context;var prop=this.properties;co.beginPath();co.fillStyle=prop['chart.text.color'];co.strokeStyle='black';if(prop['chart.xaxispos']=='top'){var context=co;var text_size=prop['chart.text.size'];var units_pre=prop['chart.units.pre'];var units_post=prop['chart.units.post'];var align=prop['chart.yaxispos']=='left'?'right':'left';var font=prop['chart.text.font'];var numYLabels=prop['chart.ylabels.count'];var ymin=prop['chart.ymin'];var offsetx=prop['chart.ylabels.offsetx'];var offsety=prop['chart.ylabels.offsety'];if(prop['chart.ylabels.inside']==true){var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft+5:ca.width-this.gutterRight-5;var align=prop['chart.yaxispos']=='left'?'left':'right';var boxed=true;}else{var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var boxed=false;}
121
- if(typeof(prop['chart.ylabels.specific'])=='object'&&prop['chart.ylabels.specific']){var labels=RGraph.array_reverse(prop['chart.ylabels.specific']);var grapharea=ca.height-this.gutterTop-this.gutterBottom;for(var i=0;i<labels.length;++i){var y=this.gutterTop+(grapharea*(i/labels.length))+(grapharea/labels.length);RG.text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':y+offsety,'text':String(labels[i]),'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
122
- return;}
123
- var labels=this.scale2.labels;for(var i=0;i<labels.length;++i){RGraph.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+((this.grapharea/labels.length)*(i+1))+offsety,'text':'-'+labels[i],'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
124
- if(prop['chart.ymin']!=0||prop['chart.noxaxis']||prop['chart.scale.zerostart']){RGraph.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+offsety,'text':(this.scale2.min!=0?'-':'')+RGraph.numberFormat(this,(this.scale2.min.toFixed((this.scale2.min===0?0:prop['chart.scale.decimals']))),units_pre,units_post),'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}}
125
- co.fill();};this.drawlabels_center=this.Drawlabels_center=function()
126
- {var ca=this.canvas;var co=this.context;var prop=this.properties;var font=prop['chart.text.font'];var numYLabels=prop['chart.ylabels.count'];co.fillStyle=prop['chart.text.color'];if(prop['chart.xaxispos']=='center'){var text_size=prop['chart.text.size'];var units_pre=prop['chart.units.pre'];var units_post=prop['chart.units.post'];var context=co;var align='';var xpos=0;var boxed=false;var ymin=prop['chart.ymin'];var offsetx=prop['chart.ylabels.offsetx'];var offsety=prop['chart.ylabels.offsety'];co.fillStyle=prop['chart.text.color'];co.strokeStyle='black';if(prop['chart.ylabels.inside']==true){var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft+5:ca.width-this.gutterRight-5;var align=prop['chart.yaxispos']=='left'?'left':'right';var boxed=true;}else{var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var align=prop['chart.yaxispos']=='left'?'right':'left';var boxed=false;}
127
- if(typeof(prop['chart.ylabels.specific'])=='object'&&prop['chart.ylabels.specific']){var labels=prop['chart.ylabels.specific'];var grapharea=ca.height-this.gutterTop-this.gutterBottom;for(var i=0;i<labels.length;++i){var y=this.gutterTop+((grapharea/2)/(labels.length-1))*i;RGraph.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':y+offsety,'text':String(labels[i]),'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
128
- for(var i=labels.length-1;i>=1;--i){var y=this.gutterTop+(grapharea*(i/((labels.length-1)*2)))+(grapharea/2);RG.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':y+offsety,'text':String(labels[labels.length-i-1]),'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
129
- return;}
130
- for(var i=0;i<this.scale2.labels.length;++i){var y=this.gutterTop+this.halfgrapharea-((this.halfgrapharea/numYLabels)*(i+1));var text=this.scale2.labels[i];RG.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':y+offsety,'text':text,'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
131
- for(var i=(this.scale2.labels.length-1);i>=0;--i){var y=this.gutterTop+((this.halfgrapharea/numYLabels)*(i+1))+this.halfgrapharea;var text=this.scale2.labels[i];RG.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':y+offsety,'text':'-'+text,'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
132
- if(this.scale2.min!=0||prop['chart.scale.zerostart']){RG.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+this.halfgrapharea+offsety,'text':RG.number_format(this,(this.scale2.min.toFixed((this.scale2.min===0?0:prop['chart.scale.decimals']))),units_pre,units_post),'valign':'center','valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}}};this.drawlabels_bottom=this.Drawlabels_bottom=function()
133
- {var text_size=prop['chart.text.size'],units_pre=prop['chart.units.pre'],units_post=prop['chart.units.post'],context=this.context,align=prop['chart.yaxispos']=='left'?'right':'left',font=prop['chart.text.font'],numYLabels=prop['chart.ylabels.count'],ymin=prop['chart.ymin'],offsetx=prop['chart.ylabels.offsetx'],offsety=prop['chart.ylabels.offsety']
134
- co.beginPath();co.fillStyle=prop['chart.text.color'];co.strokeStyle='black';if(prop['chart.ylabels.inside']==true){var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft+5:ca.width-this.gutterRight-5;var align=prop['chart.yaxispos']=='left'?'left':'right';var boxed=true;}else{var xpos=prop['chart.yaxispos']=='left'?this.gutterLeft-5:ca.width-this.gutterRight+5;var boxed=false;}
135
- if(prop['chart.ylabels.specific']&&typeof(prop['chart.ylabels.specific'])=='object'){var labels=prop['chart.ylabels.specific'];var grapharea=ca.height-this.gutterTop-this.gutterBottom;for(var i=0;i<labels.length;++i){var y=this.gutterTop+(grapharea*(i/(labels.length-1)));RGraph.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':y+offsety,'text':labels[i],'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
136
- return;}
137
- var gutterTop=this.gutterTop;var halfTextHeight=this.halfTextHeight;var scale=this.scale;for(var i=0;i<numYLabels;++i){var text=this.scale2.labels[i];RGraph.Text2(this,{'font':font,'size':text_size,'x':xpos+offsetx,'y':this.gutterTop+this.grapharea-((this.grapharea/numYLabels)*(i+1))+offsety,'text':text,'valign':'center','halign':align,'bordered':boxed,'tag':'scale'});}
138
- if(prop['chart.ymin']!=0||prop['chart.noxaxis']||prop['chart.scale.zerostart']){RG.text2(this,{font:font,size:text_size,x:xpos+offsetx,y:ca.height-this.gutterBottom+offsety,text:RG.numberFormat(this,(this.scale2.min.toFixed((this.scale2.min===0?0:prop['chart.scale.decimals']))),units_pre,units_post),valign:'center',halign:align,bordered:boxed,tag:'scale'});}
139
- co.fill();};this.drawIEShadow=this.DrawIEShadow=function(coords)
140
- {var co=this.context;var ca=this.canvas;var prop=this.properties;var prevFillStyle=co.fillStyle;var offsetx=prop['chart.shadow.offsetx'];var offsety=prop['chart.shadow.offsety'];co.lineWidth=prop['chart.linewidth'];co.fillStyle=prop['chart.shadow.color'];co.beginPath();co.fillRect(coords[0]+offsetx,coords[1]+offsety,coords[2],coords[3]);co.fill();co.fillStyle=prevFillStyle;};this.getShape=this.getBar=function(e)
141
- {var obj=arguments[1]?arguments[1]:this;var mouseXY=RG.getMouseXY(e),mouseX=mouseXY[0],mouseY=mouseXY[1],canvas=obj.canvas,context=obj.context,coords=obj.coords
142
- for(var i=0,len=coords.length;i<len;i+=1){if(obj.coords[i].length==0){continue;}
143
- var left=coords[i][0],top=coords[i][1],width=coords[i][2],height=coords[i][3],prop=obj.properties
144
- if(prop['chart.tooltips.hotspot.xonly']){pa2(co,'b r % % % %',left,this.gutterTop,width,ca.height-this.gutterBottom);}else{pa2(co,'b r % % % %',left,top,width,height);}
145
- if(co.isPointInPath(mouseX,mouseY)){if(prop['chart.tooltips']){var tooltip=RG.parseTooltipText?RG.parseTooltipText(prop['chart.tooltips'],i):prop['chart.tooltips'][i];}
146
- var dataset=0,idx=i
147
- while(idx>=(typeof obj.data[dataset]==='object'&&obj.data[dataset]?obj.data[dataset].length:1)){if(typeof obj.data[dataset]==='number'){idx-=1;}else if(obj.data[dataset]){idx-=obj.data[dataset].length;}else{idx-=1;}
148
- dataset++;}
149
- if(typeof(obj.data[dataset])=='number'){idx=null;}
150
- return{0:obj,1:left,2:top,3:width,4:height,5:i,'object':obj,'x':left,'y':top,'width':width,'height':height,'index':i,'tooltip':tooltip,'index_adjusted':idx,'dataset':dataset};}}
151
- return null;};this.getShapeByX=function(e)
152
- {var canvas=e.target;var mouseCoords=RGraph.getMouseXY(e);var obj=arguments[1]?arguments[1]:this;for(var i=0,len=obj.coords.length;i<len;i++){if(obj.coords[i].length==0){continue;}
153
- var mouseX=mouseCoords[0];var mouseY=mouseCoords[1];var left=obj.coords[i][0];var top=obj.coords[i][1];var width=obj.coords[i][2];var height=obj.coords[i][3];var prop=obj.properties;if(mouseX>=left&&mouseX<=(left+width)){if(prop['chart.tooltips']){var tooltip=RGraph.parseTooltipText?RGraph.parseTooltipText(prop['chart.tooltips'],i):prop['chart.tooltips'][i];}
154
- return{0:obj,1:left,2:top,3:width,4:height,5:i,'object':obj,'x':left,'y':top,'width':width,'height':height,'index':i,'tooltip':tooltip};}}
155
- return null;};this.getValue=function(arg)
156
- {var co=this.context;var ca=this.canvas;var prop=this.properties;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];}
157
- if(mouseY<prop['chart.gutter.top']||mouseY>(ca.height-prop['chart.gutter.bottom'])||mouseX<prop['chart.gutter.left']||mouseX>(ca.width-prop['chart.gutter.right'])){return null;}
158
- if(prop['chart.xaxispos']=='center'){var value=(((this.grapharea/2)-(mouseY-prop['chart.gutter.top']))/this.grapharea)*(this.scale2.max-this.scale2.min)
159
- value*=2;if(value>=0){value+=this.scale2.min;}else{value-=this.scale2.min;}}else if(prop['chart.xaxispos']=='top'){var value=((this.grapharea-(mouseY-prop['chart.gutter.top']))/this.grapharea)*(this.scale2.max-this.scale2.min)
160
- value=this.scale2.max-value;value=ma.abs(value)* -1;}else{var value=((this.grapharea-(mouseY-prop['chart.gutter.top']))/this.grapharea)*(this.scale2.max-this.scale2.min)
161
- value+=this.scale2.min;}
162
- return value;};this.getYCoord=function(value)
163
- {if(value>this.scale2.max){return null;}
164
- var co=this.context,ca=this.canvas,prop=this.properties;var y,xaxispos=prop['chart.xaxispos'];if(xaxispos=='top'){if(value<0){value=ma.abs(value);}
165
- y=((value-this.scale2.min)/(this.scale2.max-this.scale2.min))*this.grapharea;y=y+this.gutterTop}else if(xaxispos=='center'){y=((value-this.scale2.min)/(this.scale2.max-this.scale2.min))*(this.grapharea/2);y=(this.grapharea/2)-y;y+=this.gutterTop;}else{if(value<this.scale2.min){value=this.scale2.min;}
166
- y=((value-this.scale2.min)/(this.scale2.max-this.scale2.min));y*=(ca.height-this.gutterTop-this.gutterBottom);y=ca.height-this.gutterBottom-y;}
167
- return y;};this.highlight=this.Highlight=function(shape)
168
- {if(typeof prop['chart.highlight.style']==='function'){(prop['chart.highlight.style'])(shape);}else{RG.Highlight.Rect(this,shape);}};this.getObjectByXY=function(e)
169
- {var mouseXY=RG.getMouseXY(e);if(prop['chart.variant']==='3d'){var adjustment=prop['chart.variant.threed.angle']*mouseXY[0];mouseXY[1]-=adjustment;}
170
- if(mouseXY[0]>=prop['chart.gutter.left']&&mouseXY[0]<=(ca.width-prop['chart.gutter.right'])&&mouseXY[1]>=prop['chart.gutter.top']&&mouseXY[1]<=(ca.height-prop['chart.gutter.bottom'])){return this;}};this.adjusting_mousemove=this.Adjusting_mousemove=function(e)
171
- {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')
172
- if(shape){RG.Registry.Set('chart.adjusting.shape',shape);if(this.stackedOrGrouped&&prop['chart.grouping']=='grouped'){var indexes=RG.sequentialIndexToGrouped(shape['index'],this.data);if(typeof this.data[indexes[0]]=='number'){this.data[indexes[0]]=Number(value);}else if(!RG.isNull(this.data[indexes[0]])){this.data[indexes[0]][indexes[1]]=Number(value);}}else if(typeof this.data[shape['index']]=='number'){this.data[shape['index']]=Number(value);}
173
- RG.redrawCanvas(e.target);RG.fireCustomEvent(this,'onadjust');}}};this.parseColors=function()
174
- {if(this.original_colors.length===0){this.original_colors['chart.colors']=RGraph.array_clone(prop['chart.colors']);this.original_colors['chart.key.colors']=RGraph.array_clone(prop['chart.key.colors']);this.original_colors['chart.crosshairs.color']=prop['chart.crosshairs.color'];this.original_colors['chart.highlight.stroke']=prop['chart.highlight.stroke'];this.original_colors['chart.highlight.fill']=prop['chart.highlight.fill'];this.original_colors['chart.text.color']=prop['chart.text.color'];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.strokecolor']=prop['chart.strokecolor'];this.original_colors['chart.axis.color']=prop['chart.axis.color'];}
175
- var colors=prop['chart.colors'];if(colors){for(var i=0;i<colors.length;++i){colors[i]=this.parseSingleColorForGradient(colors[i]);}}
176
- var colors=prop['chart.key.colors'];if(colors){for(var i=0;i<colors.length;++i){colors[i]=this.parseSingleColorForGradient(colors[i]);}}
177
- prop['chart.crosshairs.color']=this.parseSingleColorForGradient(prop['chart.crosshairs.color']);prop['chart.highlight.stroke']=this.parseSingleColorForGradient(prop['chart.highlight.stroke']);prop['chart.highlight.fill']=this.parseSingleColorForGradient(prop['chart.highlight.fill']);prop['chart.text.color']=this.parseSingleColorForGradient(prop['chart.text.color']);prop['chart.background.barcolor1']=this.parseSingleColorForGradient(prop['chart.background.barcolor1']);prop['chart.background.barcolor2']=this.parseSingleColorForGradient(prop['chart.background.barcolor2']);prop['chart.background.grid.color']=this.parseSingleColorForGradient(prop['chart.background.grid.color']);prop['chart.background.color']=this.parseSingleColorForGradient(prop['chart.background.color']);prop['chart.strokecolor']=this.parseSingleColorForGradient(prop['chart.strokecolor']);prop['chart.axis.color']=this.parseSingleColorForGradient(prop['chart.axis.color']);};this.reset=function()
178
- {};this.parseSingleColorForGradient=function(color)
179
- {if(!color||typeof(color)!='string'){return color;}
180
- if(color.match(/^gradient\((.*)\)$/i)){var parts=RegExp.$1.split(':');var grad=co.createLinearGradient(0,ca.height-prop['chart.gutter.bottom'],0,prop['chart.gutter.top']);var diff=1/(parts.length-1);grad.addColorStop(0,RG.trim(parts[0]));for(var j=1,len=parts.length;j<len;++j){grad.addColorStop(j*diff,RGraph.trim(parts[j]));}}
181
- return grad?grad:color;};this.drawBevel=this.DrawBevel=function()
182
- {var coords=this.coords;var coords2=this.coords2;var prop=this.properties;var co=this.context;var ca=this.canvas;if(prop['chart.grouping']=='stacked'){for(var i=0;i<coords2.length;++i){if(coords2[i]&&coords2[i][0]&&coords2[i][0][0]){var x=coords2[i][0][0];var y=coords2[i][0][1];var w=coords2[i][0][2];var arr=[];for(var j=0;j<coords2[i].length;++j){arr.push(coords2[i][j][3]);}
183
- var h=RGraph.array_sum(arr);co.save();co.strokeStyle='black';co.beginPath();co.rect(x,y,w,h);co.clip();co.shadowColor='black';co.shadowOffsetX=0;co.shadowOffsetY=0;co.shadowBlur=20;co.beginPath();co.rect(x-3,y-3,w+6,h+100);co.lineWidth=5;co.stroke();co.restore();}}}else{for(var i=0;i<coords.length;++i){if(coords[i]){var x=coords[i][0];var y=coords[i][1];var w=coords[i][2];var h=coords[i][3];var xaxispos=prop['chart.xaxispos'];var xaxis_ycoord=((ca.height-this.gutterTop-this.gutterBottom)/2)+this.gutterTop;co.save();co.strokeStyle='black';co.beginPath();co.rect(x,y,w,h);co.clip();co.shadowColor='black';co.shadowOffsetX=0;co.shadowOffsetY=0;co.shadowBlur=20;if(xaxispos=='top'||(xaxispos=='center'&&(y+h)>xaxis_ycoord)){y=y-100;h=h+100;}else{y=y;h=h+100;}
184
- co.beginPath();co.rect(x-3,y-3,w+6,h+6);co.lineWidth=5;co.stroke();co.restore();}}}};this.interactiveKeyHighlight=function(index)
185
- {this.coords2.forEach(function(value,idx,arr)
186
- {if(typeof value[index]=='object'&&value[index]){var x=value[index][0]
187
- var y=value[index][1]
188
- var w=value[index][2]
189
- var h=value[index][3]
190
- co.fillStyle=prop['chart.key.interactive.highlight.chart.fill'];co.strokeStyle=prop['chart.key.interactive.highlight.chart.stroke'];co.lineWidth=2;co.strokeRect(x,y,w,h);co.fillRect(x,y,w,h);}});};this.on=function(type,func)
191
- {if(type.substr(0,2)!=='on'){type='on'+type;}
192
- if(typeof this[type]!=='function'){this[type]=func;}else{RG.addCustomEventListener(this,type,func);}
193
- return this;};this.drawAboveLabels=function()
194
- {var labels=prop['chart.labels.above'],specific=prop['chart.labels.above.specific'],color=prop['chart.labels.above.color'],background=prop['chart.labels.above.background'],decimals=prop['chart.labels.above.decimals'],size=prop['chart.labels.above.size'],angle=-1*prop['chart.labels.above.angle'],unitsPre=prop['chart.labels.above.units.pre'],unitsPost=prop['chart.labels.above.units.post'],coords=this.coords,coords2=this.coords2,data=this.data,ldata=RG.arrayLinearize(this.data),offset=prop['chart.labels.above.offset'],text_font=prop['chart.text.font'],text_size=prop['chart.text.size'],grouping=prop['chart.grouping']
195
- RG.noShadow(this);co.fillStyle=typeof color==='string'?color:prop['chart.text.color'];if(labels&&grouping==='grouped'){for(var i=0,len=data.length,sequentialIndex=0;i<len;i+=1){if(typeof data[i]==='number'&&data[i]>=0){var angle=angle;var halign=(angle?'left':'center');var valign=angle!==0?'center':'bottom';RG.text2(this,{'font':text_font,'size':typeof size==='number'?size:text_size-3,'x':coords2[i][0][0]+(coords2[i][0][2]/2),'y':coords2[i][0][1]-offset,'text':specific?(specific[sequentialIndex]||''):RG.numberFormat(this,Number(typeof data[i]==='object'?data[i][0]:data[i]).toFixed(decimals),unitsPre,unitsPost),'halign':halign,'valign':valign,'angle':angle,'marker':false,'bounding':true,'bounding.fill':background,'bounding.stroke':'rgba(0,0,0,0)','tag':'labels.above'});sequentialIndex++;}else if(typeof data[i]==='number'&&data[i]<0){var angle=angle;var halign=angle?'right':'center';var valign=angle!==0?'center':'top';RG.text2(this,{'font':text_font,'size':typeof size==='number'?size:text_size-3,'x':coords2[i][0][0]+(coords2[i][0][2]/2),'y':coords2[i][0][1]+coords2[i][0][3]+offset,'text':specific?(specific[sequentialIndex]||''):RG.numberFormat(this,Number(typeof data[i]==='object'?data[i][0]:data[i]).toFixed(decimals),unitsPre,unitsPost),'halign':halign,'valign':valign,'angle':angle,'bounding':true,'bounding.fill':background,'bounding.stroke':'rgba(0,0,0,0)','marker':false,'tag':'labels.above'});sequentialIndex++;}else if(typeof data[i]==='object'){for(var j=0,len2=data[i].length;j<len2;j+=1){var angle=angle;var halign=data[i][j]<0?'right':'left';halign=angle===0?'center':halign;var valign=data[i][j]<0?'top':'bottom';valign=angle!=0?'center':valign;RG.text2(this,{'font':text_font,'size':typeof size==='number'?size:text_size-3,'x':coords2[i][j][0]+(coords2[i][j][2]/2),'y':coords2[i][j][1]+(data[i][j]<0?coords2[i][j][3]+offset:-offset),'text':specific?(specific[sequentialIndex]||''):RG.numberFormat(this,Number(data[i][j]).toFixed(decimals),unitsPre,unitsPost),'halign':halign,'valign':valign,'angle':angle,'bounding':true,'bounding.fill':background,'bounding.stroke':'rgba(0,0,0,0)','marker':false,'tag':'labels.above'});sequentialIndex++;}}}}else if(labels&&grouping==='stacked'){for(var i=0,len=data.length,sequentialIndex=0;i<len;i+=1){if(typeof data[i]==='object'){var angle=angle;var halign=angle!=0?'left':'center';var valign=angle!=0?'center':'bottom';RG.text2(this,{'font':text_font,'size':typeof size==='number'?size:text_size-3,'x':coords2[i][0][0]+(coords2[i][0][2]/2),'y':coords2[i][0][1]+(data[i][0]<0?coords2[i][0][3]:0)-offset,'text':specific?(specific[sequentialIndex]||''):RG.numberFormat(this,Number(RG.arraySum(data[i])).toFixed(decimals),unitsPre,unitsPost),'halign':halign,'valign':valign,'angle':angle,'bounding':true,'bounding.fill':background,'bounding.stroke':'rgba(0,0,0,0)','marker':false,'tag':'labels.above'});sequentialIndex+=data[i].length;}else{var angle=angle;var halign=angle!=0?'left':'center';var valign=angle!=0?'center':'bottom';RG.text2(this,{'font':text_font,'size':typeof size==='number'?size:text_size-3,'x':coords2[i][0][0]+(coords2[i][0][2]/2),'y':coords2[i][0][1]+(data[i][0]<0?coords2[i][0][3]:0)-offset,'text':specific?(specific[sequentialIndex]||''):RG.numberFormat(this,Number(data[i]).toFixed(decimals),unitsPre,unitsPost),'halign':halign,'valign':valign,'angle':angle,'bounding':true,'bounding.fill':background,'bounding.stroke':'rgba(0,0,0,0)','marker':false,'tag':'labels.above'});sequentialIndex++;}}}};this.firstDrawFunc=function()
196
- {};this.wave=function()
197
- {var obj=this,opt=arguments[0]||{},labelsAbove=this.get('labelsAbove');opt.frames=opt.frames||60;opt.startFrames=[];opt.counters=[];var framesperbar=opt.frames/3,frame=-1,callback=arguments[1]||function(){},original=RG.arrayClone(this.original_data);this.set('labelsAbove',false);for(var i=0,len=obj.data.length;i<len;i+=1){opt.startFrames[i]=((opt.frames/2)/(obj.data.length-1))*i;if(typeof obj.data[i]==='object'&&obj.data[i]){opt.counters[i]=[];for(var j=0;j<obj.data[i].length;j++){opt.counters[i][j]=0;}}else{opt.counters[i]=0;}}
198
- obj.draw();obj.Set('ymax',obj.scale2.max);RG.clear(obj.canvas);function iterator()
199
- {++frame;for(var i=0,len=obj.data.length;i<len;i+=1){if(frame>opt.startFrames[i]){if(typeof obj.data[i]==='number'){obj.data[i]=ma.min(ma.abs(original[i]),ma.abs(original[i]*((opt.counters[i]++)/framesperbar)));if(original[i]<0){obj.data[i]*=-1;}}else if(!RG.isNull(obj.data[i])){for(var j=0,len2=obj.data[i].length;j<len2;j+=1){obj.data[i][j]=ma.min(ma.abs(original[i][j]),ma.abs(original[i][j]*((opt.counters[i][j]++)/framesperbar)));if(original[i][j]<0){obj.data[i][j]*=-1;}}}}else{obj.data[i]=typeof obj.data[i]==='object'&&obj.data[i]?RG.arrayPad([],obj.data[i].length,0):(RG.isNull(obj.data[i])?null:0);}}
200
- if(frame>=opt.frames){if(labelsAbove){obj.set('labelsAbove',true);RG.redraw();}
201
- callback(obj);}else{RG.redrawCanvas(obj.canvas);RG.Effects.updateCanvas(iterator);}}
202
- iterator();return this;};this.colorWave=function()
203
- {var obj=this,opt=arguments[0]||{};opt.frames=opt.frames||60;opt.startFrames=[];opt.counters=[],colors=obj.properties['chart.colors'];if(colors.length<=obj.data.length){obj.set('chart.colors.sequential',true);colors=RG.arrayPad(colors,obj.data.length,colors[colors.length-1]);}
204
- var framesperbar=opt.frames/2,frame=-1,callback=arguments[1]||function(){},originalColors=RG.arrayClone(obj.properties['chart.colors']);for(var i=0,len=originalColors.length;i<len;i+=1){opt.startFrames[i]=((opt.frames/2)/(originalColors.length-1))*i;opt.counters[i]=0;}
205
- function iterator()
206
- {++frame;for(var i=0,len=colors.length;i<len;i+=1){if(frame>opt.startFrames[i]&&colors[i].match(/^rgba?\(([0-9 ]+),([0-9 ]+),([0-9 ]+)(,([ 0-9.]+)?)\)/)){colors[i]='rgba({1},{2},{3},{4})'.format(RegExp.$1,RegExp.$2,RegExp.$3,(frame-opt.startFrames[i])/framesperbar);}else{colors[i]=colors[i].replace(/,[0-9. ]+\)/,',0)');}}
207
- if(frame>=opt.frames){callback(obj);}else{RG.redrawCanvas(obj.canvas);RG.Effects.updateCanvas(iterator);}}
208
- iterator();return this;};this.grow=function()
209
- {var opt=arguments[0]||{},frames=opt.frames||30,frame=0,callback=arguments[1]||function(){},obj=this,labelsAbove=this.get('labelsAbove')
210
- if(RG.isArray(opt.data)){var ymax=0;for(var i=0;i<opt.data.length;++i){if(typeof opt.data[i]==='object'){for(var j=0;j<opt.data[i].length;++j){if(typeof opt.data[i][j]==='string'&&opt.data[i][j].match(/(\+|\-)([0-9]+)/)){if(RegExp.$1==='+'){opt.data[i][j]=this.original_data[i][j]+parseInt(RegExp.$2);}else{opt.data[i][j]=this.original_data[i][j]-parseInt(RegExp.$2);}}
211
- ymax=ma.max(ymax,opt.data[i][j]);}}else if(typeof opt.data[i]==='string'&&opt.data[i].match(/(\+|\-)([0-9]+)/)){if(RegExp.$1==='+'){opt.data[i]=this.original_data[i]+parseInt(RegExp.$2);}else{opt.data[i]=this.original_data[i]-parseInt(RegExp.$2);}
212
- ymax=ma.max(ymax,opt.data[i]);}else{ymax=ma.max(ymax,opt.data[i]);}}
213
- var scale=RG.getScale2(this,{'max':ymax});this.Set('chart.ymax',scale.max);}
214
- this.set('labelsAbove',false);if(prop['chart.ymax']==null){var ymax=0;for(var i=0;i<obj.data.length;++i){if(RG.isArray(this.data[i])&&prop['chart.grouping']==='stacked'){ymax=ma.max(ymax,ma.abs(RG.arraySum(this.data[i])));}else if(RG.isArray(this.data[i])&&prop['chart.grouping']==='grouped'){for(var j=0,group=[];j<this.data[i].length;j++){group.push(ma.abs(this.data[i][j]));}
215
- ymax=ma.max(ymax,ma.abs(RG.arrayMax(group)));}else{ymax=ma.max(ymax,ma.abs(this.data[i]));}}
216
- var scale=RG.getScale2(this,{'max':ymax});this.Set('chart.ymax',scale.max);}
217
- if(typeof opt.ymax==='number'){obj.set('ymax',opt.ymax);}
218
- var iterator=function()
219
- {var easingMultiplier=RG.Effects.getEasingMultiplier(frames,frame);for(var j=0,len=obj.original_data.length;j<len;++j){if(typeof obj.data[j]==='object'&&!RG.isNull(obj.data[j])){for(var k=0,len2=obj.data[j].length;k<len2;++k){if(obj.firstDraw||!opt.data){obj.data[j][k]=easingMultiplier*obj.original_data[j][k];}else if(opt.data&&opt.data.length===obj.original_data.length){var diff=opt.data[j][k]-obj.original_data[j][k];obj.data[j][k]=(easingMultiplier*diff)+obj.original_data[j][k];}}}else{if(obj.firstDraw||!opt.data){obj.data[j]=easingMultiplier*obj.original_data[j];}else if(opt.data&&opt.data.length===obj.original_data.length){var diff=opt.data[j]-obj.original_data[j];obj.data[j]=(easingMultiplier*diff)+obj.original_data[j];}}}
220
- RG.redrawCanvas(obj.canvas);if(frame<frames){frame+=1;RG.Effects.updateCanvas(iterator);}else{if(RG.isArray(opt.data)){var linear_data=RG.arrayLinearize(data);for(var i=0;i<linear_data.length;++i){if(!obj['$'+i]){obj['$'+i]={};}}}
221
- obj.data=data;obj.original_data=RG.arrayClone(data);if(labelsAbove){obj.set('labelsAbove',true);RG.redraw();}
222
- callback(obj);}};iterator();return this;};this.drawErrorbars=function()
223
- {var coords=this.coords,color=prop['chart.errorbars.color']||'black',default_halfwidth=ma.min(prop['chart.errorbars.capped.width'],coords[0][2])/2,x=0,errorbars=prop['chart.errorbars'],length=0;if(!prop['chart.errorbars.capped']){prop['chart.errorbars.capped.width']=0;halfwidth=0;}
224
- co.lineWidth=prop['chart.errorbars.linewidth'];for(var i=0;i<coords.length;++i){color=prop['chart.errorbars.color']||'black';if(errorbars[i]&&typeof errorbars[i][3]==='number'){co.lineWidth=errorbars[i][3];}
225
- var halfwidth=(errorbars[i]&&typeof errorbars[i][4]==='number')?errorbars[i][4]/2:default_halfwidth;if(!prop['chart.errorbars.capped']){halfwidth=0;}
226
- if(typeof errorbars[i]==='number'){length=ma.abs(this.getYCoord(errorbars[i])-this.getYCoord(0));if(length){pa2(co,'b m % % l % % l % % l % % s %',coords[i][0]+(coords[i][2]/2),coords[i][1],coords[i][0]+(coords[i][2]/2),coords[i][1]-length,coords[i][0]+(coords[i][2]/2)-halfwidth,ma.round(coords[i][1]-length),coords[i][0]+(coords[i][2]/2)+halfwidth,ma.round(coords[i][1]-length),color);}}else if(typeof errorbars[i]==='object'&&!RG.isNull(errorbars[i])){var positiveLength=ma.abs(this.getYCoord(errorbars[i][0])-this.getYCoord(0));if(typeof errorbars[i][1]==='string'){color=errorbars[i][1];}else if(typeof errorbars[i][2]==='string'){color=errorbars[i][2];}
227
- halfwidth=typeof errorbars[i][4]==='number'?errorbars[i][4]/2:default_halfwidth;if(!prop['chart.errorbars.capped']){halfwidth=0;}
228
- if(!RG.isNull(errorbars[i][0])){pa2(co,'b m % % l % % l % % l % % s %',coords[i][0]+(coords[i][2]/2),coords[i][1],coords[i][0]+(coords[i][2]/2),coords[i][1]-positiveLength,coords[i][0]+(coords[i][2]/2)-halfwidth,ma.round(coords[i][1]-positiveLength),coords[i][0]+(coords[i][2]/2)+halfwidth,ma.round(coords[i][1]-positiveLength),color);}
229
- if(typeof errorbars[i][1]==='number'){var negativeLength=ma.abs(this.getYCoord(errorbars[i][1])-this.getYCoord(0));pa2(co,'b m % % l % % l % % l % % s %',coords[i][0]+(coords[i][2]/2),coords[i][1],coords[i][0]+(coords[i][2]/2),coords[i][1]+negativeLength,coords[i][0]+(coords[i][2]/2)-halfwidth,ma.round(coords[i][1]+negativeLength),coords[i][0]+(coords[i][2]/2)+halfwidth,ma.round(coords[i][1]+negativeLength),color);}}
230
- if(errorbars[i]&&typeof errorbars[i][3]==='number'){co.lineWidth=prop['chart.errorbars.linewidth'];}}};this.isAdjustable=function(shape)
231
- {if(RG.isNull(prop['chart.adjustable.only'])||!RG.isArray(prop['chart.adjustable.only'])){return true;}
232
- if(RG.isArray(prop['chart.adjustable.only'])&&prop['chart.adjustable.only'][shape.index]){return true;}
233
- return false;};RG.register(this);if(parseConfObjectForOptions){RG.parseObjectStyleConfig(this,conf.options);}};RGraph.CombinedChart=function()
234
- {this.objects=[];var objects=[];if(RGraph.isArray(arguments[0])){objects=arguments[0];}else{for(var i=0;i<arguments.length;i+=1){objects[i]=arguments[i];}}
235
- for(var i=0;i<objects.length;++i){this.objects[i]=objects[i];this.objects[i].set({gutterLeft:this.objects[0].get('gutter.left'),gutterRight:this.objects[0].get('gutter.right'),gutterTop:this.objects[0].get('gutter.top'),gutterBottom:this.objects[0].get('gutter.bottom')});if(this.objects[i].type=='line'){var obj=this.objects[i];obj.set('hmargin',((this.objects[0].canvas.width-this.objects[0].Get('chart.gutter.right')-this.objects[0].Get('chart.gutter.left'))/this.objects[0].data.length)/2);obj.set('noaxes',true);obj.set('backgroundGrid',false);obj.set('ylabels',false);}
236
- if(this.objects[i].get('chart.resizable')){var resizable_object=obj;}}
237
- if(resizable_object){function myOnresizebeforedraw(obj)
238
- {var gutterLeft=obj.get('gutterLeft');var gutterRight=obj.get('gutterRight');obj.set('hmargin',(obj.canvas.width-gutterLeft-gutterRight)/(obj.original_data[0].length*2));}
239
- RGraph.AddCustomEventListener(resizable_object,'onresizebeforedraw',myOnresizebeforedraw);}};RGraph.CombinedChart.prototype.add=RGraph.CombinedChart.prototype.Add=function(obj)
240
- {this.objects.push(obj);};RGraph.CombinedChart.prototype.draw=RGraph.CombinedChart.prototype.Draw=function()
241
- {for(var i=0;i<this.objects.length;++i){if(this.objects[i].properties['chart.combinedchart.effect']){var options=this.objects[i].properties['chart.combinedchart.effect.options']?eval('('+this.objects[i].properties['chart.combinedchart.effect.options']+')'):null;(this.objects[i][this.objects[i].properties['chart.combinedchart.effect']])
242
- (options,this.objects[i].properties['chart.combinedchart.effect.callback'])}else{this.objects[i].draw();}}};
13
+ RGraph = window.RGraph || {isRGraph: true};
14
+
15
+
16
+
17
+
18
+ /**
19
+ * The bar chart constructor
20
+ *
21
+ * @param object canvas The canvas object
22
+ * @param array data The chart data
23
+ */
24
+ RGraph.Bar = function (conf)
25
+ {
26
+ /**
27
+ * Allow for object config style
28
+ */
29
+ if (typeof conf === 'object' && typeof conf.data === 'object'&& typeof conf.id === 'string') {
30
+ var id = conf.id,
31
+ canvas = document.getElementById(id),
32
+ data = conf.data,
33
+ parseConfObjectForOptions = true // Set this so the config is parsed (at the end of the constructor)
34
+ } else {
35
+ var id = conf,
36
+ canvas = document.getElementById(id),
37
+ data = arguments[1]
38
+ }
39
+
40
+
41
+
42
+
43
+ // Get the canvas and context objects
44
+ this.id = id;
45
+ this.canvas = canvas;
46
+ this.context = this.canvas.getContext('2d');
47
+ this.canvas.__object__ = this;
48
+ this.type = 'bar';
49
+ this.max = 0;
50
+ this.stackedOrGrouped = false;
51
+ this.isRGraph = true;
52
+ this.uid = RGraph.CreateUID();
53
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
54
+ this.colorsParsed = false;
55
+ this.original_colors = [];
56
+ this.cachedBackgroundCanvas = null;
57
+ this.firstDraw = true; // After the first draw this will be false
58
+
59
+
60
+ /**
61
+ * Compatibility with older browsers
62
+ */
63
+ //RGraph.OldBrowserCompat(this.context);
64
+
65
+
66
+ // Various config type stuff
67
+ this.properties =
68
+ {
69
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
70
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
71
+ 'chart.background.grid': true,
72
+ 'chart.background.grid.color': '#ddd',
73
+ 'chart.background.grid.width': 1,
74
+ 'chart.background.grid.hsize': 20,
75
+ 'chart.background.grid.vsize': 20,
76
+ 'chart.background.grid.vlines': true,
77
+ 'chart.background.grid.hlines': true,
78
+ 'chart.background.grid.border': true,
79
+ 'chart.background.grid.autofit':true,
80
+ 'chart.background.grid.autofit.align': true,
81
+ 'chart.background.grid.autofit.numhlines': 5,
82
+ 'chart.background.grid.autofit.numvlines': 20,
83
+ 'chart.background.grid.dashed': false,
84
+ 'chart.background.grid.dotted': false,
85
+ 'chart.background.image.stretch': true,
86
+ 'chart.background.image.x': null,
87
+ 'chart.background.image.y': null,
88
+ 'chart.background.image.w': null,
89
+ 'chart.background.image.h': null,
90
+ 'chart.background.image.align': null,
91
+ 'chart.background.color': null,
92
+ 'chart.background.hbars': null,
93
+ 'chart.numyticks': 10,
94
+ 'chart.hmargin': 5,
95
+ 'chart.hmargin.grouped': 1,
96
+ 'chart.strokecolor': 'rgba(0,0,0,0)',
97
+ 'chart.axis.color': 'black',
98
+ 'chart.axis.linewidth': 1,
99
+ 'chart.gutter.top': 25,
100
+ 'chart.gutter.bottom': 30,
101
+ 'chart.gutter.left': 25,
102
+ 'chart.gutter.right': 25,
103
+ 'chart.labels': null,
104
+ 'chart.labels.bold': false,
105
+ 'chart.labels.color': null,
106
+ 'chart.labels.ingraph': null,
107
+ 'chart.labels.above': false,
108
+ 'chart.labels.above.decimals': 0,
109
+ 'chart.labels.above.size': null,
110
+ 'chart.labels.above.color': null,
111
+ 'chart.labels.above.background':'rgba(0,0,0,0)',
112
+ 'chart.labels.above.angle': null,
113
+ 'chart.labels.above.offset': 4,
114
+ 'chart.labels.above.units.pre': '',
115
+ 'chart.labels.above.units.post':'',
116
+ 'chart.ylabels': true,
117
+ 'chart.ylabels.count': 5,
118
+ 'chart.ylabels.inside': false,
119
+ 'chart.ylabels.offsetx': 0,
120
+ 'chart.ylabels.offsety': 0,
121
+ 'chart.labels.offsetx': 0,
122
+ 'chart.labels.offsety': 0,
123
+ 'chart.xaxispos': 'bottom',
124
+ 'chart.yaxispos': 'left',
125
+ 'chart.text.angle': 0,
126
+ 'chart.text.color': 'black', // Gradients aren't supported for this color
127
+ 'chart.text.size': 12,
128
+ 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
129
+ 'chart.text.accessible': true,
130
+ 'chart.text.accessible.overflow': 'visible',
131
+ 'chart.text.accessible.pointerevents': true,
132
+ 'chart.ymin': 0,
133
+ 'chart.ymax': null,
134
+ 'chart.title': '',
135
+ 'chart.title.font': null,
136
+ 'chart.title.background': null, // Gradients aren't supported for this color
137
+ 'chart.title.hpos': null,
138
+ 'chart.title.vpos': null,
139
+ 'chart.title.bold': true,
140
+ 'chart.title.xaxis': '',
141
+ 'chart.title.xaxis.bold': true,
142
+ 'chart.title.xaxis.size': null,
143
+ 'chart.title.xaxis.font': null,
144
+ 'chart.title.xaxis.color': null,
145
+ 'chart.title.yaxis': '',
146
+ 'chart.title.yaxis.bold': true,
147
+ 'chart.title.yaxis.size': null,
148
+ 'chart.title.yaxis.font': null,
149
+ 'chart.title.yaxis.color': null, // Gradients aren't supported for this color
150
+ 'chart.title.xaxis.pos': null,
151
+ 'chart.title.yaxis.pos': null,
152
+ 'chart.title.yaxis.x': null,
153
+ 'chart.title.yaxis.y': null,
154
+ 'chart.title.xaxis.x': null,
155
+ 'chart.title.xaxis.y': null,
156
+ 'chart.title.x': null,
157
+ 'chart.title.y': null,
158
+ 'chart.title.halign': null,
159
+ 'chart.title.valign': null,
160
+ 'chart.colors': ['red','#0f0','blue','pink','orange','cyan','black','white','green','magenta'],
161
+ 'chart.colors.sequential': false,
162
+ 'chart.colors.reverse': false,
163
+ 'chart.grouping': 'grouped',
164
+ 'chart.variant': 'bar',
165
+ 'chart.variant.sketch.verticals': true,
166
+ 'chart.variant.threed.xaxis': true,
167
+ 'chart.variant.threed.yaxis': true,
168
+ 'chart.variant.threed.angle': 0.1,
169
+ 'chart.variant.threed.offsetx': 10,
170
+ 'chart.variant.threed.offsety': 5,
171
+ 'chart.shadow': false,
172
+ 'chart.shadow.color': '#aaa', // Gradients aren't supported for this color
173
+ 'chart.shadow.offsetx': 0,
174
+ 'chart.shadow.offsety': 0,
175
+ 'chart.shadow.blur': 15,
176
+ 'chart.tooltips': null,
177
+ 'chart.tooltips.effect': 'fade',
178
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
179
+ 'chart.tooltips.event': 'onclick',
180
+ 'chart.tooltips.highlight': true,
181
+ 'chart.tooltips.hotspot.xonly': false,
182
+ 'chart.highlight.stroke': 'rgba(0,0,0,0)',
183
+ 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
184
+ 'chart.key': null,
185
+ 'chart.key.background': 'white',
186
+ 'chart.key.position': 'graph',
187
+ 'chart.key.shadow': false,
188
+ 'chart.key.shadow.color': '#666',
189
+ 'chart.key.shadow.blur': 3,
190
+ 'chart.key.shadow.offsetx': 2,
191
+ 'chart.key.shadow.offsety': 2,
192
+ 'chart.key.position.gutter.boxed':false,
193
+ 'chart.key.position.x': null,
194
+ 'chart.key.position.y': null,
195
+ 'chart.key.interactive': false,
196
+ 'chart.key.interactive.highlight.chart.stroke':'black',
197
+ 'chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)',
198
+ 'chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)',
199
+ 'chart.key.halign': 'right',
200
+ 'chart.key.color.shape': 'square',
201
+ 'chart.key.rounded': true,
202
+ 'chart.key.text.size': 10,
203
+ 'chart.key.linewidth': 1,
204
+ 'chart.key.colors': null,
205
+ 'chart.key.text.color': 'black',
206
+ 'chart.contextmenu': null,
207
+ 'chart.units.pre': '',
208
+ 'chart.units.post': '',
209
+ 'chart.scale.decimals': 0,
210
+ 'chart.scale.point': '.',
211
+ 'chart.scale.thousand': ',',
212
+ 'chart.scale.round': false,
213
+ 'chart.scale.zerostart': true,
214
+ 'chart.crosshairs': false,
215
+ 'chart.crosshairs.color': '#333',
216
+ 'chart.crosshairs.hline': true,
217
+ 'chart.crosshairs.vline': true,
218
+ 'chart.linewidth': 1,
219
+ 'chart.annotatable': false,
220
+ 'chart.annotate.color': 'black',
221
+ 'chart.zoom.factor': 1.5,
222
+ 'chart.zoom.fade.in': true,
223
+ 'chart.zoom.fade.out': true,
224
+ 'chart.zoom.hdir': 'right',
225
+ 'chart.zoom.vdir': 'down',
226
+ 'chart.zoom.frames': 25,
227
+ 'chart.zoom.delay': 16.666,
228
+ 'chart.zoom.shadow': true,
229
+ 'chart.zoom.background': true,
230
+ 'chart.resizable': false,
231
+ 'chart.resize.handle.background': null,
232
+ 'chart.adjustable': false,
233
+ 'chart.adjustable.only': null,
234
+ 'chart.noaxes': false,
235
+ 'chart.noxaxis': false,
236
+ 'chart.noyaxis': false,
237
+ 'chart.events.click': null,
238
+ 'chart.events.mousemove': null,
239
+ 'chart.numxticks': null,
240
+ 'chart.bevel': false,
241
+ 'chart.errorbars': false,
242
+ 'chart.errorbars.color': 'black',
243
+ 'chart.errorbars.capped': true,
244
+ 'chart.errorbars.capped.width': 14,
245
+ 'chart.errorbars.linewidth': 1,
246
+ 'chart.combinedchart.effect': null,
247
+ 'chart.combinedchart.effect.options': null,
248
+ 'chart.combinedchart.effect.callback': null,
249
+ 'chart.clearto': 'rgba(0,0,0,0)'
250
+ }
251
+
252
+ // Check for support
253
+ if (!this.canvas) {
254
+ alert('[BAR] No canvas support');
255
+ return;
256
+ }
257
+
258
+
259
+ //
260
+ // Convert strings into numbers. Also converts undefined elements to null
261
+ //
262
+ for (var i=0; i<data.length; ++i) {
263
+ if (typeof data[i] === 'string') {
264
+ data[i] = parseFloat(data[i]);
265
+ } else if (typeof data[i] === 'object' && data[i]) {
266
+ for (var j=0; j<data[i].length; ++j) {
267
+ if (typeof data[i][j] === 'string') {
268
+ data[i][j] = parseFloat(data[i][j]);
269
+ }
270
+ }
271
+ } else if (typeof data[i] === 'undefined') {
272
+ data[i] = null;
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Determine whether the chart will contain stacked or grouped bars
278
+ */
279
+ for (var i=0; i<data.length; ++i) {
280
+ if (typeof data[i] === 'object' && !RGraph.is_null(data[i])) {
281
+ this.stackedOrGrouped = true;
282
+ }
283
+ }
284
+
285
+
286
+ /**
287
+ * Create the dollar objects so that functions can be added to them
288
+ */
289
+ var linear_data = RGraph.arrayLinearize(data);
290
+
291
+ for (var i=0; i<linear_data.length; ++i) {
292
+ this['$' + i] = {};
293
+ }
294
+
295
+
296
+ // Store the data and set the orignal_data to it
297
+ this.data = data;
298
+ this.original_data = RGraph.arrayClone(data);
299
+
300
+
301
+ // Used to store the coords of the bars
302
+ this.coords = [];
303
+ this.coords2 = [];
304
+ this.coordsText = [];
305
+
306
+
307
+
308
+ /**
309
+ * This linearises the data. Doing so can make it easier to pull
310
+ * out the appropriate data from tooltips
311
+ */
312
+ this.data_arr = RGraph.arrayLinearize(this.data);
313
+
314
+
315
+ /**
316
+ * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
317
+ * done already
318
+ */
319
+ if (!this.canvas.__rgraph_aa_translated__) {
320
+ this.context.translate(0.5,0.5);
321
+
322
+ this.canvas.__rgraph_aa_translated__ = true;
323
+ }
324
+
325
+
326
+
327
+
328
+
329
+ // Short variable names
330
+ var RG = RGraph,
331
+ ca = this.canvas,
332
+ co = ca.getContext('2d'),
333
+ prop = this.properties,
334
+ pa2 = RG.path2,
335
+ win = window,
336
+ doc = document,
337
+ ma = Math
338
+
339
+
340
+
341
+ /**
342
+ * "Decorate" the object with the generic effects if the effects library has been included
343
+ */
344
+ if (RG.Effects && typeof RG.Effects.decorate === 'function') {
345
+ RG.Effects.decorate(this);
346
+ }
347
+
348
+
349
+
350
+
351
+
352
+ /**
353
+ * A setter
354
+ *
355
+ * @param name string The name of the property to set
356
+ * @param value mixed The value of the property
357
+ */
358
+ this.set =
359
+ this.Set = function (name)
360
+ {
361
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
362
+
363
+ /**
364
+ * the number of arguments is only one and it's an
365
+ * object - parse it for configuration data and return.
366
+ */
367
+ if (arguments.length === 1 && typeof arguments[0] === 'object') {
368
+ RG.parseObjectStyleConfig(this, arguments[0]);
369
+ return this;
370
+ }
371
+
372
+
373
+
374
+
375
+
376
+
377
+
378
+
379
+ /**
380
+ * This should be done first - prepend the propertyy name with "chart." if necessary
381
+ */
382
+ if (name.substr(0,6) != 'chart.') {
383
+ name = 'chart.' + name;
384
+ }
385
+
386
+
387
+
388
+
389
+ // Convert uppercase letters to dot+lower case letter
390
+ while(name.match(/([A-Z])/)) {
391
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
392
+ }
393
+
394
+
395
+ // BC accommodation
396
+ if (name === 'chart.xlabels.offset') {
397
+ name = 'chart.labels.offsety';
398
+ }
399
+
400
+ if (name == 'chart.labels.abovebar') {
401
+ name = 'chart.labels.above';
402
+ }
403
+
404
+ if (name == 'chart.strokestyle') {
405
+ name = 'chart.strokecolor';
406
+ }
407
+
408
+ /**
409
+ * Check for xaxispos
410
+ */
411
+ if (name == 'chart.xaxispos' ) {
412
+ if (value != 'bottom' && value != 'center' && value != 'top') {
413
+ alert('[BAR] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
414
+ value = 'center';
415
+ }
416
+
417
+ if (value == 'top') {
418
+ for (var i=0; i<this.data.length; ++i) {
419
+ if (typeof(this.data[i]) == 'number' && this.data[i] > 0) {
420
+ alert('[BAR] The data element with index ' + i + ' should be negative');
421
+ }
422
+ }
423
+ }
424
+ }
425
+
426
+ /**
427
+ * lineWidth doesn't appear to like a zero setting
428
+ */
429
+ if (name.toLowerCase() == 'chart.linewidth' && value == 0) {
430
+ value = 0.0001;
431
+ }
432
+
433
+
434
+
435
+
436
+
437
+
438
+ prop[name] = value;
439
+
440
+ return this;
441
+ };
442
+
443
+
444
+
445
+
446
+ /**
447
+ * A getter
448
+ *
449
+ * @param name string The name of the property to get
450
+ */
451
+ this.get =
452
+ this.Get = function (name)
453
+ {
454
+ /**
455
+ * This should be done first - prepend the property name with "chart." if necessary
456
+ */
457
+ if (name.substr(0,6) != 'chart.') {
458
+ name = 'chart.' + name;
459
+ }
460
+
461
+ // Convert uppercase letters to dot+lower case letter
462
+ while(name.match(/([A-Z])/)) {
463
+ name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
464
+ }
465
+
466
+ return prop[name];
467
+ };
468
+
469
+
470
+
471
+
472
+ /**
473
+ * The function you call to draw the bar chart
474
+ */
475
+ this.draw =
476
+ this.Draw = function ()
477
+ {
478
+ // MUST be the first thing done!
479
+ if (typeof(prop['chart.background.image']) == 'string') {
480
+ RG.DrawBackgroundImage(this);
481
+ }
482
+
483
+ /**
484
+ * Fire the onbeforedraw event
485
+ */
486
+ RG.FireCustomEvent(this, 'onbeforedraw');
487
+
488
+
489
+
490
+ //
491
+ // If the chart is 3d then angle it it
492
+ //
493
+ if (prop['chart.variant'] === '3d') {
494
+ if (prop['chart.text.accessible']) {
495
+ // Nada
496
+ } else {
497
+ co.setTransform(1,prop['chart.variant.threed.angle'],0,1,0.5,0.5);
498
+ }
499
+ }
500
+
501
+
502
+
503
+ /**
504
+ * Parse the colors. This allows for simple gradient syntax
505
+ */
506
+ if (!this.colorsParsed) {
507
+ this.parseColors();
508
+
509
+ // Don't want to do this again
510
+ this.colorsParsed = true;
511
+ }
512
+
513
+
514
+
515
+ /**
516
+ * This is new in May 2011 and facilitates indiviual gutter settings,
517
+ * eg chart.gutter.left
518
+ */
519
+ this.gutterLeft = prop['chart.gutter.left'];
520
+ this.gutterRight = prop['chart.gutter.right'];
521
+ this.gutterTop = prop['chart.gutter.top'];
522
+ this.gutterBottom = prop['chart.gutter.bottom'];
523
+
524
+ // Cache this in a class variable as it's used rather a lot
525
+
526
+ /**
527
+ * Check for tooltips and alert the user that they're not supported
528
+ * with pyramid charts
529
+ */
530
+ if ( (prop['chart.variant'] == 'pyramid' || prop['chart.variant'] == 'dot')
531
+ && typeof(prop['chart.tooltips']) == 'object'
532
+ && prop['chart.tooltips']
533
+ && prop['chart.tooltips'].length > 0) {
534
+
535
+ alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts');
536
+ }
537
+
538
+ /**
539
+ * Stop the coords arrays from growing uncontrollably
540
+ */
541
+ this.coords = [];
542
+ this.coords2 = [];
543
+ this.coordsText = [];
544
+
545
+ /**
546
+ * Work out a few things. They need to be here because they depend on things you can change before you
547
+ * call Draw() but after you instantiate the object
548
+ */
549
+ this.max = 0;
550
+ this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
551
+ this.halfgrapharea = this.grapharea / 2;
552
+ this.halfTextHeight = prop['chart.text.size'] / 2;
553
+
554
+
555
+
556
+
557
+
558
+ // Now draw the background on to the main canvas
559
+ RG.background.Draw(this);
560
+
561
+
562
+
563
+
564
+ //If it's a sketch chart variant, draw the axes first
565
+ //if (prop['chart.variant'] == 'sketch') {
566
+ // this.DrawAxes();
567
+ // this.Drawbars();
568
+ //} else {
569
+ this.drawbars();
570
+ this.drawAxes();
571
+ //}
572
+
573
+ this.DrawLabels();
574
+
575
+
576
+ /**
577
+ * Draw the bevel if required
578
+ */
579
+ if (prop['chart.bevel'] || prop['chart.bevelled']) {
580
+ this.DrawBevel();
581
+ }
582
+
583
+
584
+ // Draw the key if necessary
585
+ if (prop['chart.key'] && prop['chart.key'].length) {
586
+ RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
587
+ }
588
+
589
+
590
+ /**
591
+ * Setup the context menu if required
592
+ */
593
+ if (prop['chart.contextmenu']) {
594
+ RG.ShowContext(this);
595
+ }
596
+
597
+
598
+
599
+
600
+ /**
601
+ * Draw errorbars
602
+ */
603
+ if (prop['chart.errorbars']) {
604
+ this.drawErrorbars();
605
+ }
606
+
607
+
608
+
609
+
610
+ /**
611
+ * Draw "in graph" labels
612
+ */
613
+ if (prop['chart.labels.ingraph']) {
614
+ RG.DrawInGraphLabels(this);
615
+ }
616
+
617
+
618
+ /**
619
+ * This function enables resizing
620
+ */
621
+ if (prop['chart.resizable']) {
622
+ RG.AllowResizing(this);
623
+ }
624
+
625
+
626
+ /**
627
+ * This installs the event listeners
628
+ */
629
+ RG.InstallEventListeners(this);
630
+
631
+
632
+ /**
633
+ * Fire the onfirstdraw event
634
+ */
635
+ if (this.firstDraw) {
636
+ RG.fireCustomEvent(this, 'onfirstdraw');
637
+ this.firstDraw = false;
638
+ this.firstDrawFunc();
639
+ }
640
+
641
+
642
+ /**
643
+ * Fire the RGraph ondraw event
644
+ */
645
+ RG.fireCustomEvent(this, 'ondraw');
646
+
647
+ return this;
648
+ };
649
+
650
+
651
+
652
+ /**
653
+ * Used in chaining. Runs a function there and then - not waiting for
654
+ * the events to fire (eg the onbeforedraw event)
655
+ *
656
+ * @param function func The function to execute
657
+ */
658
+ this.exec = function (func)
659
+ {
660
+ func(this);
661
+
662
+ return this;
663
+ };
664
+
665
+
666
+
667
+
668
+ /**
669
+ * Draws the charts axes
670
+ */
671
+ this.drawAxes =
672
+ this.DrawAxes = function ()
673
+ {
674
+ if (prop['chart.noaxes']) {
675
+ return;
676
+ }
677
+
678
+ var xaxispos = prop['chart.xaxispos'];
679
+ var yaxispos = prop['chart.yaxispos'];
680
+ var isSketch = prop['chart.variant'] == 'sketch';
681
+
682
+ co.beginPath();
683
+ co.strokeStyle = prop['chart.axis.color'];
684
+ co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
685
+
686
+
687
+ if (RG.ISSAFARI == -1) {
688
+ co.lineCap = 'square';
689
+ }
690
+
691
+
692
+ // Draw the Y axis
693
+ if (prop['chart.noyaxis'] == false) {
694
+ if (yaxispos == 'right') {
695
+ co.moveTo(ca.width - this.gutterRight + (isSketch ? 3 : 0), this.gutterTop - (isSketch ? 3 : 0));
696
+ co.lineTo(ca.width - this.gutterRight - (isSketch ? 2 : 0), ca.height - this.gutterBottom + (isSketch ? 5 : 0));
697
+ } else {
698
+ co.moveTo(this.gutterLeft - (isSketch ? 2 : 0), this.gutterTop - (isSketch ? 5 : 0));
699
+ co.lineTo(this.gutterLeft - (isSketch ? 1 : 0), ca.height - this.gutterBottom + (isSketch ? 5 : 0));
700
+ }
701
+ }
702
+
703
+ // Draw the X axis
704
+ if (prop['chart.noxaxis'] == false) {
705
+ if (xaxispos == 'center') {
706
+ co.moveTo(this.gutterLeft - (isSketch ? 5 : 0), Math.round(((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + (isSketch ? 2 : 0)));
707
+ co.lineTo(ca.width - this.gutterRight + (isSketch ? 5 : 0), Math.round(((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - (isSketch ? 2 : 0)));
708
+ } else if (xaxispos == 'top') {
709
+ co.moveTo(this.gutterLeft - (isSketch ? 3 : 0), this.gutterTop - (isSketch ? 3 : 0));
710
+ co.lineTo(ca.width - this.gutterRight + (isSketch ? 5 : 0), this.gutterTop + (isSketch ? 2 : 0));
711
+ } else {
712
+ co.moveTo(
713
+ this.gutterLeft - (isSketch ? 5 : 0),
714
+ ma.round(this.getYCoord(0) - (isSketch ? 2 : 0))
715
+ );
716
+ co.lineTo(
717
+ ca.width - this.gutterRight + (isSketch ? 8 : 0),
718
+ ma.round(this.getYCoord(0) + (isSketch ? 2 : 0))
719
+ );
720
+
721
+ }
722
+ }
723
+
724
+ var numYTicks = prop['chart.numyticks'];
725
+
726
+ //
727
+ // DRAW THE Y TICKMARKS
728
+ //
729
+ if (prop['chart.noyaxis'] == false && !isSketch) {
730
+
731
+ var yTickGap = (ca.height - this.gutterTop - this.gutterBottom) / numYTicks;
732
+ var xpos = yaxispos == 'left' ? this.gutterLeft : ca.width - this.gutterRight;
733
+
734
+ if (this.properties['chart.numyticks'] > 0) {
735
+ for (y=this.gutterTop;
736
+ xaxispos == 'center' ? y <= (ca.height - this.gutterBottom) : y < (ca.height - this.gutterBottom + (xaxispos == 'top' ? 1 : 0));
737
+ y += yTickGap) {
738
+
739
+ if (xaxispos == 'center' && y == (this.gutterTop + (this.grapharea / 2))) {
740
+ continue;
741
+ }
742
+
743
+ // X axis at the top
744
+ if (xaxispos == 'top' && y == this.gutterTop) {
745
+ continue;
746
+ }
747
+
748
+ co.moveTo(xpos + (yaxispos == 'left' ? 0 : 0), ma.round(y));
749
+ co.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), ma.round(y));
750
+ }
751
+
752
+ //
753
+ // If the X axis is offset (ie not at the bottom when xaxispos
754
+ // is set to bottom) - draw an extra tick
755
+ //
756
+ if (xaxispos === 'bottom' && prop['chart.ymin'] !== 0) {
757
+ co.moveTo(xpos + (yaxispos == 'left' ? 0 : 0), ma.round(ca.height - prop['chart.gutter.bottom']));
758
+ co.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), ma.round(ca.height - prop['chart.gutter.bottom']));
759
+ }
760
+ }
761
+
762
+ /**
763
+ * If the X axis is not being shown, draw an extra tick
764
+ */
765
+ if (prop['chart.noxaxis']) {
766
+ if (xaxispos == 'center') {
767
+ co.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(ca.height / 2));
768
+ co.lineTo(xpos, Math.round(ca.height / 2));
769
+ } else if (xaxispos == 'top') {
770
+ co.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(this.gutterTop));
771
+ co.lineTo(xpos, Math.round(this.gutterTop));
772
+ } else {
773
+ co.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(ca.height - this.gutterBottom));
774
+ co.lineTo(xpos, Math.round(ca.height - this.gutterBottom));
775
+ }
776
+ }
777
+ }
778
+
779
+
780
+ // Draw the X tickmarks
781
+ if (prop['chart.noxaxis'] == false && !isSketch) {
782
+
783
+ if (typeof(prop['chart.numxticks']) == 'number') {
784
+ var xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
785
+ } else {
786
+ var xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / this.data.length;
787
+ }
788
+
789
+ if (xaxispos == 'bottom') {
790
+ yStart = prop['chart.ymin'] < 0 ? this.getYCoord(0) - 3 : this.getYCoord(0);
791
+ yEnd = this.getYCoord(0) + 3;
792
+ } else if (xaxispos == 'top') {
793
+ yStart = this.gutterTop - 3;
794
+ yEnd = this.gutterTop;
795
+ } else if (xaxispos == 'center') {
796
+ yStart = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + 3;
797
+ yEnd = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - 3;
798
+ }
799
+
800
+ //yStart = yStart;
801
+ //yEnd = yEnd;
802
+
803
+ //////////////// X TICKS ////////////////
804
+ var noEndXTick = prop['chart.noendxtick'];
805
+
806
+ for (x=this.gutterLeft + (yaxispos == 'left' ? xTickGap : 0),len=(ca.width - this.gutterRight + (yaxispos == 'left' ? 5 : 0)); x<len; x+=xTickGap) {
807
+
808
+ if (yaxispos == 'left' && !noEndXTick && x > this.gutterLeft) {
809
+ co.moveTo(ma.round(x), yStart);
810
+ co.lineTo(ma.round(x), yEnd);
811
+
812
+ } else if (yaxispos == 'left' && noEndXTick && x > this.gutterLeft && x < (ca.width - this.gutterRight) ) {
813
+ co.moveTo(ma.round(x), yStart);
814
+ co.lineTo(ma.round(x), yEnd);
815
+
816
+ } else if (yaxispos == 'right' && x < (ca.width - this.gutterRight) && !noEndXTick) {
817
+ co.moveTo(ma.round(x), yStart);
818
+ co.lineTo(ma.round(x), yEnd);
819
+
820
+ } else if (yaxispos == 'right' && x < (ca.width - this.gutterRight) && x > (this.gutterLeft) && noEndXTick) {
821
+ co.moveTo(ma.round(x), yStart);
822
+ co.lineTo(ma.round(x), yEnd);
823
+ }
824
+ }
825
+
826
+ if (prop['chart.noyaxis'] || prop['chart.numxticks'] == null) {
827
+ if (typeof(prop['chart.numxticks']) == 'number' && prop['chart.numxticks'] > 0) {
828
+ co.moveTo(Math.round(this.gutterLeft), yStart);
829
+ co.lineTo(Math.round(this.gutterLeft), yEnd);
830
+ }
831
+ }
832
+
833
+ //////////////// X TICKS ////////////////
834
+ }
835
+
836
+ /**
837
+ * If the Y axis is not being shown, draw an extra tick
838
+ */
839
+ if (prop['chart.noyaxis'] && prop['chart.noxaxis'] == false && prop['chart.numxticks'] == null) {
840
+ if (xaxispos == 'center') {
841
+ co.moveTo(ma.round(this.gutterLeft), (ca.height / 2) - 3);
842
+ co.lineTo(ma.round(this.gutterLeft), (ca.height / 2) + 3);
843
+ } else {
844
+ co.moveTo(ma.round(this.gutterLeft), ca.height - this.gutterBottom);
845
+ co.lineTo(ma.round(this.gutterLeft), ca.height - this.gutterBottom + 3);
846
+ }
847
+ }
848
+
849
+ co.stroke();
850
+ };
851
+
852
+
853
+
854
+
855
+ /**
856
+ * Draws the bars
857
+ */
858
+ this.drawbars =
859
+ this.Drawbars = function ()
860
+ {
861
+ co.lineWidth = prop['chart.linewidth'];
862
+ co.strokeStyle = prop['chart.strokecolor'];
863
+ co.fillStyle = prop['chart.colors'][0];
864
+
865
+ var prevX = 0,
866
+ prevY = 0,
867
+ decimals = prop['chart.scale.decimals'];
868
+
869
+
870
+ /**
871
+ * Work out the max value
872
+ */
873
+ if (prop['chart.ymax']) {
874
+
875
+ this.scale2 = RG.getScale2(this, {
876
+ 'max':prop['chart.ymax'],
877
+ 'strict': prop['chart.scale.round'] ? false : true,
878
+ 'min':prop['chart.ymin'],
879
+ 'scale.thousand':prop['chart.scale.thousand'],
880
+ 'scale.point':prop['chart.scale.point'],
881
+ 'scale.decimals':prop['chart.scale.decimals'],
882
+ 'ylabels.count':prop['chart.ylabels.count'],
883
+ 'scale.round':prop['chart.scale.round'],
884
+ 'units.pre': prop['chart.units.pre'],
885
+ 'units.post': prop['chart.units.post']
886
+ });
887
+
888
+ } else {
889
+
890
+
891
+
892
+
893
+
894
+ //
895
+ // If errorbars are given as a number then convert the nuumber to an
896
+ // array.
897
+ //
898
+ var errorbars = prop['chart.errorbars'];
899
+
900
+ if (typeof errorbars === 'number') {
901
+
902
+ var value = errorbars;
903
+
904
+ prop['chart.errorbars'] = [];
905
+
906
+ for (var i=0; i<this.data.length; ++i) {
907
+ if (typeof this.data[i] === 'number') {
908
+ prop['chart.errorbars'].push([value, null]);
909
+
910
+ } else if (typeof this.data[i] === 'object' && !RG.isNull(this.data[i])) {
911
+ for (var j=0; j<this.data[i].length; ++j) {
912
+ prop['chart.errorbars'].push([value, null]);
913
+ }
914
+ }
915
+ }
916
+
917
+ errorbars = prop['chart.errorbars'];
918
+ }
919
+
920
+
921
+
922
+
923
+
924
+
925
+
926
+
927
+ for (i=0; i<this.data.length; ++i) {
928
+ if (typeof(this.data[i]) == 'object') {
929
+ var value = prop['chart.grouping'] === 'grouped' ? Number(RG.arrayMax(this.data[i], true)) : Number(RG.array_sum(this.data[i]));
930
+
931
+ } else {
932
+ var value = Number(this.data[i]);
933
+ }
934
+
935
+ this.max = ma.max(ma.abs(this.max), ma.abs(value) +
936
+
937
+ Number(
938
+ (
939
+ typeof prop['chart.errorbars'] === 'object'
940
+ && typeof prop['chart.errorbars'][i] === 'object'
941
+ && !RG.isNull(prop['chart.errorbars'][i])
942
+ && typeof prop['chart.errorbars'][i][0] === 'number'
943
+ ) ? prop['chart.errorbars'][i][0] : 0
944
+ )
945
+ );
946
+ }
947
+
948
+
949
+
950
+
951
+
952
+
953
+
954
+ this.scale2 = RGraph.getScale2(this, {
955
+ 'max':this.max,
956
+ 'min':prop['chart.ymin'],
957
+ 'scale.thousand':prop['chart.scale.thousand'],
958
+ 'scale.point':prop['chart.scale.point'],
959
+ 'scale.decimals':prop['chart.scale.decimals'],
960
+ 'ylabels.count':prop['chart.ylabels.count'],
961
+ 'scale.round':prop['chart.scale.round'],
962
+ 'units.pre': prop['chart.units.pre'],
963
+ 'units.post': prop['chart.units.post']
964
+ });
965
+
966
+ this.max = this.scale2.max;
967
+ }
968
+
969
+ /**
970
+ * if the chart is adjustable fix the scale so that it doesn't change.
971
+ */
972
+ if (prop['chart.adjustable'] && !prop['chart.ymax']) {
973
+ this.Set('chart.ymax', this.scale2.max);
974
+ }
975
+
976
+ /**
977
+ * Draw horizontal bars here
978
+ */
979
+ if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length > 0) {
980
+ RGraph.DrawBars(this);
981
+ }
982
+
983
+ var variant = prop['chart.variant'];
984
+
985
+ /**
986
+ * Draw the 3D axes is necessary
987
+ */
988
+ if (variant === '3d') {
989
+ RG.draw3DAxes(this);
990
+ }
991
+
992
+ /**
993
+ * Get the variant once, and draw the bars, be they regular, stacked or grouped
994
+ */
995
+
996
+ // Get these variables outside of the loop
997
+ var xaxispos = prop['chart.xaxispos'],
998
+ width = (ca.width - this.gutterLeft - this.gutterRight ) / this.data.length,
999
+ orig_height = height,
1000
+ hmargin = prop['chart.hmargin'],
1001
+ shadow = prop['chart.shadow'],
1002
+ shadowColor = prop['chart.shadow.color'],
1003
+ shadowBlur = prop['chart.shadow.blur'],
1004
+ shadowOffsetX = prop['chart.shadow.offsetx'],
1005
+ shadowOffsetY = prop['chart.shadow.offsety'],
1006
+ strokeStyle = prop['chart.strokecolor'],
1007
+ colors = prop['chart.colors'],
1008
+ sequentialColorIndex = 0
1009
+
1010
+ var height;
1011
+
1012
+ for (i=0,len=this.data.length; i<len; i+=1) {
1013
+
1014
+
1015
+
1016
+
1017
+
1018
+ // Work out the height
1019
+ //The width is up outside the loop
1020
+ if (RG.arraySum(this.data[i]) < 0) {
1021
+ var height = (RG.arraySum(this.data[i]) + this.scale2.min) / (this.scale2.max - this.scale2.min);
1022
+ } else {
1023
+ var height = (RG.arraySum(this.data[i]) - this.scale2.min) / (this.scale2.max - this.scale2.min);
1024
+ }
1025
+
1026
+ height *= ma.abs(this.getYCoord(this.scale2.max) - this.getYCoord(this.scale2.min));
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+
1033
+ var x = (i * width) + this.gutterLeft;
1034
+ var y = xaxispos == 'center' ? ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - height
1035
+ : ca.height - height - this.gutterBottom;
1036
+
1037
+ // xaxispos is top
1038
+ if (xaxispos == 'top') {
1039
+ y = this.gutterTop + ma.abs(height);
1040
+ }
1041
+
1042
+
1043
+ // Account for negative lengths - Some browsers don't like a negative value
1044
+ if (height < 0) {
1045
+ y += height;
1046
+ height = ma.abs(height);
1047
+ }
1048
+
1049
+
1050
+
1051
+
1052
+
1053
+
1054
+ /**
1055
+ * Turn on the shadow if need be
1056
+ */
1057
+ if (shadow) {
1058
+ co.shadowColor = shadowColor;
1059
+ co.shadowBlur = shadowBlur;
1060
+ co.shadowOffsetX = shadowOffsetX;
1061
+ co.shadowOffsetY = shadowOffsetY;
1062
+ }
1063
+
1064
+ /**
1065
+ * Draw the bar
1066
+ */
1067
+ co.beginPath();
1068
+ if (typeof this.data[i] == 'number') {
1069
+
1070
+
1071
+ // If the Y axis is offset change the bar start (the top of the bar)
1072
+ if (xaxispos === 'bottom' && prop['chart.ymin'] < 0) {
1073
+ if (this.data[i] >= 0) {
1074
+ height = ma.abs(this.getYCoord(0) - this.getYCoord(this.data[i]));
1075
+ } else {
1076
+ y = this.getYCoord(0);
1077
+ height = ma.abs(this.getYCoord(0) - this.getYCoord(this.data[i]));
1078
+ }
1079
+ }
1080
+
1081
+ var barWidth = width - (2 * hmargin);
1082
+
1083
+ /**
1084
+ * Check for a negative bar width
1085
+ */
1086
+ if (barWidth < 0) {
1087
+ alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');
1088
+ }
1089
+
1090
+ // Set the fill color
1091
+ co.strokeStyle = strokeStyle;
1092
+ co.fillStyle = colors[0];
1093
+
1094
+ /**
1095
+ * Sequential colors
1096
+ */
1097
+ if (prop['chart.colors.sequential']) {
1098
+ co.fillStyle = colors[i];
1099
+ }
1100
+
1101
+ if (variant == 'sketch') {
1102
+
1103
+ co.lineCap = 'round';
1104
+
1105
+ var sketchOffset = 3;
1106
+
1107
+ co.beginPath();
1108
+
1109
+ co.strokeStyle = colors[0];
1110
+
1111
+ /**
1112
+ * Sequential colors
1113
+ */
1114
+ if (prop['chart.colors.sequential']) {
1115
+ co.strokeStyle = colors[i];
1116
+ }
1117
+
1118
+ // Left side
1119
+ co.moveTo(x + hmargin + 2, y + height - 2);
1120
+ co.lineTo(x + hmargin - 1, y - 4);
1121
+
1122
+ // The top
1123
+ co.moveTo(x + hmargin - 3, y + -2 + (this.data[i] < 0 ? height : 0));
1124
+ co.bezierCurveTo(
1125
+ x + ((hmargin + width) * 0.33),
1126
+ y + 15 + (this.data[i] < 0 ? height - 10: 0),
1127
+ x + ((hmargin + width) * 0.66),
1128
+ y + 5 + (this.data[i] < 0 ? height - 10 : 0),x + hmargin + width + -1, y + 0 + (this.data[i] < 0 ? height : 0)
1129
+ );
1130
+
1131
+
1132
+ // The right side
1133
+ co.moveTo(x + hmargin + width - 5, y - 5);
1134
+ co.lineTo(x + hmargin + width - 3, y + height - 3);
1135
+
1136
+ if (prop['chart.variant.sketch.verticals']) {
1137
+ for (var r=0.2; r<=0.8; r+=0.2) {
1138
+ co.moveTo(x + hmargin + width + (r > 0.4 ? -1 : 3) - (r * width),y - 1);
1139
+ co.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2));
1140
+ }
1141
+ }
1142
+
1143
+ co.stroke();
1144
+
1145
+ // Regular bar
1146
+ } else if (variant == 'bar' || variant == '3d' || variant == 'glass' || variant == 'bevel') {
1147
+
1148
+ if (RGraph.ISOLD && shadow) {
1149
+ this.DrawIEShadow([x + hmargin, y, barWidth, height]);
1150
+ }
1151
+
1152
+ if (variant == 'glass') {
1153
+ RGraph.filledCurvyRect(co, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
1154
+ RGraph.strokedCurvyRect(co, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
1155
+ } else {
1156
+ // On 9th April 2013 these two were swapped around so that the stroke happens SECOND so that any
1157
+ // shadow that is cast by the fill does not overwrite the stroke
1158
+
1159
+ co.beginPath();
1160
+ co.rect(x + hmargin, y, barWidth, height);
1161
+ co.fill();
1162
+
1163
+ // Turn the shadow off so that the stroke doesn't cast any "extra" shadow
1164
+ // that would show inside the bar
1165
+ RG.NoShadow(this);
1166
+
1167
+ co.beginPath();
1168
+ co.rect(x + hmargin, y, barWidth, height);
1169
+ co.stroke();
1170
+ }
1171
+
1172
+ // 3D effect
1173
+ if (variant == '3d') {
1174
+
1175
+ var prevStrokeStyle = co.strokeStyle;
1176
+ var prevFillStyle = co.fillStyle;
1177
+
1178
+ // Draw the top (if the value is positive - otherwise there's no point)
1179
+ if (this.data[i] >= 0) {
1180
+ co.beginPath();
1181
+ co.moveTo(x + hmargin, y);
1182
+ co.lineTo(x + hmargin + prop['chart.variant.threed.offsetx'], y - prop['chart.variant.threed.offsety']);
1183
+ co.lineTo(x + hmargin + prop['chart.variant.threed.offsetx'] + barWidth, y - prop['chart.variant.threed.offsety']);
1184
+ co.lineTo(x + hmargin + barWidth, y);
1185
+ co.closePath();
1186
+
1187
+ co.stroke();
1188
+ co.fill();
1189
+ }
1190
+
1191
+ // Draw the right hand side
1192
+ co.beginPath();
1193
+ co.moveTo(x + hmargin + barWidth, y);
1194
+ co.lineTo(
1195
+ x + hmargin + barWidth + prop['chart.variant.threed.offsetx'],
1196
+ this.data[i] < 0 && xaxispos === 'bottom' ?
1197
+ this.getYCoord(0) : (
1198
+ this.data[i] < 0 && (y - prop['chart.variant.threed.offsety'])
1199
+ < (this.gutterTop + this.halfgrapharea)
1200
+
1201
+ ?
1202
+
1203
+ (this.gutterTop + this.halfgrapharea)
1204
+
1205
+ : (y - prop['chart.variant.threed.offsety']))
1206
+ );
1207
+
1208
+ co.lineTo(
1209
+ x + hmargin + barWidth + prop['chart.variant.threed.offsetx'],
1210
+
1211
+
1212
+ this.data[i] < 0 && (y - prop['chart.variant.threed.offsety'] + height) < (this.gutterTop + this.getYCoord(0))
1213
+ ? this.getYCoord(this.data[i]) - prop['chart.variant.threed.offsety']
1214
+ : (this.data[i] > 0 ?
1215
+ y - prop['chart.variant.threed.offsety'] + height :
1216
+ ma.min(y - prop['chart.variant.threed.offsety'] + height, ca.height - this.gutterBottom)
1217
+ )
1218
+ );
1219
+ co.lineTo(x + hmargin + barWidth, y + height);
1220
+ co.closePath();
1221
+ co.stroke();
1222
+ co.fill();
1223
+
1224
+
1225
+
1226
+
1227
+ // Draw the lighter top section
1228
+ if (this.data[i] > 0) {
1229
+ co.beginPath();
1230
+ co.fillStyle = 'rgba(255,255,255,0.5)';
1231
+ co.moveTo(x + hmargin, y);
1232
+ co.lineTo(x + hmargin + prop['chart.variant.threed.offsetx'], y - prop['chart.variant.threed.offsety']);
1233
+ co.lineTo(x + hmargin + prop['chart.variant.threed.offsetx'] + barWidth, y - prop['chart.variant.threed.offsety']);
1234
+ co.lineTo(x + hmargin + barWidth, y);
1235
+ co.lineTo(x + hmargin, y);
1236
+ co.closePath();
1237
+ co.stroke();
1238
+ co.fill();
1239
+ }
1240
+
1241
+
1242
+
1243
+
1244
+ // Draw the darker right side section
1245
+ co.beginPath();
1246
+ co.fillStyle = 'rgba(0,0,0,0.4)';
1247
+ // TL
1248
+ co.moveTo(x + hmargin + barWidth, y);
1249
+
1250
+ // TR
1251
+ co.lineTo(
1252
+ x + hmargin + barWidth + prop['chart.variant.threed.offsetx'],
1253
+ this.data[i] < 0 && xaxispos === 'bottom' ? this.getYCoord(0) : (this.data[i] < 0 && (y - prop['chart.variant.threed.offsety']) < (this.gutterTop + this.halfgrapharea) ? (this.gutterTop + this.halfgrapharea) : y - prop['chart.variant.threed.offsety'])
1254
+ );
1255
+
1256
+ // BR
1257
+ co.lineTo(
1258
+ x + hmargin + barWidth + prop['chart.variant.threed.offsetx'],
1259
+
1260
+ this.data[i] < 0 && (y - prop['chart.variant.threed.offsety'] + height) < this.getYCoord(0)
1261
+ ? this.getYCoord(0)
1262
+ : this.data[i] > 0 ? y - prop['chart.variant.threed.offsety'] + height : ma.min(y - prop['chart.variant.threed.offsety'] + height, ca.height - this.gutterBottom)
1263
+ );
1264
+ // BL
1265
+ co.lineTo(x + hmargin + barWidth, y + height);
1266
+ co.lineTo(x + hmargin + barWidth, y);
1267
+ co.closePath();
1268
+
1269
+ co.stroke();
1270
+ co.fill();
1271
+
1272
+ co.strokeStyle = prevStrokeStyle;
1273
+ co.fillStyle = prevFillStyle;
1274
+
1275
+ // Glass variant
1276
+ } else if (variant == 'glass') {
1277
+
1278
+ var grad = co.createLinearGradient(x + hmargin,y,x + hmargin + (barWidth / 2),y);
1279
+ grad.addColorStop(0, 'rgba(255,255,255,0.9)');
1280
+ grad.addColorStop(1, 'rgba(255,255,255,0.5)');
1281
+
1282
+ co.beginPath();
1283
+ co.fillStyle = grad;
1284
+ co.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2);
1285
+ co.fill();
1286
+ }
1287
+
1288
+
1289
+ // Dot chart
1290
+ } else if (variant == 'dot') {
1291
+
1292
+ co.beginPath();
1293
+ co.moveTo(x + (width / 2), y);
1294
+ co.lineTo(x + (width / 2), y + height);
1295
+ co.stroke();
1296
+
1297
+ co.beginPath();
1298
+ co.fillStyle = this.properties['chart.colors'][i];
1299
+ co.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0);
1300
+
1301
+ // Set the colour for the dots
1302
+ co.fillStyle = prop['chart.colors'][0];
1303
+
1304
+ /**
1305
+ * Sequential colors
1306
+ */
1307
+ if (prop['chart.colors.sequential']) {
1308
+ co.fillStyle = colors[i];
1309
+ }
1310
+
1311
+ co.stroke();
1312
+ co.fill();
1313
+
1314
+
1315
+
1316
+ // Unknown variant type
1317
+ } else {
1318
+ alert('[BAR] Warning! Unknown chart.variant: ' + variant);
1319
+ }
1320
+
1321
+ this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
1322
+
1323
+ if (typeof this.coords2[i] == 'undefined') {
1324
+ this.coords2[i] = [];
1325
+ }
1326
+ this.coords2[i].push([x + hmargin, y, width - (2 * hmargin), height]);
1327
+
1328
+
1329
+ /**
1330
+ * Stacked bar
1331
+ */
1332
+ } else if (this.data[i] && typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'stacked') {
1333
+
1334
+ if (this.scale2.min) {
1335
+ alert("[ERROR] Stacked Bar charts with a Y min are not supported");
1336
+ }
1337
+
1338
+ var barWidth = width - (2 * hmargin);
1339
+ var redrawCoords = [];// Necessary to draw if the shadow is enabled
1340
+ var startY = 0;
1341
+ var dataset = this.data[i];
1342
+
1343
+ /**
1344
+ * Check for a negative bar width
1345
+ */
1346
+ if (barWidth < 0) {
1347
+ alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');
1348
+ }
1349
+
1350
+ for (j=0; j<dataset.length; ++j) {
1351
+
1352
+ // Stacked bar chart and X axis pos in the middle - poitless since negative values are not permitted
1353
+ if (xaxispos == 'center') {
1354
+ alert("[BAR] It's pointless having the X axis position at the center on a stacked bar chart.");
1355
+ return;
1356
+ }
1357
+
1358
+ // Negative values not permitted for the stacked chart
1359
+ if (this.data[i][j] < 0) {
1360
+ alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');
1361
+ return;
1362
+ }
1363
+
1364
+ /**
1365
+ * Set the fill and stroke colors
1366
+ */
1367
+ co.strokeStyle = strokeStyle
1368
+ co.fillStyle = colors[j];
1369
+
1370
+ if (prop['chart.colors.reverse']) {
1371
+ co.fillStyle = colors[this.data[i].length - j - 1];
1372
+ }
1373
+
1374
+ if (prop['chart.colors.sequential'] && colors[sequentialColorIndex]) {
1375
+ co.fillStyle = colors[sequentialColorIndex++];
1376
+ } else if (prop['chart.colors.sequential']) {
1377
+ co.fillStyle = colors[sequentialColorIndex - 1];
1378
+ }
1379
+
1380
+ var height = (dataset[j] / this.scale2.max) * (ca.height - this.gutterTop - this.gutterBottom );
1381
+
1382
+ // If the X axis pos is in the center, we need to half the height
1383
+ if (xaxispos == 'center') {
1384
+ height /= 2;
1385
+ }
1386
+
1387
+ var totalHeight = (RGraph.array_sum(dataset) / this.scale2.max) * (ca.height - hmargin - this.gutterTop - this.gutterBottom);
1388
+
1389
+ /**
1390
+ * Store the coords for tooltips
1391
+ */
1392
+ this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
1393
+ if (typeof this.coords2[i] == 'undefined') {
1394
+ this.coords2[i] = [];
1395
+ }
1396
+ this.coords2[i].push([x + hmargin, y, width - (2 * hmargin), height]);
1397
+
1398
+ // MSIE shadow
1399
+ if (RGraph.ISOLD && shadow) {
1400
+ this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]);
1401
+ }
1402
+
1403
+ if (height > 0) {
1404
+ co.strokeRect(x + hmargin, y, width - (2 * hmargin), height);
1405
+ co.fillRect(x + hmargin, y, width - (2 * hmargin), height);
1406
+ }
1407
+
1408
+
1409
+ if (j == 0) {
1410
+ var startY = y;
1411
+ var startX = x;
1412
+ }
1413
+
1414
+ /**
1415
+ * Store the redraw coords if the shadow is enabled
1416
+ */
1417
+ if (shadow) {
1418
+ redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, co.fillStyle]);
1419
+ }
1420
+
1421
+ /**
1422
+ * Stacked 3D effect
1423
+ */
1424
+ if (variant == '3d') {
1425
+
1426
+ var prevFillStyle = co.fillStyle;
1427
+ var prevStrokeStyle = co.strokeStyle;
1428
+
1429
+
1430
+ // Draw the top side
1431
+ if (j == 0) {
1432
+ co.beginPath();
1433
+ co.moveTo(startX + hmargin, y);
1434
+ co.lineTo(startX + prop['chart.variant.threed.offsetx'] + hmargin, y - prop['chart.variant.threed.offsety']);
1435
+ co.lineTo(startX + prop['chart.variant.threed.offsetx'] + barWidth + hmargin, y - prop['chart.variant.threed.offsety']);
1436
+ co.lineTo(startX + barWidth + hmargin, y);
1437
+ co.closePath();
1438
+
1439
+ co.fill();
1440
+ co.stroke();
1441
+ }
1442
+
1443
+ // Draw the side section
1444
+ co.beginPath();
1445
+ co.moveTo(startX + barWidth + hmargin, y);
1446
+ co.lineTo(startX + barWidth + hmargin + prop['chart.variant.threed.offsetx'], y - prop['chart.variant.threed.offsety']);
1447
+ co.lineTo(startX + barWidth + hmargin + prop['chart.variant.threed.offsetx'], y - prop['chart.variant.threed.offsety'] + height);
1448
+ co.lineTo(startX + barWidth + hmargin , y + height);
1449
+ co.closePath();
1450
+
1451
+ co.fill();
1452
+ co.stroke();
1453
+
1454
+ // Draw the lighter top side
1455
+ if (j == 0) {
1456
+ co.fillStyle = 'rgba(255,255,255,0.5)';
1457
+ co.beginPath();
1458
+ co.moveTo(startX + hmargin, y);
1459
+ co.lineTo(startX + prop['chart.variant.threed.offsetx'] + hmargin, y - prop['chart.variant.threed.offsety']);
1460
+ co.lineTo(startX + prop['chart.variant.threed.offsetx'] + barWidth + hmargin, y - prop['chart.variant.threed.offsety']);
1461
+ co.lineTo(startX + barWidth + hmargin, y);
1462
+ co.closePath();
1463
+
1464
+ co.fill();
1465
+ co.stroke();
1466
+ }
1467
+
1468
+ // Draw the darker side section
1469
+ co.fillStyle = 'rgba(0,0,0,0.4)';
1470
+ co.beginPath();
1471
+ co.moveTo(startX + barWidth + hmargin, y);
1472
+ co.lineTo(startX + barWidth + hmargin + prop['chart.variant.threed.offsetx'], y - prop['chart.variant.threed.offsety']);
1473
+ co.lineTo(startX + barWidth + hmargin + prop['chart.variant.threed.offsetx'], y - prop['chart.variant.threed.offsety'] + height);
1474
+ co.lineTo(startX + barWidth + hmargin , y + height);
1475
+ co.closePath();
1476
+
1477
+ co.fill();
1478
+ co.stroke();
1479
+
1480
+ co.strokeStyle = prevStrokeStyle;
1481
+ co.fillStyle = prevFillStyle;
1482
+ }
1483
+
1484
+ y += height;
1485
+ }
1486
+
1487
+
1488
+
1489
+ /**
1490
+ * Redraw the bars if the shadow is enabled due to hem being drawn from the bottom up, and the
1491
+ * shadow spilling over to higher up bars
1492
+ */
1493
+ if (shadow) {
1494
+
1495
+ RGraph.NoShadow(this);
1496
+
1497
+ for (k=0; k<redrawCoords.length; ++k) {
1498
+ co.strokeStyle = strokeStyle;
1499
+ co.fillStyle = redrawCoords[k][4];
1500
+ co.strokeRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
1501
+ co.fillRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
1502
+
1503
+ co.stroke();
1504
+ co.fill();
1505
+ }
1506
+
1507
+ // Reset the redraw coords to be empty
1508
+ redrawCoords = [];
1509
+ }
1510
+
1511
+ /**
1512
+ * Grouped bar
1513
+ */
1514
+ } else if (this.data[i] && typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'grouped') {
1515
+
1516
+ var redrawCoords = [];
1517
+ co.lineWidth = prop['chart.linewidth'];
1518
+
1519
+ for (j=0; j<this.data[i].length; ++j) {
1520
+
1521
+ // Set the fill and stroke colors
1522
+ co.strokeStyle = strokeStyle;
1523
+ co.fillStyle = colors[j];
1524
+
1525
+ /**
1526
+ * Sequential colors
1527
+ */
1528
+ if (prop['chart.colors.sequential'] && colors[sequentialColorIndex]) {
1529
+ co.fillStyle = colors[sequentialColorIndex++];
1530
+ } else if (prop['chart.colors.sequential']) {
1531
+ co.fillStyle = colors[sequentialColorIndex - 1];
1532
+ }
1533
+
1534
+ var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length;
1535
+ var height = ((this.data[i][j] + (this.data[i][j] < 0 ? this.scale2.min : (-1 * this.scale2.min) )) / (this.scale2.max - this.scale2.min) ) * (ca.height - this.gutterTop - this.gutterBottom );
1536
+ var groupedMargin = prop['chart.hmargin.grouped'];
1537
+ var startX = x + hmargin + (j * individualBarWidth);
1538
+
1539
+ /**
1540
+ * Check for a negative bar width
1541
+ */
1542
+ if (individualBarWidth < 0) {
1543
+ alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');
1544
+ }
1545
+
1546
+ // If the X axis pos is in the center, we need to half the height
1547
+ if (xaxispos == 'center') {
1548
+ height /= 2;
1549
+ }
1550
+
1551
+ /**
1552
+ * Determine the start positioning for the bar
1553
+ */
1554
+ if (xaxispos == 'top') {
1555
+ var startY = this.gutterTop;
1556
+ var height = Math.abs(height);
1557
+
1558
+ } else if (xaxispos == 'center') {
1559
+ var startY = this.gutterTop + (this.grapharea / 2) - height;
1560
+
1561
+ } else {
1562
+ var startY = this.getYCoord(0);//ca.height - this.gutterBottom - height;
1563
+ var height = ma.abs(ma.abs(this.getYCoord(this.data[i][j])) - this.getYCoord(0));
1564
+
1565
+ if (this.data[i][j] >= 0) {
1566
+ startY -= height;
1567
+ }
1568
+
1569
+ }
1570
+
1571
+ co.strokeRect(startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height);
1572
+ co.fillRect(startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height);
1573
+ y += height;
1574
+
1575
+
1576
+
1577
+ /**
1578
+ * Grouped 3D effect
1579
+ */
1580
+ if (variant == '3d') {
1581
+
1582
+ var prevFillStyle = co.fillStyle;
1583
+ var prevStrokeStyle = co.strokeStyle;
1584
+ var hmarginGrouped = prop['chart.hmargin.grouped'];
1585
+
1586
+ // Draw the top side
1587
+ if (this.data[i][j] >= 0) {
1588
+
1589
+ co.beginPath();
1590
+ co.moveTo(startX + hmarginGrouped, startY);
1591
+ co.lineTo(startX + hmarginGrouped + prop['chart.variant.threed.offsetx'], startY - prop['chart.variant.threed.offsety']);
1592
+ co.lineTo(startX + prop['chart.variant.threed.offsetx'] + individualBarWidth - hmarginGrouped, startY - prop['chart.variant.threed.offsety']);
1593
+ co.lineTo(startX + individualBarWidth - hmarginGrouped, startY);
1594
+ co.closePath();
1595
+ co.fill();
1596
+ co.stroke();
1597
+ }
1598
+
1599
+ // Draw the side section
1600
+ co.beginPath();
1601
+ co.moveTo(startX + individualBarWidth - hmarginGrouped - 1, startY);
1602
+ co.lineTo(
1603
+ startX + individualBarWidth - hmarginGrouped + prop['chart.variant.threed.offsetx'],
1604
+ this.data[i][j] < 0 ? (this.getYCoord(0) + ma.abs(height) - prop['chart.variant.threed.offsety']) : this.getYCoord(0) - height - prop['chart.variant.threed.offsety']
1605
+ );
1606
+
1607
+ co.lineTo(
1608
+ startX + individualBarWidth - hmarginGrouped + prop['chart.variant.threed.offsetx'],
1609
+ this.data[i][j] < 0 && (startY + height - prop['chart.variant.threed.offsety']) < (this.gutterTop + this.halfgrapharea) ? (this.gutterTop + this.halfgrapharea) : (startY + height - prop['chart.variant.threed.offsety'])
1610
+ );
1611
+ co.lineTo(startX + individualBarWidth - hmarginGrouped - 1, startY + height);
1612
+ co.closePath();
1613
+ co.fill();
1614
+ co.stroke();
1615
+
1616
+
1617
+ // Draw the lighter top side - but only if the current value is positive
1618
+ if (this.data[i][j] >= 0) {
1619
+ co.fillStyle = 'rgba(255,255,255,0.5)';
1620
+ co.beginPath();
1621
+ // BL
1622
+ co.moveTo(startX + hmarginGrouped, startY);
1623
+
1624
+ // BR
1625
+ co.lineTo(startX + hmarginGrouped + prop['chart.variant.threed.offsetx'], startY - prop['chart.variant.threed.offsety']);
1626
+
1627
+ // TR
1628
+ co.lineTo(startX + prop['chart.variant.threed.offsetx'] + individualBarWidth - hmarginGrouped, startY - prop['chart.variant.threed.offsety']);
1629
+
1630
+ // TL
1631
+ co.lineTo(startX + individualBarWidth - hmarginGrouped, startY);
1632
+ co.closePath();
1633
+
1634
+ co.fill();
1635
+ co.stroke();
1636
+ }
1637
+
1638
+ // Draw the darker side section
1639
+ co.fillStyle = 'rgba(0,0,0,0.4)';
1640
+ co.beginPath();
1641
+ // TL corner
1642
+ co.moveTo(
1643
+ startX + individualBarWidth - hmarginGrouped,
1644
+ startY
1645
+ );
1646
+
1647
+
1648
+ co.lineTo(
1649
+ startX + individualBarWidth + prop['chart.variant.threed.offsetx'] - hmarginGrouped,
1650
+ this.data[i][j] < 0 ? (this.getYCoord(0) + ma.abs(height) - prop['chart.variant.threed.offsety']) : this.getYCoord(0) - height - prop['chart.variant.threed.offsety']
1651
+ );
1652
+
1653
+ // TR corner
1654
+ co.lineTo(
1655
+ startX + individualBarWidth + prop['chart.variant.threed.offsetx'] - hmarginGrouped,
1656
+ this.data[i][j] < 0 && (startY + height - 5) < (this.gutterTop + this.halfgrapharea) ? (this.gutterTop + this.halfgrapharea) : (startY + height - prop['chart.variant.threed.offsety'])
1657
+ );
1658
+
1659
+ // TL corner
1660
+ co.lineTo(startX + individualBarWidth - hmarginGrouped, startY + height);
1661
+ co.closePath();
1662
+
1663
+ co.fill();
1664
+ co.stroke();
1665
+
1666
+ co.strokeStyle = prevStrokeStyle;
1667
+ co.fillStyle = prevFillStyle;
1668
+ }
1669
+
1670
+ if (height < 0) {
1671
+ height = Math.abs(height);
1672
+ startY = startY - height;
1673
+ }
1674
+
1675
+ this.coords.push([startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height]);
1676
+ if (typeof this.coords2[i] == 'undefined') {
1677
+ this.coords2[i] = [];
1678
+ }
1679
+
1680
+ this.coords2[i].push([startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height]);
1681
+
1682
+ // Facilitate shadows going to the left
1683
+ if (prop['chart.shadow']) {
1684
+ redrawCoords.push([startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height, co.fillStyle]);
1685
+ }
1686
+ }
1687
+
1688
+
1689
+
1690
+
1691
+
1692
+
1693
+
1694
+ /**
1695
+ * Redraw the bar if shadows are going to the left
1696
+ */
1697
+ if (redrawCoords.length) {
1698
+
1699
+ RGraph.NoShadow(this);
1700
+
1701
+ co.lineWidth = prop['chart.linewidth'];
1702
+
1703
+ co.beginPath();
1704
+ for (var j=0; j<redrawCoords.length; ++j) {
1705
+
1706
+ co.fillStyle = redrawCoords[j][4];
1707
+ co.strokeStyle = prop['chart.strokecolor'];
1708
+
1709
+ co.fillRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
1710
+ co.strokeRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
1711
+ }
1712
+ co.fill();
1713
+ co.stroke();
1714
+
1715
+ redrawCoords = [];
1716
+ }
1717
+ } else {
1718
+ this.coords.push([]);
1719
+ }
1720
+
1721
+ co.closePath();
1722
+ }
1723
+
1724
+ // If 3D, redraw the right hand Y axis
1725
+ if (prop['chart.variant'] === '3d' && prop['chart.yaxispos'] === 'right') {
1726
+ RG.draw3DYAxis(this);
1727
+ }
1728
+
1729
+
1730
+
1731
+
1732
+
1733
+ /**
1734
+ * Turn off any shadow
1735
+ */
1736
+ RGraph.noShadow(this);
1737
+ };
1738
+
1739
+
1740
+
1741
+ /**
1742
+ * Draws the labels for the graph
1743
+ */
1744
+ this.drawLabels =
1745
+ this.DrawLabels = function ()
1746
+ {
1747
+ var context = co;
1748
+
1749
+ var text_angle = prop['chart.text.angle'],
1750
+ text_size = prop['chart.text.size'],
1751
+ labels = prop['chart.labels']
1752
+
1753
+
1754
+
1755
+ // Draw the Y axis labels:
1756
+ if (prop['chart.ylabels']) {
1757
+ if (prop['chart.xaxispos'] == 'top') this.Drawlabels_top();
1758
+ if (prop['chart.xaxispos'] == 'center') this.Drawlabels_center();
1759
+ if (prop['chart.xaxispos'] == 'bottom') this.Drawlabels_bottom();
1760
+ }
1761
+
1762
+ /**
1763
+ * The X axis labels
1764
+ */
1765
+ if (typeof(labels) == 'object' && labels) {
1766
+
1767
+ var yOffset = Number(prop['chart.labels.offsety']),
1768
+ xOffset = Number(prop['chart.labels.offsetx']),
1769
+ bold = prop['chart.labels.bold']
1770
+
1771
+ /**
1772
+ * Text angle
1773
+ */
1774
+ if (prop['chart.text.angle'] != 0) {
1775
+ var valign = 'center';
1776
+ var halign = 'right';
1777
+ var angle = 0 - prop['chart.text.angle'];
1778
+ } else {
1779
+ var valign = 'top';
1780
+ var halign = 'center';
1781
+ var angle = 0;
1782
+ }
1783
+
1784
+ // Draw the X axis labels
1785
+ co.fillStyle = prop['chart.labels.color'] || prop['chart.text.color'];
1786
+
1787
+ // How wide is each bar
1788
+ var barWidth = (ca.width - this.gutterRight - this.gutterLeft) / labels.length;
1789
+
1790
+ // Reset the xTickGap
1791
+ xTickGap = (ca.width - this.gutterRight - this.gutterLeft) / labels.length
1792
+
1793
+ // Draw the X tickmarks
1794
+ var i=0;
1795
+ var font = prop['chart.text.font'];
1796
+
1797
+ for (x=this.gutterLeft + (xTickGap / 2); x<=ca.width - this.gutterRight; x+=xTickGap) {
1798
+
1799
+ RG.text2(this, {
1800
+ 'font': font,
1801
+ 'size': text_size,
1802
+ 'x': x + xOffset,
1803
+ 'y': prop['chart.xaxispos'] == 'top' ? this.gutterTop + yOffset - 5: (ca.height - this.gutterBottom) + yOffset + 3,
1804
+ 'bold': bold,
1805
+ 'text': String(labels[i++]),
1806
+ 'valign': prop['chart.xaxispos'] == 'top' ? 'bottom' : valign,
1807
+ 'halign': halign,
1808
+ 'tag':'label',
1809
+ 'marker':false,
1810
+ 'angle':angle,
1811
+ 'tag': 'labels'
1812
+ });
1813
+ }
1814
+ }
1815
+
1816
+ /**
1817
+ * Draw above labels
1818
+ */
1819
+ this.drawAboveLabels();
1820
+ };
1821
+
1822
+
1823
+
1824
+ /**
1825
+ * Draws the X axis at the top
1826
+ */
1827
+ this.drawlabels_top =
1828
+ this.Drawlabels_top = function ()
1829
+ {
1830
+ var ca = this.canvas;
1831
+ var co = this.context;
1832
+ var prop = this.properties;
1833
+
1834
+ co.beginPath();
1835
+ co.fillStyle = prop['chart.text.color'];
1836
+ co.strokeStyle = 'black';
1837
+
1838
+ if (prop['chart.xaxispos'] == 'top') {
1839
+
1840
+ var context = co;
1841
+ var text_size = prop['chart.text.size'];
1842
+ var units_pre = prop['chart.units.pre'];
1843
+ var units_post = prop['chart.units.post'];
1844
+ var align = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1845
+ var font = prop['chart.text.font'];
1846
+ var numYLabels = prop['chart.ylabels.count'];
1847
+ var ymin = prop['chart.ymin'];
1848
+ var offsetx = prop['chart.ylabels.offsetx'];
1849
+ var offsety = prop['chart.ylabels.offsety'];
1850
+
1851
+ if (prop['chart.ylabels.inside'] == true) {
1852
+ var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : ca.width - this.gutterRight - 5;
1853
+ var align = prop['chart.yaxispos'] == 'left' ? 'left' : 'right';
1854
+ var boxed = true;
1855
+ } else {
1856
+ var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1857
+ var boxed = false;
1858
+ }
1859
+
1860
+ /**
1861
+ * Draw specific Y labels here so that the local variables can be reused
1862
+ */
1863
+ if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific']) {
1864
+
1865
+ var labels = RGraph.array_reverse(prop['chart.ylabels.specific']);
1866
+ var grapharea = ca.height - this.gutterTop - this.gutterBottom;
1867
+
1868
+ for (var i=0; i<labels.length; ++i) {
1869
+
1870
+ var y = this.gutterTop + (grapharea * (i / labels.length)) + (grapharea / labels.length);
1871
+
1872
+ RG.text2(this, {
1873
+ 'font': font,
1874
+ 'size': text_size,
1875
+ 'x': xpos + offsetx,
1876
+ 'y': y + offsety,
1877
+ 'text': String(labels[i]),
1878
+ 'valign': 'center',
1879
+ 'halign': align,
1880
+ 'bordered':boxed,
1881
+ 'tag': 'scale'
1882
+ });
1883
+ }
1884
+
1885
+ return;
1886
+ }
1887
+
1888
+
1889
+
1890
+
1891
+
1892
+
1893
+
1894
+ /**
1895
+ * Draw the scale
1896
+ */
1897
+ var labels = this.scale2.labels;
1898
+ for (var i=0; i<labels.length; ++i) {
1899
+ RGraph.Text2(this, {
1900
+ 'font': font,
1901
+ 'size':text_size,
1902
+ 'x':xpos + offsetx,
1903
+ 'y':this.gutterTop + ((this.grapharea / labels.length) * (i + 1)) + offsety,
1904
+ 'text': '-' + labels[i],
1905
+ 'valign': 'center',
1906
+ 'halign': align,
1907
+ 'bordered': boxed,
1908
+ 'tag':'scale'
1909
+ });
1910
+ }
1911
+
1912
+
1913
+
1914
+
1915
+
1916
+
1917
+
1918
+
1919
+ /**
1920
+ * Show the minimum value if its not zero
1921
+ */
1922
+ if (prop['chart.ymin'] != 0 || prop['chart.noxaxis'] || prop['chart.scale.zerostart']) {
1923
+
1924
+ RGraph.Text2(this, {
1925
+ 'font': font,
1926
+ 'size': text_size,
1927
+ 'x': xpos + offsetx,
1928
+ 'y': this.gutterTop + offsety,
1929
+ 'text': (this.scale2.min != 0 ? '-' : '') + RGraph.numberFormat(this,(this.scale2.min.toFixed((this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']))), units_pre, units_post),
1930
+ 'valign': 'center',
1931
+ 'halign': align,
1932
+ 'bordered': boxed,
1933
+ 'tag': 'scale'
1934
+ });
1935
+ }
1936
+
1937
+ }
1938
+
1939
+ co.fill();
1940
+ };
1941
+
1942
+
1943
+
1944
+ /**
1945
+ * Draws the X axis in the middle
1946
+ */
1947
+ this.drawlabels_center =
1948
+ this.Drawlabels_center = function ()
1949
+ {
1950
+ var ca = this.canvas;
1951
+ var co = this.context;
1952
+ var prop = this.properties;
1953
+
1954
+ var font = prop['chart.text.font'];
1955
+ var numYLabels = prop['chart.ylabels.count'];
1956
+
1957
+ co.fillStyle = prop['chart.text.color'];
1958
+
1959
+ if (prop['chart.xaxispos'] == 'center') {
1960
+
1961
+ /**
1962
+ * Draw the top labels
1963
+ */
1964
+ var text_size = prop['chart.text.size'];
1965
+ var units_pre = prop['chart.units.pre'];
1966
+ var units_post = prop['chart.units.post'];
1967
+ var context = co;
1968
+ var align = '';
1969
+ var xpos = 0;
1970
+ var boxed = false;
1971
+ var ymin = prop['chart.ymin'];
1972
+ var offsetx = prop['chart.ylabels.offsetx'];
1973
+ var offsety = prop['chart.ylabels.offsety'];
1974
+
1975
+ co.fillStyle = prop['chart.text.color'];
1976
+ co.strokeStyle = 'black';
1977
+
1978
+ if (prop['chart.ylabels.inside'] == true) {
1979
+ var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : ca.width - this.gutterRight - 5;
1980
+ var align = prop['chart.yaxispos'] == 'left' ? 'left' : 'right';
1981
+ var boxed = true;
1982
+ } else {
1983
+ var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1984
+ var align = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1985
+ var boxed = false;
1986
+ }
1987
+
1988
+
1989
+
1990
+
1991
+
1992
+
1993
+
1994
+
1995
+
1996
+
1997
+
1998
+
1999
+ /**
2000
+ * Draw specific Y labels here so that the local variables can be reused
2001
+ */
2002
+ if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific']) {
2003
+
2004
+ var labels = prop['chart.ylabels.specific'];
2005
+ var grapharea = ca.height - this.gutterTop - this.gutterBottom;
2006
+
2007
+ // Draw the top halves labels
2008
+ for (var i=0; i<labels.length; ++i) {
2009
+
2010
+ var y = this.gutterTop + ((grapharea / 2) / (labels.length - 1)) * i;
2011
+
2012
+ RGraph.Text2(this, {
2013
+ 'font':font,
2014
+ 'size':text_size,
2015
+ 'x':xpos + offsetx,
2016
+ 'y':y + offsety,
2017
+ 'text':String(labels[i]),
2018
+ 'valign':'center',
2019
+ 'halign':align,
2020
+ 'bordered':boxed,
2021
+ 'tag': 'scale'
2022
+ });
2023
+ }
2024
+
2025
+ // Draw the bottom halves labels
2026
+ for (var i=labels.length-1; i>=1; --i) {
2027
+
2028
+ var y = this.gutterTop + (grapharea * (i / ((labels.length - 1) * 2) )) + (grapharea / 2);
2029
+
2030
+ RG.Text2(this, {
2031
+ 'font':font,
2032
+ 'size':text_size,
2033
+ 'x':xpos + offsetx,
2034
+ 'y':y + offsety,
2035
+ 'text':String(labels[labels.length - i - 1]),
2036
+ 'valign':'center',
2037
+ 'halign':align,
2038
+ 'bordered':boxed,
2039
+ 'tag': 'scale'
2040
+ });
2041
+ }
2042
+
2043
+ return;
2044
+ }
2045
+
2046
+
2047
+
2048
+
2049
+
2050
+
2051
+
2052
+
2053
+
2054
+
2055
+ /**
2056
+ * Draw the top halfs labels
2057
+ */
2058
+ for (var i=0; i<this.scale2.labels.length; ++i) {
2059
+ var y = this.gutterTop + this.halfgrapharea - ((this.halfgrapharea / numYLabels) * (i + 1));
2060
+ var text = this.scale2.labels[i];
2061
+ RG.Text2(this, {
2062
+ 'font':font,
2063
+ 'size':text_size,
2064
+ 'x':xpos + offsetx,
2065
+ 'y':y + offsety,
2066
+ 'text':
2067
+ text,
2068
+ 'valign':
2069
+ 'center',
2070
+ 'halign': align,
2071
+ 'bordered': boxed,
2072
+ 'tag':'scale'
2073
+ });
2074
+ }
2075
+
2076
+ /**
2077
+ * Draw the bottom halfs labels
2078
+ */
2079
+ for (var i=(this.scale2.labels.length - 1); i>=0; --i) {
2080
+ var y = this.gutterTop + ((this.halfgrapharea / numYLabels) * (i + 1)) + this.halfgrapharea;
2081
+ var text = this.scale2.labels[i];
2082
+ RG.Text2(this, {
2083
+ 'font':font,
2084
+ 'size':text_size,
2085
+ 'x':xpos + offsetx,
2086
+ 'y':y + offsety,
2087
+ 'text': '-' + text,
2088
+ 'valign':'center',
2089
+ 'halign': align,
2090
+ 'bordered': boxed,
2091
+ 'tag':'scale'
2092
+ });
2093
+ }
2094
+
2095
+
2096
+
2097
+
2098
+
2099
+ /**
2100
+ * Show the minimum value if its not zero
2101
+ */
2102
+ if (this.scale2.min != 0 || prop['chart.scale.zerostart']) {
2103
+ RG.Text2(this, {
2104
+ 'font':font,
2105
+ 'size':text_size,
2106
+ 'x':xpos + offsetx,
2107
+ 'y':this.gutterTop + this.halfgrapharea + offsety,
2108
+ 'text': RG.number_format(this,(this.scale2.min.toFixed((this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']))), units_pre, units_post),
2109
+ 'valign':'center',
2110
+ 'valign':'center',
2111
+ 'halign': align,
2112
+ 'bordered': boxed,
2113
+ 'tag':'scale'
2114
+ });
2115
+ }
2116
+ }
2117
+ };
2118
+
2119
+
2120
+
2121
+
2122
+ /**
2123
+ * Draws the X axdis at the bottom (the default)
2124
+ */
2125
+ this.drawlabels_bottom =
2126
+ this.Drawlabels_bottom = function ()
2127
+ {
2128
+ var text_size = prop['chart.text.size'],
2129
+ units_pre = prop['chart.units.pre'],
2130
+ units_post = prop['chart.units.post'],
2131
+ context = this.context,
2132
+ align = prop['chart.yaxispos'] == 'left' ? 'right' : 'left',
2133
+ font = prop['chart.text.font'],
2134
+ numYLabels = prop['chart.ylabels.count'],
2135
+ ymin = prop['chart.ymin'],
2136
+ offsetx = prop['chart.ylabels.offsetx'],
2137
+ offsety = prop['chart.ylabels.offsety']
2138
+
2139
+ co.beginPath();
2140
+
2141
+ co.fillStyle = prop['chart.text.color'];
2142
+ co.strokeStyle = 'black';
2143
+
2144
+ if (prop['chart.ylabels.inside'] == true) {
2145
+ var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : ca.width - this.gutterRight - 5;
2146
+ var align = prop['chart.yaxispos'] == 'left' ? 'left' : 'right';
2147
+ var boxed = true;
2148
+ } else {
2149
+ var xpos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
2150
+ var boxed = false;
2151
+ }
2152
+
2153
+ /**
2154
+ * Draw specific Y labels here so that the local variables can be reused
2155
+ */
2156
+ if (prop['chart.ylabels.specific'] && typeof(prop['chart.ylabels.specific']) == 'object') {
2157
+
2158
+ var labels = prop['chart.ylabels.specific'];
2159
+ var grapharea = ca.height - this.gutterTop - this.gutterBottom;
2160
+
2161
+ for (var i=0; i<labels.length; ++i) {
2162
+ var y = this.gutterTop + (grapharea * (i / (labels.length - 1)));
2163
+
2164
+ RGraph.Text2(this, {
2165
+ 'font':font,
2166
+ 'size':text_size,
2167
+ 'x':xpos + offsetx,
2168
+ 'y':y + offsety,
2169
+ 'text': labels[i],
2170
+ 'valign':'center',
2171
+ 'halign': align,
2172
+ 'bordered': boxed,
2173
+ 'tag':'scale'
2174
+ });
2175
+ }
2176
+
2177
+ return;
2178
+ }
2179
+
2180
+ var gutterTop = this.gutterTop;
2181
+ var halfTextHeight = this.halfTextHeight;
2182
+ var scale = this.scale;
2183
+
2184
+
2185
+ for (var i=0; i<numYLabels; ++i) {
2186
+ var text = this.scale2.labels[i];
2187
+ RGraph.Text2(this, {
2188
+ 'font':font,
2189
+ 'size':text_size,
2190
+ 'x':xpos + offsetx,
2191
+ 'y':this.gutterTop + this.grapharea - ((this.grapharea / numYLabels) * (i+1)) + offsety,
2192
+ 'text': text,
2193
+ 'valign':'center',
2194
+ 'halign': align,
2195
+ 'bordered': boxed,
2196
+ 'tag':'scale'
2197
+ });
2198
+ }
2199
+
2200
+
2201
+ /**
2202
+ * Show the minimum value if its not zero
2203
+ */
2204
+ if (prop['chart.ymin'] != 0 || prop['chart.noxaxis'] || prop['chart.scale.zerostart']) {
2205
+ RG.text2(this, {
2206
+ font:font,
2207
+ size:text_size,
2208
+ x:xpos + offsetx,
2209
+ y:ca.height - this.gutterBottom + offsety,
2210
+ text: RG.numberFormat(this,(this.scale2.min.toFixed((this.scale2.min === 0 ? 0 : prop['chart.scale.decimals']))), units_pre, units_post),
2211
+ valign:'center',
2212
+ halign: align,
2213
+ bordered: boxed,
2214
+ tag:'scale'
2215
+ });
2216
+ }
2217
+
2218
+ co.fill();
2219
+ };
2220
+
2221
+
2222
+ /**
2223
+ * This function is used by MSIE only to manually draw the shadow
2224
+ *
2225
+ * @param array coords The coords for the bar
2226
+ */
2227
+ this.drawIEShadow =
2228
+ this.DrawIEShadow = function (coords)
2229
+ {
2230
+ var co = this.context;
2231
+ var ca = this.canvas;
2232
+ var prop = this.properties;
2233
+
2234
+ var prevFillStyle = co.fillStyle;
2235
+ var offsetx = prop['chart.shadow.offsetx'];
2236
+ var offsety = prop['chart.shadow.offsety'];
2237
+
2238
+ co.lineWidth = prop['chart.linewidth'];
2239
+ co.fillStyle = prop['chart.shadow.color'];
2240
+ co.beginPath();
2241
+
2242
+ // Draw shadow here
2243
+ co.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]);
2244
+
2245
+ co.fill();
2246
+
2247
+ // Change the fillstyle back to what it was
2248
+ co.fillStyle = prevFillStyle;
2249
+ };
2250
+
2251
+
2252
+
2253
+
2254
+ /**
2255
+ * Not used by the class during creating the graph, but is used by event handlers
2256
+ * to get the coordinates (if any) of the selected bar
2257
+ *
2258
+ * @param object e The event object
2259
+ * @param object OPTIONAL You can pass in the bar object instead of the
2260
+ * function using "this"
2261
+ */
2262
+ this.getShape =
2263
+ this.getBar = function (e)
2264
+ {
2265
+ // This facilitates you being able to pass in the bar object as a parameter instead of
2266
+ // the function getting it from itself
2267
+ var obj = arguments[1] ? arguments[1] : this;
2268
+
2269
+ var mouseXY = RG.getMouseXY(e),
2270
+ mouseX = mouseXY[0],
2271
+ mouseY = mouseXY[1],
2272
+ canvas = obj.canvas,
2273
+ context = obj.context,
2274
+ coords = obj.coords
2275
+
2276
+ for (var i=0,len=coords.length; i<len; i+=1) {
2277
+
2278
+ if (obj.coords[i].length == 0) {
2279
+ continue;
2280
+ }
2281
+
2282
+ var left = coords[i][0],
2283
+ top = coords[i][1],
2284
+ width = coords[i][2],
2285
+ height = coords[i][3],
2286
+ prop = obj.properties
2287
+
2288
+ // Old way of testing
2289
+ //if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height)) {
2290
+
2291
+ // Recreate the path/rectangle so that it can be tested
2292
+ // ** DO NOT STROKE OR FILL IT **
2293
+ if (prop['chart.tooltips.hotspot.xonly']) {
2294
+ pa2(co,
2295
+ 'b r % % % %',
2296
+ left,
2297
+ this.gutterTop,
2298
+ width,
2299
+ ca.height - this.gutterBottom
2300
+ );
2301
+ } else {
2302
+ pa2(co,
2303
+ 'b r % % % %',
2304
+ left,
2305
+ top,
2306
+ width,
2307
+ height
2308
+ );
2309
+ }
2310
+
2311
+ if (co.isPointInPath(mouseX, mouseY)) {
2312
+
2313
+
2314
+ if (prop['chart.tooltips']) {
2315
+ var tooltip = RG.parseTooltipText ? RG.parseTooltipText(prop['chart.tooltips'], i) : prop['chart.tooltips'][i];
2316
+ }
2317
+
2318
+ // Work out the dataset
2319
+ var dataset = 0,
2320
+ idx = i
2321
+
2322
+ while (idx >= (typeof obj.data[dataset] === 'object' && obj.data[dataset] ? obj.data[dataset].length : 1)) {
2323
+
2324
+ if (typeof obj.data[dataset] === 'number') {
2325
+ idx -= 1;
2326
+ } else if (obj.data[dataset]) { // Accounts for null being an object
2327
+ idx -= obj.data[dataset].length;
2328
+ } else {
2329
+ idx -= 1;
2330
+ }
2331
+
2332
+ dataset++;
2333
+ }
2334
+
2335
+ if (typeof(obj.data[dataset]) == 'number') {
2336
+ idx = null;
2337
+ }
2338
+
2339
+
2340
+ return {
2341
+ 0: obj, 1: left, 2: top, 3: width, 4: height, 5: i,
2342
+ 'object': obj, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip, 'index_adjusted': idx, 'dataset': dataset
2343
+ };
2344
+ }
2345
+ }
2346
+
2347
+ return null;
2348
+ };
2349
+
2350
+
2351
+
2352
+
2353
+ /**
2354
+ * This retrives the bar based on the X coordinate only.
2355
+ *
2356
+ * @param object e The event object
2357
+ * @param object OPTIONAL You can pass in the bar object instead of the
2358
+ * function using "this"
2359
+ */
2360
+ this.getShapeByX = function (e)
2361
+ {
2362
+ var canvas = e.target;
2363
+ var mouseCoords = RGraph.getMouseXY(e);
2364
+
2365
+
2366
+ // This facilitates you being able to pass in the bar object as a parameter instead of
2367
+ // the function getting it from itself
2368
+ var obj = arguments[1] ? arguments[1] : this;
2369
+
2370
+
2371
+ /**
2372
+ * Loop through the bars determining if the mouse is over a bar
2373
+ */
2374
+ for (var i=0,len=obj.coords.length; i<len; i++) {
2375
+
2376
+ if (obj.coords[i].length == 0) {
2377
+ continue;
2378
+ }
2379
+
2380
+ var mouseX = mouseCoords[0];
2381
+ var mouseY = mouseCoords[1];
2382
+ var left = obj.coords[i][0];
2383
+ var top = obj.coords[i][1];
2384
+ var width = obj.coords[i][2];
2385
+ var height = obj.coords[i][3];
2386
+ var prop = obj.properties;
2387
+
2388
+ if (mouseX >= left && mouseX <= (left + width)) {
2389
+
2390
+ if (prop['chart.tooltips']) {
2391
+ var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(prop['chart.tooltips'], i) : prop['chart.tooltips'][i];
2392
+ }
2393
+
2394
+
2395
+
2396
+ return {
2397
+ 0: obj, 1: left, 2: top, 3: width, 4: height, 5: i,
2398
+ 'object': obj, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip
2399
+ };
2400
+ }
2401
+ }
2402
+
2403
+ return null;
2404
+ };
2405
+
2406
+
2407
+
2408
+
2409
+ /**
2410
+ * When you click on the chart, this method can return the Y value at that point. It works for any point on the
2411
+ * chart (that is inside the gutters) - not just points within the Bars.
2412
+ *
2413
+ * EITHER:
2414
+ *
2415
+ * @param object arg The event object
2416
+ *
2417
+ * OR:
2418
+ *
2419
+ * @param object arg A two element array containing the X and Y coordinates
2420
+ */
2421
+ this.getValue = function (arg)
2422
+ {
2423
+ var co = this.context;
2424
+ var ca = this.canvas;
2425
+ var prop = this.properties;
2426
+
2427
+ if (arg.length == 2) {
2428
+ var mouseX = arg[0];
2429
+ var mouseY = arg[1];
2430
+ } else {
2431
+ var mouseCoords = RG.getMouseXY(arg);
2432
+ var mouseX = mouseCoords[0];
2433
+ var mouseY = mouseCoords[1];
2434
+ }
2435
+
2436
+ if ( mouseY < prop['chart.gutter.top']
2437
+ || mouseY > (ca.height - prop['chart.gutter.bottom'])
2438
+ || mouseX < prop['chart.gutter.left']
2439
+ || mouseX > (ca.width - prop['chart.gutter.right'])
2440
+ ) {
2441
+ return null;
2442
+ }
2443
+
2444
+ if (prop['chart.xaxispos'] == 'center') {
2445
+ var value = (((this.grapharea / 2) - (mouseY - prop['chart.gutter.top'])) / this.grapharea) * (this.scale2.max - this.scale2.min)
2446
+ value *= 2;
2447
+
2448
+ if (value >= 0) {
2449
+ value += this.scale2.min;
2450
+ } else {
2451
+ value -= this.scale2.min;
2452
+ }
2453
+
2454
+ } else if (prop['chart.xaxispos'] == 'top') {
2455
+ var value = ((this.grapharea - (mouseY - prop['chart.gutter.top'])) / this.grapharea) * (this.scale2.max - this.scale2.min)
2456
+ value = this.scale2.max - value;
2457
+ value = ma.abs(value) * -1;
2458
+ } else {
2459
+ var value = ((this.grapharea - (mouseY - prop['chart.gutter.top'])) / this.grapharea) * (this.scale2.max - this.scale2.min)
2460
+ value += this.scale2.min;
2461
+ }
2462
+
2463
+
2464
+
2465
+
2466
+ return value;
2467
+ };
2468
+
2469
+
2470
+ /**
2471
+ * This function can be used when the canvas is clicked on (or similar - depending on the event)
2472
+ * to retrieve the relevant Y coordinate for a particular value.
2473
+ *
2474
+ * @param int value The value to get the Y coordinate for
2475
+ */
2476
+ this.getYCoord = function (value)
2477
+ {
2478
+
2479
+ if (value > this.scale2.max) {
2480
+ return null;
2481
+ }
2482
+
2483
+ var co = this.context,
2484
+ ca = this.canvas,
2485
+ prop = this.properties;
2486
+
2487
+ var y, xaxispos = prop['chart.xaxispos'];
2488
+
2489
+ if (xaxispos == 'top') {
2490
+
2491
+ // Account for negative numbers
2492
+ if (value < 0) {
2493
+ value = ma.abs(value);
2494
+ }
2495
+
2496
+ y = ((value - this.scale2.min) / (this.scale2.max - this.scale2.min)) * this.grapharea;
2497
+ y = y + this.gutterTop
2498
+
2499
+ } else if (xaxispos == 'center') {
2500
+
2501
+ y = ((value - this.scale2.min) / (this.scale2.max - this.scale2.min)) * (this.grapharea / 2);
2502
+ y = (this.grapharea / 2) - y;
2503
+ y += this.gutterTop;
2504
+
2505
+ } else {
2506
+
2507
+ if (value < this.scale2.min) {
2508
+ value = this.scale2.min;
2509
+ }
2510
+
2511
+ y = ((value - this.scale2.min) / (this.scale2.max - this.scale2.min));
2512
+ y *= (ca.height - this.gutterTop - this.gutterBottom);
2513
+
2514
+ y = ca.height - this.gutterBottom - y;
2515
+ }
2516
+
2517
+ return y;
2518
+ };
2519
+
2520
+
2521
+
2522
+ /**
2523
+ * Each object type has its own Highlight() function which highlights the appropriate shape
2524
+ *
2525
+ * @param object shape The shape to highlight
2526
+ */
2527
+ this.highlight =
2528
+ this.Highlight = function (shape)
2529
+ {
2530
+ if (typeof prop['chart.highlight.style'] === 'function') {
2531
+ (prop['chart.highlight.style'])(shape);
2532
+ } else {
2533
+ // Add the new highlight
2534
+ RG.Highlight.Rect(this, shape);
2535
+ }
2536
+ };
2537
+
2538
+
2539
+
2540
+ /**
2541
+ * The getObjectByXY() worker method
2542
+ */
2543
+ this.getObjectByXY = function (e)
2544
+ {
2545
+ var mouseXY = RG.getMouseXY(e);
2546
+
2547
+ // Adjust the mouse Y coordinate for when the bar chart is
2548
+ // a 3D variant
2549
+ if (prop['chart.variant'] === '3d') {
2550
+ var adjustment = prop['chart.variant.threed.angle'] * mouseXY[0];
2551
+ mouseXY[1] -= adjustment;
2552
+ }
2553
+
2554
+
2555
+
2556
+ if (
2557
+ mouseXY[0] >= prop['chart.gutter.left']
2558
+ && mouseXY[0] <= (ca.width - prop['chart.gutter.right'])
2559
+ && mouseXY[1] >= prop['chart.gutter.top']
2560
+ && mouseXY[1] <= (ca.height - prop['chart.gutter.bottom'])
2561
+ ) {
2562
+
2563
+ return this;
2564
+ }
2565
+ };
2566
+
2567
+
2568
+
2569
+
2570
+ /**
2571
+ * This method handles the adjusting calculation for when the mouse is moved
2572
+ *
2573
+ * @param object e The event object
2574
+ */
2575
+ this.adjusting_mousemove =
2576
+ this.Adjusting_mousemove = function (e)
2577
+ {
2578
+ /**
2579
+ * Handle adjusting for the Bar
2580
+ */
2581
+ if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
2582
+
2583
+ // Rounding the value to the given number of decimals make the chart step
2584
+ var value = Number(this.getValue(e));
2585
+ var shape = RG.Registry.Get('chart.adjusting.shape')
2586
+
2587
+ if (shape) {
2588
+
2589
+ RG.Registry.Set('chart.adjusting.shape', shape);
2590
+
2591
+ if (this.stackedOrGrouped && prop['chart.grouping'] == 'grouped') {
2592
+
2593
+ var indexes = RG.sequentialIndexToGrouped(shape['index'], this.data);
2594
+
2595
+ if (typeof this.data[indexes[0]] == 'number') {
2596
+ this.data[indexes[0]] = Number(value);
2597
+ } else if (!RG.isNull(this.data[indexes[0]])) {
2598
+ this.data[indexes[0]][indexes[1]] = Number(value);
2599
+ }
2600
+ } else if (typeof this.data[shape['index']] == 'number') {
2601
+
2602
+ this.data[shape['index']] = Number(value);
2603
+ }
2604
+
2605
+ RG.redrawCanvas(e.target);
2606
+ RG.fireCustomEvent(this, 'onadjust');
2607
+ }
2608
+ }
2609
+ };
2610
+
2611
+
2612
+
2613
+
2614
+ /**
2615
+ * This allows for easy specification of gradients
2616
+ */
2617
+ this.parseColors = function ()
2618
+ {
2619
+ // Save the original colors so that they can be restored when the canvas is reset
2620
+ if (this.original_colors.length === 0) {
2621
+ this.original_colors['chart.colors'] = RGraph.array_clone(prop['chart.colors']);
2622
+ this.original_colors['chart.key.colors'] = RGraph.array_clone(prop['chart.key.colors']);
2623
+ this.original_colors['chart.crosshairs.color'] = prop['chart.crosshairs.color'];
2624
+ this.original_colors['chart.highlight.stroke'] = prop['chart.highlight.stroke'];
2625
+ this.original_colors['chart.highlight.fill'] = prop['chart.highlight.fill'];
2626
+ this.original_colors['chart.text.color'] = prop['chart.text.color'];
2627
+ this.original_colors['chart.background.barcolor1'] = prop['chart.background.barcolor1'];
2628
+ this.original_colors['chart.background.barcolor2'] = prop['chart.background.barcolor2'];
2629
+ this.original_colors['chart.background.grid.color'] = prop['chart.background.grid.color'];
2630
+ this.original_colors['chart.background.color'] = prop['chart.background.color'];
2631
+ this.original_colors['chart.strokecolor'] = prop['chart.strokecolor'];
2632
+ this.original_colors['chart.axis.color'] = prop['chart.axis.color'];
2633
+ }
2634
+
2635
+
2636
+ // chart.colors
2637
+ var colors = prop['chart.colors'];
2638
+ if (colors) {
2639
+ for (var i=0; i<colors.length; ++i) {
2640
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
2641
+ }
2642
+ }
2643
+
2644
+ // chart.key.colors
2645
+ var colors = prop['chart.key.colors'];
2646
+ if (colors) {
2647
+ for (var i=0; i<colors.length; ++i) {
2648
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
2649
+ }
2650
+ }
2651
+
2652
+ prop['chart.crosshairs.color'] = this.parseSingleColorForGradient(prop['chart.crosshairs.color']);
2653
+ prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
2654
+ prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
2655
+ prop['chart.text.color'] = this.parseSingleColorForGradient(prop['chart.text.color']);
2656
+ prop['chart.background.barcolor1'] = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
2657
+ prop['chart.background.barcolor2'] = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
2658
+ prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
2659
+ prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
2660
+ prop['chart.strokecolor'] = this.parseSingleColorForGradient(prop['chart.strokecolor']);
2661
+ prop['chart.axis.color'] = this.parseSingleColorForGradient(prop['chart.axis.color']);
2662
+ };
2663
+
2664
+
2665
+
2666
+
2667
+ /**
2668
+ * Use this function to reset the object to the post-constructor state. Eg reset colors if
2669
+ * need be etc
2670
+ */
2671
+ this.reset = function ()
2672
+ {
2673
+ };
2674
+
2675
+
2676
+
2677
+ /**
2678
+ * This parses a single color value
2679
+ */
2680
+ this.parseSingleColorForGradient = function (color)
2681
+ {
2682
+ if (!color || typeof(color) != 'string') {
2683
+ return color;
2684
+ }
2685
+
2686
+ if (color.match(/^gradient\((.*)\)$/i)) {
2687
+
2688
+ var parts = RegExp.$1.split(':');
2689
+
2690
+ // Create the gradient
2691
+ var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'], 0, prop['chart.gutter.top']);
2692
+
2693
+ var diff = 1 / (parts.length - 1);
2694
+
2695
+ grad.addColorStop(0, RG.trim(parts[0]));
2696
+
2697
+ for (var j=1,len=parts.length; j<len; ++j) {
2698
+ grad.addColorStop(j * diff, RGraph.trim(parts[j]));
2699
+ }
2700
+ }
2701
+
2702
+ return grad ? grad : color;
2703
+ };
2704
+
2705
+
2706
+
2707
+
2708
+ this.drawBevel =
2709
+ this.DrawBevel = function ()
2710
+ {
2711
+ var coords = this.coords;
2712
+ var coords2 = this.coords2;
2713
+
2714
+ var prop = this.properties;
2715
+ var co = this.context;
2716
+ var ca = this.canvas;
2717
+
2718
+ if (prop['chart.grouping'] == 'stacked') {
2719
+ for (var i=0; i<coords2.length; ++i) {
2720
+ if (coords2[i] && coords2[i][0] && coords2[i][0][0]) {
2721
+
2722
+ var x = coords2[i][0][0];
2723
+ var y = coords2[i][0][1];
2724
+ var w = coords2[i][0][2];
2725
+
2726
+ var arr = [];
2727
+ for (var j=0; j<coords2[i].length; ++j) {
2728
+ arr.push(coords2[i][j][3]);
2729
+ }
2730
+ var h = RGraph.array_sum(arr);
2731
+
2732
+
2733
+ co.save();
2734
+
2735
+ co.strokeStyle = 'black';
2736
+
2737
+ // Clip to the rect
2738
+ co.beginPath();
2739
+ co.rect(x, y, w, h);
2740
+ co.clip();
2741
+
2742
+ // Add the shadow
2743
+ co.shadowColor = 'black';
2744
+ co.shadowOffsetX = 0;
2745
+ co.shadowOffsetY = 0;
2746
+ co.shadowBlur = 20;
2747
+
2748
+ co.beginPath();
2749
+ co.rect(x - 3, y - 3, w + 6, h + 100);
2750
+ co.lineWidth = 5;
2751
+ co.stroke();
2752
+ co.restore();
2753
+ }
2754
+ }
2755
+ } else {
2756
+
2757
+ for (var i=0; i<coords.length; ++i) {
2758
+ if (coords[i]) {
2759
+
2760
+ var x = coords[i][0];
2761
+ var y = coords[i][1];
2762
+ var w = coords[i][2];
2763
+ var h = coords[i][3];
2764
+
2765
+ var xaxispos = prop['chart.xaxispos'];
2766
+ var xaxis_ycoord = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
2767
+
2768
+
2769
+ co.save();
2770
+
2771
+ co.strokeStyle = 'black';
2772
+
2773
+ // Clip to the rect
2774
+ co.beginPath();
2775
+ co.rect(x, y, w, h);
2776
+
2777
+ co.clip();
2778
+
2779
+ // Add the shadow
2780
+ co.shadowColor = 'black';
2781
+ co.shadowOffsetX = 0;
2782
+ co.shadowOffsetY = 0;
2783
+ co.shadowBlur = 20;
2784
+
2785
+ if (xaxispos == 'top' || (xaxispos == 'center' && (y + h) > xaxis_ycoord)) {
2786
+ y = y - 100;
2787
+ h = h + 100;
2788
+ } else {
2789
+ y = y;
2790
+ h = h + 100;
2791
+ }
2792
+
2793
+ co.beginPath();
2794
+ co.rect(x - 3, y - 3, w + 6, h + 6);
2795
+ co.lineWidth = 5;
2796
+ co.stroke();
2797
+ co.restore();
2798
+ }
2799
+ }
2800
+ }
2801
+ };
2802
+
2803
+
2804
+
2805
+
2806
+ /**
2807
+ * This function handles highlighting an entire data-series for the interactive
2808
+ * key
2809
+ *
2810
+ * @param int index The index of the data series to be highlighted
2811
+ */
2812
+ this.interactiveKeyHighlight = function (index)
2813
+ {
2814
+ this.coords2.forEach(function (value, idx, arr)
2815
+ {
2816
+ if (typeof value[index] == 'object' && value[index]) {
2817
+
2818
+ var x = value[index][0]
2819
+ var y = value[index][1]
2820
+ var w = value[index][2]
2821
+ var h = value[index][3]
2822
+
2823
+ co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
2824
+ co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
2825
+ co.lineWidth = 2;
2826
+ co.strokeRect(x, y, w, h);
2827
+ co.fillRect(x, y, w, h);
2828
+ }
2829
+ });
2830
+ };
2831
+
2832
+
2833
+
2834
+
2835
+ /**
2836
+ * Using a function to add events makes it easier to facilitate method chaining
2837
+ *
2838
+ * @param string type The type of even to add
2839
+ * @param function func
2840
+ */
2841
+ this.on = function (type, func)
2842
+ {
2843
+ if (type.substr(0,2) !== 'on') {
2844
+ type = 'on' + type;
2845
+ }
2846
+
2847
+ if (typeof this[type] !== 'function') {
2848
+ this[type] = func;
2849
+ } else {
2850
+ RG.addCustomEventListener(this, type, func);
2851
+ }
2852
+
2853
+ return this;
2854
+ };
2855
+
2856
+
2857
+
2858
+
2859
+ /**
2860
+ * Draws the above labels
2861
+ */
2862
+ this.drawAboveLabels = function ()
2863
+ {
2864
+ var labels = prop['chart.labels.above'],
2865
+ specific = prop['chart.labels.above.specific'],
2866
+ color = prop['chart.labels.above.color'],
2867
+ background= prop['chart.labels.above.background'],
2868
+ decimals = prop['chart.labels.above.decimals'],
2869
+ size = prop['chart.labels.above.size'],
2870
+ angle = -1 * prop['chart.labels.above.angle'],
2871
+ unitsPre = prop['chart.labels.above.units.pre'],
2872
+ unitsPost = prop['chart.labels.above.units.post'],
2873
+ coords = this.coords,
2874
+ coords2 = this.coords2,
2875
+ data = this.data,
2876
+ ldata = RG.arrayLinearize(this.data),
2877
+ offset = prop['chart.labels.above.offset'],
2878
+ text_font = prop['chart.text.font'],
2879
+ text_size = prop['chart.text.size'],
2880
+ grouping = prop['chart.grouping']
2881
+
2882
+
2883
+ // Turn off any shadow
2884
+ RG.noShadow(this);
2885
+
2886
+ // Color
2887
+ co.fillStyle = typeof color === 'string' ? color : prop['chart.text.color'];
2888
+
2889
+
2890
+ // This bit draws the text labels that appear above the bars if requested
2891
+ if (labels && grouping === 'grouped') {
2892
+ for (var i=0,len=data.length,sequentialIndex=0; i<len; i+=1) {
2893
+
2894
+ // Alignment for regular, positive bars
2895
+ if (typeof data[i] === 'number' && data[i] >= 0) {
2896
+
2897
+ var angle = angle;
2898
+ var halign = (angle ? 'left' : 'center');
2899
+ var valign = angle !== 0 ? 'center' : 'bottom';
2900
+
2901
+ RG.text2(this, {
2902
+ 'font': text_font,
2903
+ 'size': typeof size === 'number' ? size : text_size - 3,
2904
+ 'x': coords2[i][0][0] + (coords2[i][0][2] / 2),
2905
+ 'y': coords2[i][0][1] - offset,
2906
+ 'text': specific ? (specific[sequentialIndex] || '') : RG.numberFormat(this, Number(typeof data[i] === 'object' ? data[i][0] : data[i]).toFixed(decimals), unitsPre, unitsPost),
2907
+ 'halign': halign,
2908
+ 'valign': valign,
2909
+ 'angle': angle,
2910
+ 'marker': false,
2911
+ 'bounding': true,
2912
+ 'bounding.fill': background,
2913
+ 'bounding.stroke': 'rgba(0,0,0,0)',
2914
+ 'tag': 'labels.above'
2915
+ });
2916
+
2917
+ sequentialIndex++;
2918
+
2919
+
2920
+
2921
+
2922
+
2923
+
2924
+ // Alignment for regular, negative bars
2925
+ } else if (typeof data[i] === 'number' && data[i] < 0) {
2926
+
2927
+ var angle = angle;
2928
+ var halign = angle ? 'right' : 'center';
2929
+ var valign = angle !== 0 ? 'center' : 'top';
2930
+
2931
+
2932
+ RG.text2(this, {
2933
+ 'font': text_font,
2934
+ 'size': typeof size === 'number' ? size : text_size - 3,
2935
+ 'x': coords2[i][0][0] + (coords2[i][0][2] / 2),
2936
+ 'y': coords2[i][0][1] + coords2[i][0][3] + offset,
2937
+ 'text': specific ? (specific[sequentialIndex] || '') : RG.numberFormat(this, Number(typeof data[i] === 'object' ? data[i][0] : data[i]).toFixed(decimals), unitsPre, unitsPost),
2938
+ 'halign': halign,
2939
+ 'valign': valign,
2940
+ 'angle': angle,
2941
+ 'bounding': true,
2942
+ 'bounding.fill': background,
2943
+ 'bounding.stroke': 'rgba(0,0,0,0)',
2944
+ 'marker': false,
2945
+ 'tag': 'labels.above'
2946
+ });
2947
+
2948
+ sequentialIndex++;
2949
+
2950
+
2951
+
2952
+
2953
+
2954
+
2955
+ // Alignment for grouped bars
2956
+ } else if (typeof data[i] === 'object') {
2957
+
2958
+ for (var j=0,len2=data[i].length; j<len2; j+=1) {
2959
+
2960
+ var angle = angle;
2961
+ var halign = data[i][j] < 0 ? 'right' : 'left';
2962
+ halign = angle === 0 ? 'center' : halign;
2963
+ var valign = data[i][j] < 0 ? 'top' : 'bottom';
2964
+ valign = angle != 0 ? 'center' : valign;
2965
+
2966
+ RG.text2(this, {
2967
+ 'font': text_font,
2968
+ 'size': typeof size === 'number' ? size : text_size - 3,
2969
+ 'x': coords2[i][j][0] + (coords2[i][j][2] / 2),
2970
+ 'y': coords2[i][j][1] + (data[i][j] < 0 ? coords2[i][j][3] + offset: -offset),
2971
+ 'text': specific ? (specific[sequentialIndex] || '') : RG.numberFormat(this, Number(data[i][j]).toFixed(decimals), unitsPre, unitsPost),
2972
+ 'halign': halign,
2973
+ 'valign': valign,
2974
+ 'angle': angle,
2975
+ 'bounding': true,
2976
+ 'bounding.fill': background,
2977
+ 'bounding.stroke': 'rgba(0,0,0,0)',
2978
+ 'marker': false,
2979
+ 'tag': 'labels.above'
2980
+ });
2981
+ sequentialIndex++;
2982
+ }
2983
+ }
2984
+ }
2985
+
2986
+
2987
+
2988
+
2989
+
2990
+ /**
2991
+ * STACKED bars
2992
+ */
2993
+ } else if (labels && grouping === 'stacked') {
2994
+ for (var i=0,len=data.length,sequentialIndex=0; i<len; i+=1) {
2995
+ if (typeof data[i] === 'object') {
2996
+
2997
+ var angle = angle;
2998
+ var halign = angle != 0 ? 'left' : 'center';
2999
+ var valign = angle != 0 ? 'center' : 'bottom';
3000
+
3001
+ RG.text2(this, {
3002
+ 'font': text_font,
3003
+ 'size': typeof size === 'number' ? size : text_size - 3,
3004
+ 'x': coords2[i][0][0] + (coords2[i][0][2] / 2),
3005
+ 'y': coords2[i][0][1] + (data[i][0] < 0 ? coords2[i][0][3] : 0) - offset,
3006
+ 'text': specific ? (specific[sequentialIndex] || '') : RG.numberFormat(this, Number(RG.arraySum(data[i])).toFixed(decimals), unitsPre, unitsPost),
3007
+ 'halign': halign,
3008
+ 'valign': valign,
3009
+ 'angle': angle,
3010
+ 'bounding': true,
3011
+ 'bounding.fill': background,
3012
+ 'bounding.stroke': 'rgba(0,0,0,0)',
3013
+ 'marker': false,
3014
+ 'tag': 'labels.above'
3015
+ });
3016
+
3017
+ sequentialIndex += data[i].length;
3018
+
3019
+ /**
3020
+ * Regular numbers but in a stacked grouping
3021
+ */
3022
+ } else {
3023
+
3024
+ var angle = angle;
3025
+ var halign = angle != 0 ? 'left' : 'center';
3026
+ var valign = angle != 0 ? 'center' : 'bottom';
3027
+
3028
+ RG.text2(this, {
3029
+ 'font': text_font,
3030
+ 'size': typeof size === 'number' ? size : text_size - 3,
3031
+ 'x': coords2[i][0][0] + (coords2[i][0][2] / 2),
3032
+ 'y': coords2[i][0][1] + (data[i][0] < 0 ? coords2[i][0][3] : 0) - offset,
3033
+ 'text': specific ? (specific[sequentialIndex] || '') : RG.numberFormat(this, Number(data[i]).toFixed(decimals), unitsPre, unitsPost),
3034
+ 'halign': halign,
3035
+ 'valign': valign,
3036
+ 'angle': angle,
3037
+ 'bounding': true,
3038
+ 'bounding.fill': background,
3039
+ 'bounding.stroke': 'rgba(0,0,0,0)',
3040
+ 'marker': false,
3041
+ 'tag': 'labels.above'
3042
+ });
3043
+
3044
+ sequentialIndex++;
3045
+ }
3046
+ }
3047
+ }
3048
+ };
3049
+
3050
+
3051
+
3052
+
3053
+ /**
3054
+ * This function runs once only
3055
+ */
3056
+ this.firstDrawFunc = function ()
3057
+ {
3058
+ };
3059
+
3060
+
3061
+
3062
+
3063
+ /**
3064
+ * (new) Bar chart Wave effect. This is a rewrite that should be smoother
3065
+ * because it just uses a single loop and not setTimeout
3066
+ *
3067
+ * @param object OPTIONAL An object map of options. You specify 'frames' here to give the number of frames in the effect
3068
+ * @param function OPTIONAL A function that will be called when the effect is complete
3069
+ */
3070
+ this.wave = function ()
3071
+ {
3072
+ var obj = this,
3073
+ opt = arguments[0] || {},
3074
+ labelsAbove = this.get('labelsAbove');
3075
+
3076
+ opt.frames = opt.frames || 60;
3077
+ opt.startFrames = [];
3078
+ opt.counters = [];
3079
+
3080
+ var framesperbar = opt.frames / 3,
3081
+ frame = -1,
3082
+ callback = arguments[1] || function () {},
3083
+ original = RG.arrayClone(this.original_data);
3084
+
3085
+ //
3086
+ // turn off the labelsAbove option whilst animating
3087
+ //
3088
+ this.set('labelsAbove', false);
3089
+
3090
+ for (var i=0,len=obj.data.length; i<len; i+=1) {
3091
+ opt.startFrames[i] = ((opt.frames / 2) / (obj.data.length - 1)) * i;
3092
+
3093
+ if (typeof obj.data[i] === 'object' && obj.data[i]) {
3094
+ opt.counters[i] = [];
3095
+ for (var j=0; j<obj.data[i].length; j++) {
3096
+ opt.counters[i][j] = 0;
3097
+ }
3098
+ } else {
3099
+ opt.counters[i] = 0;
3100
+ }
3101
+ }
3102
+
3103
+ /**
3104
+ * This stops the chart from jumping
3105
+ */
3106
+ obj.draw();
3107
+ obj.Set('ymax', obj.scale2.max);
3108
+ RG.clear(obj.canvas);
3109
+
3110
+ function iterator ()
3111
+ {
3112
+ ++frame;
3113
+
3114
+ for (var i=0,len=obj.data.length; i<len; i+=1) {
3115
+ if (frame > opt.startFrames[i]) {
3116
+ if (typeof obj.data[i] === 'number') {
3117
+
3118
+ obj.data[i] = ma.min(
3119
+ ma.abs(original[i]),
3120
+ ma.abs(original[i] * ( (opt.counters[i]++) / framesperbar))
3121
+ );
3122
+
3123
+ // Make the number negative if the original was
3124
+ if (original[i] < 0) {
3125
+ obj.data[i] *= -1;
3126
+ }
3127
+ } else if (!RG.isNull(obj.data[i])) {
3128
+ for (var j=0,len2=obj.data[i].length; j<len2; j+=1) {
3129
+
3130
+ obj.data[i][j] = ma.min(
3131
+ ma.abs(original[i][j]),
3132
+ ma.abs(original[i][j] * ( (opt.counters[i][j]++) / framesperbar))
3133
+ );
3134
+
3135
+ // Make the number negative if the original was
3136
+ if (original[i][j] < 0) {
3137
+ obj.data[i][j] *= -1;
3138
+ }
3139
+ }
3140
+ }
3141
+ } else {
3142
+ obj.data[i] = typeof obj.data[i] === 'object' && obj.data[i] ? RG.arrayPad([], obj.data[i].length, 0) : (RG.isNull(obj.data[i]) ? null : 0);
3143
+ }
3144
+ }
3145
+
3146
+
3147
+ if (frame >= opt.frames) {
3148
+
3149
+ if (labelsAbove) {
3150
+ obj.set('labelsAbove', true);
3151
+ RG.redraw();
3152
+ }
3153
+
3154
+ callback(obj);
3155
+ } else {
3156
+ RG.redrawCanvas(obj.canvas);
3157
+ RG.Effects.updateCanvas(iterator);
3158
+ }
3159
+ }
3160
+
3161
+ iterator();
3162
+
3163
+ return this;
3164
+ };
3165
+
3166
+
3167
+
3168
+
3169
+ /**
3170
+ * Color Wave effect. This fades in color sequentially like the wave effect
3171
+ * makes the bars grow.
3172
+ *
3173
+ * @param object OPTIONAL An object map of options. You specify 'frames'
3174
+ * here to give the number of frames in the effect
3175
+ * @param function OPTIONAL A function that will be called when the effect
3176
+ * is complete
3177
+ */
3178
+ this.colorWave = function ()
3179
+ {
3180
+ var obj = this,
3181
+ opt = arguments[0] || {};
3182
+ opt.frames = opt.frames || 60;
3183
+ opt.startFrames = [];
3184
+ opt.counters = [],
3185
+ colors = obj.properties['chart.colors'];
3186
+
3187
+ // If just one color is specified and colorsSequential is not, then
3188
+ // pad the colors array out
3189
+ if (colors.length <= obj.data.length) {
3190
+ obj.set('chart.colors.sequential', true);
3191
+ colors = RG.arrayPad(colors, obj.data.length, colors[colors.length - 1]);
3192
+ }
3193
+
3194
+ var framesperbar = opt.frames / 2,
3195
+ frame = -1,
3196
+ callback = arguments[1] || function () {},
3197
+ originalColors = RG.arrayClone(obj.properties['chart.colors']);
3198
+
3199
+
3200
+
3201
+ for (var i=0,len=originalColors.length; i<len; i+=1) {
3202
+ opt.startFrames[i] = ((opt.frames / 2) / (originalColors.length - 1)) * i;
3203
+ opt.counters[i] = 0;
3204
+ }
3205
+
3206
+
3207
+ function iterator ()
3208
+ {
3209
+ ++frame;
3210
+
3211
+ for (var i=0,len=colors.length; i<len; i+=1) {
3212
+ if (frame > opt.startFrames[i] && colors[i].match(/^rgba?\(([0-9 ]+),([0-9 ]+),([0-9 ]+)(,([ 0-9.]+)?)\)/)) {
3213
+
3214
+ // DO NOT USE SPACES!
3215
+ colors[i] = 'rgba({1},{2},{3},{4})'.format(
3216
+ RegExp.$1,
3217
+ RegExp.$2,
3218
+ RegExp.$3,
3219
+ (frame - opt.startFrames[i]) / framesperbar
3220
+ );
3221
+ } else {
3222
+ colors[i] = colors[i].replace(/,[0-9. ]+\)/, ',0)');
3223
+ }
3224
+ }
3225
+
3226
+
3227
+ if (frame >= opt.frames) {
3228
+ callback(obj);
3229
+ } else {
3230
+ RG.redrawCanvas(obj.canvas);
3231
+ RG.Effects.updateCanvas(iterator);
3232
+ }
3233
+ }
3234
+
3235
+ iterator();
3236
+
3237
+ return this;
3238
+ };
3239
+
3240
+
3241
+
3242
+
3243
+ /**
3244
+ * Grow
3245
+ *
3246
+ * The Bar chart Grow effect gradually increases the values of the bars
3247
+ *
3248
+ * @param object An object of options - eg: {frames: 30}
3249
+ * @param function A function to call when the effect is complete
3250
+ */
3251
+ this.grow = function ()
3252
+ {
3253
+ // Callback
3254
+ var opt = arguments[0] || {},
3255
+ frames = opt.frames || 30,
3256
+ frame = 0,
3257
+ callback = arguments[1] || function () {},
3258
+ obj = this,
3259
+ labelsAbove = this.get('labelsAbove')
3260
+
3261
+
3262
+
3263
+
3264
+
3265
+
3266
+ // Go through the data and change string arguments of the format +/-[0-9]
3267
+ // to absolute numbers
3268
+ if (RG.isArray(opt.data)) {
3269
+
3270
+ var ymax = 0;
3271
+
3272
+ for (var i=0; i<opt.data.length; ++i) {
3273
+ if (typeof opt.data[i] === 'object') {
3274
+ for (var j=0; j<opt.data[i].length; ++j) {
3275
+ if (typeof opt.data[i][j] === 'string'&& opt.data[i][j].match(/(\+|\-)([0-9]+)/)) {
3276
+ if (RegExp.$1 === '+') {
3277
+ opt.data[i][j] = this.original_data[i][j] + parseInt(RegExp.$2);
3278
+ } else {
3279
+ opt.data[i][j] = this.original_data[i][j] - parseInt(RegExp.$2);
3280
+ }
3281
+ }
3282
+
3283
+ ymax = ma.max(ymax, opt.data[i][j]);
3284
+ }
3285
+ } else if (typeof opt.data[i] === 'string' && opt.data[i].match(/(\+|\-)([0-9]+)/)) {
3286
+ if (RegExp.$1 === '+') {
3287
+ opt.data[i] = this.original_data[i] + parseInt(RegExp.$2);
3288
+ } else {
3289
+ opt.data[i] = this.original_data[i] - parseInt(RegExp.$2);
3290
+ }
3291
+ ymax = ma.max(ymax, opt.data[i]);
3292
+ } else {
3293
+ ymax = ma.max(ymax, opt.data[i]);
3294
+ }
3295
+ }
3296
+
3297
+
3298
+ var scale = RG.getScale2(this, {'max':ymax});
3299
+ this.Set('chart.ymax', scale.max);
3300
+ }
3301
+
3302
+
3303
+
3304
+
3305
+
3306
+
3307
+ //
3308
+ // turn off the labelsAbove option whilst animating
3309
+ //
3310
+ this.set('labelsAbove', false);
3311
+
3312
+
3313
+
3314
+
3315
+
3316
+
3317
+ // Stop the scale from changing by setting chart.ymax (if it's not already set)
3318
+ if (prop['chart.ymax'] == null) {
3319
+
3320
+ var ymax = 0;
3321
+
3322
+ for (var i=0; i<obj.data.length; ++i) {
3323
+ if (RG.isArray(this.data[i]) && prop['chart.grouping'] === 'stacked') {
3324
+ ymax = ma.max(ymax, ma.abs(RG.arraySum(this.data[i])));
3325
+
3326
+ } else if (RG.isArray(this.data[i]) && prop['chart.grouping'] === 'grouped') {
3327
+
3328
+ for (var j=0,group=[]; j<this.data[i].length; j++) {
3329
+ group.push(ma.abs(this.data[i][j]));
3330
+ }
3331
+
3332
+ ymax = ma.max(ymax, ma.abs(RG.arrayMax(group)));
3333
+
3334
+ } else {
3335
+ ymax = ma.max(ymax, ma.abs(this.data[i]));
3336
+ }
3337
+ }
3338
+
3339
+ var scale = RG.getScale2(this, {'max':ymax});
3340
+ this.Set('chart.ymax', scale.max);
3341
+ }
3342
+
3343
+ // You can give a ymax to the grow function
3344
+ if (typeof opt.ymax === 'number') {
3345
+ obj.set('ymax', opt.ymax);
3346
+ }
3347
+
3348
+
3349
+
3350
+ var iterator = function ()
3351
+ {
3352
+ var easingMultiplier = RG.Effects.getEasingMultiplier(frames, frame);
3353
+
3354
+ // Alter the Bar chart data depending on the frame
3355
+ for (var j=0,len=obj.original_data.length; j<len; ++j) {
3356
+ if (typeof obj.data[j] === 'object' && !RG.isNull(obj.data[j])) {
3357
+ for (var k=0,len2=obj.data[j].length; k<len2; ++k) {
3358
+ if (obj.firstDraw || !opt.data) {
3359
+ obj.data[j][k] = easingMultiplier * obj.original_data[j][k];
3360
+ } else if (opt.data && opt.data.length === obj.original_data.length) {
3361
+ var diff = opt.data[j][k] - obj.original_data[j][k];
3362
+ obj.data[j][k] = (easingMultiplier * diff) + obj.original_data[j][k];
3363
+ }
3364
+ }
3365
+ } else {
3366
+
3367
+ if (obj.firstDraw || !opt.data) {
3368
+ obj.data[j] = easingMultiplier * obj.original_data[j];
3369
+ } else if (opt.data && opt.data.length === obj.original_data.length) {
3370
+ var diff = opt.data[j] - obj.original_data[j];
3371
+ obj.data[j] = (easingMultiplier * diff) + obj.original_data[j];
3372
+ }
3373
+ }
3374
+ }
3375
+
3376
+
3377
+
3378
+
3379
+ //RGraph.clear(obj.canvas);
3380
+ RG.redrawCanvas(obj.canvas);
3381
+
3382
+
3383
+
3384
+
3385
+ if (frame < frames) {
3386
+ frame += 1;
3387
+
3388
+ RG.Effects.updateCanvas(iterator);
3389
+
3390
+ // Call the callback function
3391
+ } else {
3392
+
3393
+
3394
+
3395
+
3396
+
3397
+ // Do some housekeeping if new data was specified thats done in
3398
+ // the constructor - but needs to be redone because new data
3399
+ // has been specified
3400
+ if (RG.isArray(opt.data)) {
3401
+
3402
+ var linear_data = RG.arrayLinearize(data);
3403
+
3404
+ for (var i=0; i<linear_data.length; ++i) {
3405
+ if (!obj['$' + i]) {
3406
+ obj['$' + i] = {};
3407
+ }
3408
+ }
3409
+ }
3410
+
3411
+
3412
+
3413
+ obj.data = data;
3414
+ obj.original_data = RG.arrayClone(data);
3415
+
3416
+
3417
+
3418
+
3419
+
3420
+ if (labelsAbove) {
3421
+ obj.set('labelsAbove', true);
3422
+ RG.redraw();
3423
+ }
3424
+ callback(obj);
3425
+ }
3426
+ };
3427
+
3428
+ iterator();
3429
+
3430
+ return this;
3431
+ };
3432
+
3433
+
3434
+
3435
+
3436
+ //
3437
+ // Draws error-bars for the Bar and Line charts
3438
+ //
3439
+ this.drawErrorbars = function ()
3440
+ {
3441
+ var coords = this.coords,
3442
+ color = prop['chart.errorbars.color'] || 'black',
3443
+ default_halfwidth = ma.min(prop['chart.errorbars.capped.width'], coords[0][2]) / 2,
3444
+ x = 0,
3445
+ errorbars = prop['chart.errorbars'],
3446
+ length = 0;
3447
+
3448
+
3449
+ // If not capped set the width of the cqap to zero
3450
+ if (!prop['chart.errorbars.capped']) {
3451
+ prop['chart.errorbars.capped.width'] = 0;
3452
+ halfwidth = 0;
3453
+ }
3454
+
3455
+ // Set the linewidth
3456
+ co.lineWidth = prop['chart.errorbars.linewidth'];
3457
+
3458
+
3459
+
3460
+
3461
+ for (var i=0; i<coords.length; ++i) {
3462
+
3463
+
3464
+ // Default to black
3465
+ color = prop['chart.errorbars.color'] || 'black';
3466
+
3467
+ // Set the perbar linewidth if the fourth option in the array
3468
+ // is specified
3469
+ if (errorbars[i] && typeof errorbars[i][3] === 'number') {
3470
+ co.lineWidth = errorbars[i][3];
3471
+ }
3472
+
3473
+ // Set the halfwidth
3474
+ var halfwidth = (errorbars[i]&& typeof errorbars[i][4] === 'number') ? errorbars[i][4] / 2 : default_halfwidth;
3475
+
3476
+ if (!prop['chart.errorbars.capped']) {
3477
+ halfwidth = 0;
3478
+ }
3479
+
3480
+
3481
+
3482
+ // Calulate the pixel size
3483
+ if (typeof errorbars[i] === 'number') {
3484
+
3485
+ length = ma.abs(this.getYCoord(errorbars[i]) - this.getYCoord(0));
3486
+
3487
+ if (length) {
3488
+ pa2(
3489
+ co,
3490
+ 'b m % % l % % l % % l % % s %',
3491
+ coords[i][0] + (coords[i][2] / 2),
3492
+ coords[i][1],
3493
+ coords[i][0] + (coords[i][2] / 2),
3494
+ coords[i][1] - length,
3495
+ coords[i][0] + (coords[i][2] / 2) - halfwidth,
3496
+ ma.round(coords[i][1] - length),
3497
+ coords[i][0] + (coords[i][2] / 2) + halfwidth,
3498
+ ma.round(coords[i][1] - length),
3499
+ color
3500
+ );
3501
+ }
3502
+ } else if (typeof errorbars[i] === 'object' && !RG.isNull(errorbars[i])) {
3503
+
3504
+ var positiveLength = ma.abs(this.getYCoord(errorbars[i][0]) - this.getYCoord(0));
3505
+
3506
+ // Color
3507
+ if (typeof errorbars[i][1] === 'string') {
3508
+ color = errorbars[i][1];
3509
+
3510
+ } else if (typeof errorbars[i][2] === 'string') {
3511
+ color = errorbars[i][2];
3512
+ }
3513
+
3514
+ // Cap width
3515
+ halfwidth = typeof errorbars[i][4] === 'number' ? errorbars[i][4] / 2 : default_halfwidth;
3516
+
3517
+ if (!prop['chart.errorbars.capped']) {
3518
+ halfwidth = 0;
3519
+ }
3520
+
3521
+ if (!RG.isNull(errorbars[i][0])) {
3522
+ pa2(
3523
+ co,
3524
+ 'b m % % l % % l % % l % % s %',
3525
+ coords[i][0] + (coords[i][2] / 2),
3526
+ coords[i][1],
3527
+ coords[i][0] + (coords[i][2] / 2),
3528
+ coords[i][1] - positiveLength,
3529
+ coords[i][0] + (coords[i][2] / 2) - halfwidth,
3530
+ ma.round(coords[i][1] - positiveLength),
3531
+ coords[i][0] + (coords[i][2] / 2) + halfwidth,
3532
+ ma.round(coords[i][1] - positiveLength),
3533
+ color
3534
+ );
3535
+ }
3536
+
3537
+ if (typeof errorbars[i][1] === 'number') {
3538
+
3539
+ var negativeLength = ma.abs(this.getYCoord(errorbars[i][1]) - this.getYCoord(0));
3540
+
3541
+ pa2(
3542
+ co,
3543
+ 'b m % % l % % l % % l % % s %',
3544
+ coords[i][0] + (coords[i][2] / 2),
3545
+ coords[i][1],
3546
+ coords[i][0] + (coords[i][2] / 2),
3547
+ coords[i][1] + negativeLength,
3548
+ coords[i][0] + (coords[i][2] / 2) - halfwidth,
3549
+ ma.round(coords[i][1] + negativeLength),
3550
+ coords[i][0] + (coords[i][2] / 2) + halfwidth,
3551
+ ma.round(coords[i][1] + negativeLength),
3552
+ color
3553
+ );
3554
+ }
3555
+ }
3556
+
3557
+
3558
+ // Reset the perbar linewidth to the default if the fourth option
3559
+ // in the array was specified specified
3560
+ if (errorbars[i] && typeof errorbars[i][3] === 'number') {
3561
+ co.lineWidth = prop['chart.errorbars.linewidth'];
3562
+ }
3563
+ }
3564
+ };
3565
+
3566
+
3567
+
3568
+
3569
+ //
3570
+ // A per-object to test whether a particular bar is adjustable or not
3571
+ //
3572
+ // @param shape The shape object
3573
+ //
3574
+ this.isAdjustable = function (shape)
3575
+ {
3576
+ if (RG.isNull(prop['chart.adjustable.only']) || !RG.isArray(prop['chart.adjustable.only'])) {
3577
+ return true;
3578
+ }
3579
+
3580
+ if (RG.isArray(prop['chart.adjustable.only']) && prop['chart.adjustable.only'][shape.index]) {
3581
+ return true;
3582
+ }
3583
+
3584
+ return false;
3585
+ };
3586
+
3587
+
3588
+
3589
+
3590
+ /**
3591
+ * Register the object
3592
+ */
3593
+ RG.register(this);
3594
+
3595
+
3596
+
3597
+
3598
+ /**
3599
+ * This is the 'end' of the constructor so if the first argument
3600
+ * contains configuration dsta - handle that.
3601
+ */
3602
+ if (parseConfObjectForOptions) {
3603
+ RG.parseObjectStyleConfig(this, conf.options);
3604
+ }
3605
+ };
3606
+
3607
+
3608
+
3609
+
3610
+
3611
+ /*********************************************************************************************************
3612
+ * This is the combined bar and Line class which makes creating bar/line combo charts a little bit easier *
3613
+ /*********************************************************************************************************/
3614
+
3615
+
3616
+
3617
+
3618
+
3619
+
3620
+
3621
+ RGraph.CombinedChart = function ()
3622
+ {
3623
+ /**
3624
+ * Create a default empty array for the objects
3625
+ */
3626
+ this.objects = [];
3627
+ var objects = [];
3628
+
3629
+ if (RGraph.isArray(arguments[0])) {
3630
+ objects = arguments[0];
3631
+ } else {
3632
+
3633
+ for (var i=0; i<arguments.length; i+=1) {
3634
+
3635
+ objects[i] = arguments[i];
3636
+ }
3637
+ }
3638
+
3639
+ for (var i=0; i<objects.length; ++i) {
3640
+
3641
+ this.objects[i] = objects[i];
3642
+
3643
+ /**
3644
+ * Set the Line chart gutters to match the Bar chart gutters
3645
+ */
3646
+ this.objects[i].set({
3647
+ gutterLeft: this.objects[0].get('gutter.left'), // Needs to use the dot form to skirt an IE9 bug
3648
+ gutterRight: this.objects[0].get('gutter.right'), // Needs to use the dot form to skirt an IE9 bug
3649
+ gutterTop: this.objects[0].get('gutter.top'), // Needs to use the dot form to skirt an IE9 bug
3650
+ gutterBottom: this.objects[0].get('gutter.bottom') // Needs to use the dot form to skirt an IE9 bug
3651
+ });
3652
+
3653
+ if (this.objects[i].type == 'line') {
3654
+
3655
+ var obj = this.objects[i];
3656
+
3657
+ /**
3658
+ * Set the line chart hmargin
3659
+ */
3660
+ obj.set('hmargin', ((this.objects[0].canvas.width - this.objects[0].Get('chart.gutter.right') - this.objects[0].Get('chart.gutter.left')) / this.objects[0].data.length) / 2 );
3661
+
3662
+
3663
+ /**
3664
+ * No labels, axes or grid on the Line chart
3665
+ */
3666
+ obj.set('noaxes', true);
3667
+ obj.set('backgroundGrid', false);
3668
+ obj.set('ylabels', false);
3669
+ }
3670
+
3671
+ /**
3672
+ * Resizing
3673
+ */
3674
+ if (this.objects[i].get('chart.resizable')) {
3675
+ var resizable_object = obj;
3676
+ }
3677
+ }
3678
+
3679
+ /**
3680
+ * Resizing
3681
+ */
3682
+ if (resizable_object) {
3683
+ /**
3684
+ * This recalculates the Line chart hmargin when the chart is resized
3685
+ */
3686
+ function myOnresizebeforedraw (obj)
3687
+ {
3688
+ var gutterLeft = obj.get('gutterLeft');
3689
+ var gutterRight = obj.get('gutterRight');
3690
+
3691
+ obj.set('hmargin', (obj.canvas.width - gutterLeft - gutterRight) / (obj.original_data[0].length * 2));
3692
+ }
3693
+
3694
+ RGraph.AddCustomEventListener(
3695
+ resizable_object,
3696
+ 'onresizebeforedraw',
3697
+ myOnresizebeforedraw
3698
+ );
3699
+ }
3700
+ };
3701
+
3702
+
3703
+
3704
+
3705
+ /**
3706
+ * The Add method can be used to add methods to the CombinedChart object.
3707
+ */
3708
+ RGraph.CombinedChart.prototype.add =
3709
+ RGraph.CombinedChart.prototype.Add = function (obj)
3710
+ {
3711
+ this.objects.push(obj);
3712
+ };
3713
+
3714
+
3715
+ /**
3716
+ * The Draw method goes through all of the objects drawing them (sequentially)
3717
+ */
3718
+ RGraph.CombinedChart.prototype.draw =
3719
+ RGraph.CombinedChart.prototype.Draw = function ()
3720
+ {
3721
+ for (var i=0; i<this.objects.length; ++i) {
3722
+ if (this.objects[i].properties['chart.combinedchart.effect']) {
3723
+
3724
+ var options = this.objects[i].properties['chart.combinedchart.effect.options'] ? eval('(' + this.objects[i].properties['chart.combinedchart.effect.options'] + ')') : null;
3725
+
3726
+ (this.objects[i][this.objects[i].properties['chart.combinedchart.effect']])
3727
+ (
3728
+ options,
3729
+ this.objects[i].properties['chart.combinedchart.effect.callback']
3730
+ )
3731
+ } else {
3732
+ this.objects[i].draw();
3733
+ }
3734
+ }
3735
+ };