rgraph-rails 1.0.1

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