rgraph-rails 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/rgraph-rails/version.rb +3 -0
- data/lib/rgraph-rails.rb +8 -0
- data/license.txt +19 -0
- data/rgraph-rails.gemspec +26 -0
- data/vendor/assets/images/bg.png +0 -0
- data/vendor/assets/images/bullet.png +0 -0
- data/vendor/assets/images/facebook-large.png +0 -0
- data/vendor/assets/images/google-plus-large.png +0 -0
- data/vendor/assets/images/logo.png +0 -0
- data/vendor/assets/images/meter-image-sd-needle.png +0 -0
- data/vendor/assets/images/meter-image-sd.png +0 -0
- data/vendor/assets/images/meter-sketch-needle.png +0 -0
- data/vendor/assets/images/meter-sketch.png +0 -0
- data/vendor/assets/images/odometer-background.png +0 -0
- data/vendor/assets/images/rgraph.jpg +0 -0
- data/vendor/assets/images/title.png +0 -0
- data/vendor/assets/images/twitter-large.png +0 -0
- data/vendor/assets/javascripts/RGraph.bar.js +3246 -0
- data/vendor/assets/javascripts/RGraph.bipolar.js +2003 -0
- data/vendor/assets/javascripts/RGraph.common.annotate.js +399 -0
- data/vendor/assets/javascripts/RGraph.common.context.js +600 -0
- data/vendor/assets/javascripts/RGraph.common.core.js +4751 -0
- data/vendor/assets/javascripts/RGraph.common.csv.js +275 -0
- data/vendor/assets/javascripts/RGraph.common.deprecated.js +454 -0
- data/vendor/assets/javascripts/RGraph.common.dynamic.js +1194 -0
- data/vendor/assets/javascripts/RGraph.common.effects.js +1524 -0
- data/vendor/assets/javascripts/RGraph.common.key.js +735 -0
- data/vendor/assets/javascripts/RGraph.common.resizing.js +550 -0
- data/vendor/assets/javascripts/RGraph.common.tooltips.js +605 -0
- data/vendor/assets/javascripts/RGraph.common.zoom.js +223 -0
- data/vendor/assets/javascripts/RGraph.drawing.background.js +636 -0
- data/vendor/assets/javascripts/RGraph.drawing.circle.js +579 -0
- data/vendor/assets/javascripts/RGraph.drawing.image.js +810 -0
- data/vendor/assets/javascripts/RGraph.drawing.marker1.js +710 -0
- data/vendor/assets/javascripts/RGraph.drawing.marker2.js +672 -0
- data/vendor/assets/javascripts/RGraph.drawing.marker3.js +568 -0
- data/vendor/assets/javascripts/RGraph.drawing.poly.js +623 -0
- data/vendor/assets/javascripts/RGraph.drawing.rect.js +603 -0
- data/vendor/assets/javascripts/RGraph.drawing.text.js +648 -0
- data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +815 -0
- data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +860 -0
- data/vendor/assets/javascripts/RGraph.fuel.js +965 -0
- data/vendor/assets/javascripts/RGraph.funnel.js +988 -0
- data/vendor/assets/javascripts/RGraph.gantt.js +1242 -0
- data/vendor/assets/javascripts/RGraph.gauge.js +1391 -0
- data/vendor/assets/javascripts/RGraph.hbar.js +1794 -0
- data/vendor/assets/javascripts/RGraph.hprogress.js +1307 -0
- data/vendor/assets/javascripts/RGraph.line.js +3940 -0
- data/vendor/assets/javascripts/RGraph.meter.js +1242 -0
- data/vendor/assets/javascripts/RGraph.modaldialog.js +292 -0
- data/vendor/assets/javascripts/RGraph.odo.js +1265 -0
- data/vendor/assets/javascripts/RGraph.pie.js +1979 -0
- data/vendor/assets/javascripts/RGraph.radar.js +1840 -0
- data/vendor/assets/javascripts/RGraph.rose.js +1860 -0
- data/vendor/assets/javascripts/RGraph.rscatter.js +1332 -0
- data/vendor/assets/javascripts/RGraph.scatter.js +3029 -0
- data/vendor/assets/javascripts/RGraph.thermometer.js +1131 -0
- data/vendor/assets/javascripts/RGraph.vprogress.js +1326 -0
- data/vendor/assets/javascripts/RGraph.waterfall.js +1252 -0
- data/vendor/assets/javascripts/financial-data.js +1067 -0
- data/vendor/assets/stylesheets/ModalDialog.css +90 -0
- data/vendor/assets/stylesheets/animations.css +3347 -0
- data/vendor/assets/stylesheets/website.css +402 -0
- metadata +175 -0
|
@@ -0,0 +1,1794 @@
|
|
|
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
|
+
* The horizontal bar chart constructor. The horizontal bar is a minor variant
|
|
20
|
+
* on the bar chart. If you have big labels, this may be useful as there is usually
|
|
21
|
+
* more space available for them.
|
|
22
|
+
*
|
|
23
|
+
* @param object canvas The canvas object
|
|
24
|
+
* @param array data The chart data
|
|
25
|
+
*/
|
|
26
|
+
RGraph.HBar = function (conf)
|
|
27
|
+
{
|
|
28
|
+
/**
|
|
29
|
+
* Allow for object config style
|
|
30
|
+
*/
|
|
31
|
+
if ( typeof conf === 'object'
|
|
32
|
+
&& typeof conf.data === 'object'
|
|
33
|
+
&& typeof conf.id === 'string') {
|
|
34
|
+
|
|
35
|
+
var id = conf.id
|
|
36
|
+
var canvas = document.getElementById(id);
|
|
37
|
+
var data = conf.data;
|
|
38
|
+
var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
|
|
39
|
+
|
|
40
|
+
} else {
|
|
41
|
+
|
|
42
|
+
var id = conf;
|
|
43
|
+
var canvas = document.getElementById(id);
|
|
44
|
+
var data = arguments[1];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
this.id = id;
|
|
49
|
+
this.canvas = canvas;
|
|
50
|
+
this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null;
|
|
51
|
+
this.canvas.__object__ = this;
|
|
52
|
+
this.data = data;
|
|
53
|
+
this.type = 'hbar';
|
|
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.coords = [];
|
|
59
|
+
this.coords2 = [];
|
|
60
|
+
this.coordsText = [];
|
|
61
|
+
this.original_colors = [];
|
|
62
|
+
this.firstDraw = true; // After the first draw this will be false
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Compatibility with older browsers
|
|
67
|
+
*/
|
|
68
|
+
//RGraph.OldBrowserCompat(this.context);
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
this.max = 0;
|
|
72
|
+
this.stackedOrGrouped = false;
|
|
73
|
+
|
|
74
|
+
// Default properties
|
|
75
|
+
this.properties =
|
|
76
|
+
{
|
|
77
|
+
'chart.gutter.left': 75,
|
|
78
|
+
'chart.gutter.left.autosize': false,
|
|
79
|
+
'chart.gutter.right': 25,
|
|
80
|
+
'chart.gutter.top': 25,
|
|
81
|
+
'chart.gutter.bottom': 25,
|
|
82
|
+
'chart.background.grid': true,
|
|
83
|
+
'chart.background.grid.color': '#ddd',
|
|
84
|
+
'chart.background.grid.width': 1,
|
|
85
|
+
'chart.background.grid.hsize': 25,
|
|
86
|
+
'chart.background.grid.vsize': 25,
|
|
87
|
+
'chart.background.barcolor1': 'rgba(0,0,0,0)',
|
|
88
|
+
'chart.background.barcolor2': 'rgba(0,0,0,0)',
|
|
89
|
+
'chart.background.grid.hlines': true,
|
|
90
|
+
'chart.background.grid.vlines': true,
|
|
91
|
+
'chart.background.grid.border': true,
|
|
92
|
+
'chart.background.grid.autofit':true,
|
|
93
|
+
'chart.background.grid.autofit.align':true,
|
|
94
|
+
'chart.background.grid.autofit.numhlines': null,
|
|
95
|
+
'chart.background.grid.autofit.numvlines': 5,
|
|
96
|
+
'chart.background.grid.dashed': false,
|
|
97
|
+
'chart.background.grid.dotted': false,
|
|
98
|
+
'chart.background.color': null,
|
|
99
|
+
'chart.linewidth': 1,
|
|
100
|
+
'chart.title': '',
|
|
101
|
+
'chart.title.background': null,
|
|
102
|
+
'chart.title.xaxis': '',
|
|
103
|
+
'chart.title.xaxis.bold': true,
|
|
104
|
+
'chart.title.xaxis.size': null,
|
|
105
|
+
'chart.title.xaxis.font': null,
|
|
106
|
+
'chart.title.yaxis': '',
|
|
107
|
+
'chart.title.yaxis.bold': true,
|
|
108
|
+
'chart.title.yaxis.size': null,
|
|
109
|
+
'chart.title.yaxis.font': null,
|
|
110
|
+
'chart.title.yaxis.color': null,
|
|
111
|
+
'chart.title.xaxis.pos': null,
|
|
112
|
+
'chart.title.yaxis.pos': 0.8,
|
|
113
|
+
'chart.title.yaxis.x': null,
|
|
114
|
+
'chart.title.yaxis.y': null,
|
|
115
|
+
'chart.title.xaxis.x': null,
|
|
116
|
+
'chart.title.xaxis.y': null,
|
|
117
|
+
'chart.title.hpos': null,
|
|
118
|
+
'chart.title.vpos': null,
|
|
119
|
+
'chart.title.bold': true,
|
|
120
|
+
'chart.title.font': null,
|
|
121
|
+
'chart.title.x': null,
|
|
122
|
+
'chart.title.y': null,
|
|
123
|
+
'chart.title.halign': null,
|
|
124
|
+
'chart.title.valign': null,
|
|
125
|
+
'chart.text.size': 12,
|
|
126
|
+
'chart.text.color': 'black',
|
|
127
|
+
'chart.text.font': 'Arial',
|
|
128
|
+
'chart.colors': ['Gradient(white:red)', 'Gradient(white:blue)', 'Gradient(white:green)', 'Gradient(white:pink)', 'Gradient(white:yellow)', 'Gradient(white:cyan)', 'Gradient(white:navy)', 'Gradient(white:gray)', 'Gradient(white:black)'],
|
|
129
|
+
'chart.colors.sequential': false,
|
|
130
|
+
'chart.xlabels.specific': null,
|
|
131
|
+
'chart.labels': [],
|
|
132
|
+
'chart.labels.bold': false,
|
|
133
|
+
'chart.labels.color': null,
|
|
134
|
+
'chart.labels.above': false,
|
|
135
|
+
'chart.labels.above.decimals': 0,
|
|
136
|
+
'chart.labels.above.specific': null,
|
|
137
|
+
'chart.xlabels': true,
|
|
138
|
+
'chart.xlabels.count': 5,
|
|
139
|
+
'chart.contextmenu': null,
|
|
140
|
+
'chart.key': null,
|
|
141
|
+
'chart.key.background': 'white',
|
|
142
|
+
'chart.key.position': 'graph',
|
|
143
|
+
'chart.key.halign': 'right',
|
|
144
|
+
'chart.key.shadow': false,
|
|
145
|
+
'chart.key.shadow.color': '#666',
|
|
146
|
+
'chart.key.shadow.blur': 3,
|
|
147
|
+
'chart.key.shadow.offsetx': 2,
|
|
148
|
+
'chart.key.shadow.offsety': 2,
|
|
149
|
+
'chart.key.position.gutter.boxed': false,
|
|
150
|
+
'chart.key.position.x': null,
|
|
151
|
+
'chart.key.position.y': null,
|
|
152
|
+
'chart.key.color.shape': 'square',
|
|
153
|
+
'chart.key.rounded': true,
|
|
154
|
+
'chart.key.linewidth': 1,
|
|
155
|
+
'chart.key.colors': null,
|
|
156
|
+
'chart.key.interactive': false,
|
|
157
|
+
'chart.key.interactive.highlight.chart.stroke': 'black',
|
|
158
|
+
'chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)',
|
|
159
|
+
'chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)',
|
|
160
|
+
'chart.key.text.color': 'black',
|
|
161
|
+
'chart.units.pre': '',
|
|
162
|
+
'chart.units.post': '',
|
|
163
|
+
'chart.units.ingraph': false,
|
|
164
|
+
'chart.strokestyle': 'rgba(0,0,0,0)',
|
|
165
|
+
'chart.xmin': 0,
|
|
166
|
+
'chart.xmax': 0,
|
|
167
|
+
'chart.axis.color': 'black',
|
|
168
|
+
'chart.shadow': false,
|
|
169
|
+
'chart.shadow.color': '#666',
|
|
170
|
+
'chart.shadow.blur': 3,
|
|
171
|
+
'chart.shadow.offsetx': 3,
|
|
172
|
+
'chart.shadow.offsety': 3,
|
|
173
|
+
'chart.vmargin': 2,
|
|
174
|
+
'chart.vmargin.grouped': 2,
|
|
175
|
+
'chart.grouping': 'grouped',
|
|
176
|
+
'chart.tooltips': null,
|
|
177
|
+
'chart.tooltips.event': 'onclick',
|
|
178
|
+
'chart.tooltips.effect': 'fade',
|
|
179
|
+
'chart.tooltips.css.class': 'RGraph_tooltip',
|
|
180
|
+
'chart.tooltips.highlight': true,
|
|
181
|
+
'chart.highlight.fill': 'rgba(255,255,255,0.7)',
|
|
182
|
+
'chart.highlight.stroke': 'rgba(0,0,0,0)',
|
|
183
|
+
'chart.annotatable': false,
|
|
184
|
+
'chart.annotate.color': 'black',
|
|
185
|
+
'chart.zoom.factor': 1.5,
|
|
186
|
+
'chart.zoom.fade.in': true,
|
|
187
|
+
'chart.zoom.fade.out': true,
|
|
188
|
+
'chart.zoom.hdir': 'right',
|
|
189
|
+
'chart.zoom.vdir': 'down',
|
|
190
|
+
'chart.zoom.frames': 25,
|
|
191
|
+
'chart.zoom.delay': 16.666,
|
|
192
|
+
'chart.zoom.shadow': true,
|
|
193
|
+
'chart.zoom.background': true,
|
|
194
|
+
'chart.zoom.action': 'zoom',
|
|
195
|
+
'chart.resizable': false,
|
|
196
|
+
'chart.resize.handle.adjust': [0,0],
|
|
197
|
+
'chart.resize.handle.background': null,
|
|
198
|
+
'chart.scale.point': '.',
|
|
199
|
+
'chart.scale.thousand': ',',
|
|
200
|
+
'chart.scale.decimals': null,
|
|
201
|
+
'chart.noredraw': false,
|
|
202
|
+
'chart.events.click': null,
|
|
203
|
+
'chart.events.mousemove': null,
|
|
204
|
+
'chart.noxaxis': false,
|
|
205
|
+
'chart.noyaxis': false,
|
|
206
|
+
'chart.noaxes': false,
|
|
207
|
+
'chart.noxtickmarks': false,
|
|
208
|
+
'chart.noytickmarks': false,
|
|
209
|
+
'chart.numyticks': data.length,
|
|
210
|
+
'chart.numxticks': 10
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check for support
|
|
214
|
+
if (!this.canvas) {
|
|
215
|
+
alert('[HBAR] No canvas support');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (i=0,len=this.data.length; i<len; ++i) {
|
|
220
|
+
if (typeof this.data[i] == 'object') {
|
|
221
|
+
this.stackedOrGrouped = true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Create the dollar objects so that functions can be added to them
|
|
228
|
+
*/
|
|
229
|
+
var linear_data = RGraph.arrayLinearize(data);
|
|
230
|
+
for (var i=0,len=linear_data.length; i<len; ++i) {
|
|
231
|
+
this['$' + i] = {};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create the linear data array
|
|
238
|
+
*/
|
|
239
|
+
this.data_arr = RGraph.array_linearize(this.data);
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
|
|
244
|
+
* done already
|
|
245
|
+
*/
|
|
246
|
+
if (!this.canvas.__rgraph_aa_translated__) {
|
|
247
|
+
this.context.translate(0.5,0.5);
|
|
248
|
+
|
|
249
|
+
this.canvas.__rgraph_aa_translated__ = true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
// Short variable names
|
|
256
|
+
var RG = RGraph,
|
|
257
|
+
ca = this.canvas,
|
|
258
|
+
co = ca.getContext('2d'),
|
|
259
|
+
prop = this.properties,
|
|
260
|
+
pa = RG.Path,
|
|
261
|
+
pa2 = RG.path2,
|
|
262
|
+
win = window,
|
|
263
|
+
doc = document,
|
|
264
|
+
ma = Math
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* "Decorate" the object with the generic effects if the effects library has been included
|
|
270
|
+
*/
|
|
271
|
+
if (RG.Effects && typeof RG.Effects.decorate === 'function') {
|
|
272
|
+
RG.Effects.decorate(this);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* A setter
|
|
280
|
+
*
|
|
281
|
+
* @param name string The name of the property to set
|
|
282
|
+
* @param value mixed The value of the property
|
|
283
|
+
*/
|
|
284
|
+
this.set =
|
|
285
|
+
this.Set = function (name)
|
|
286
|
+
{
|
|
287
|
+
var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* the number of arguments is only one and it's an
|
|
291
|
+
* object - parse it for configuration data and return.
|
|
292
|
+
*/
|
|
293
|
+
if (arguments.length === 1 && typeof name === 'object') {
|
|
294
|
+
RG.parseObjectStyleConfig(this, name);
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* This should be done first - prepend the propertyy name with "chart." if necessary
|
|
303
|
+
*/
|
|
304
|
+
if (name.substr(0,6) != 'chart.') {
|
|
305
|
+
name = 'chart.' + name;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
// Convert uppercase letters to dot+lower case letter
|
|
311
|
+
name = name.replace(/([A-Z])/g, function (str)
|
|
312
|
+
{
|
|
313
|
+
return '.' + String(RegExp.$1).toLowerCase()
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
if (name == 'chart.labels.abovebar') {
|
|
317
|
+
name = 'chart.labels.above';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
prop[name] = value;
|
|
321
|
+
|
|
322
|
+
return this;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* A getter
|
|
330
|
+
*
|
|
331
|
+
* @param name string The name of the property to get
|
|
332
|
+
*/
|
|
333
|
+
this.get =
|
|
334
|
+
this.Get = function (name)
|
|
335
|
+
{
|
|
336
|
+
/**
|
|
337
|
+
* This should be done first - prepend the property name with "chart." if necessary
|
|
338
|
+
*/
|
|
339
|
+
if (name.substr(0,6) != 'chart.') {
|
|
340
|
+
name = 'chart.' + name;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Convert uppercase letters to dot+lower case letter
|
|
344
|
+
name = name.replace(/([A-Z])/g, function (str)
|
|
345
|
+
{
|
|
346
|
+
return '.' + String(RegExp.$1).toLowerCase()
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
if (name == 'chart.labels.abovebar') {
|
|
351
|
+
name = 'chart.labels.above';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return prop[name];
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* The function you call to draw the bar chart
|
|
362
|
+
*/
|
|
363
|
+
this.draw =
|
|
364
|
+
this.Draw = function ()
|
|
365
|
+
{
|
|
366
|
+
/**
|
|
367
|
+
* Fire the onbeforedraw event
|
|
368
|
+
*/
|
|
369
|
+
RG.FireCustomEvent(this, 'onbeforedraw');
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Parse the colors. This allows for simple gradient syntax
|
|
374
|
+
*/
|
|
375
|
+
if (!this.colorsParsed) {
|
|
376
|
+
this.parseColors();
|
|
377
|
+
|
|
378
|
+
// Don't want to do this again
|
|
379
|
+
this.colorsParsed = true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Accomodate autosizing the left gutter
|
|
389
|
+
*/
|
|
390
|
+
if (prop['chart.gutter.left.autosize']) {
|
|
391
|
+
var len = 0;
|
|
392
|
+
var labels = prop['chart.labels'];
|
|
393
|
+
var font = prop['chart.text.font'];
|
|
394
|
+
var size = prop['chart.text.size'];
|
|
395
|
+
|
|
396
|
+
for (var i=0; i<labels.length; i+=1) {
|
|
397
|
+
var length = RG.measureText(labels[i], false, font, size)[0] || 0
|
|
398
|
+
len = ma.max(len, length);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
prop['chart.gutter.left'] = len + 10;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* This is new in May 2011 and facilitates indiviual gutter settings,
|
|
418
|
+
* eg chart.gutter.left
|
|
419
|
+
*/
|
|
420
|
+
this.gutterLeft = prop['chart.gutter.left'];
|
|
421
|
+
this.gutterRight = prop['chart.gutter.right'];
|
|
422
|
+
this.gutterTop = prop['chart.gutter.top'];
|
|
423
|
+
this.gutterBottom = prop['chart.gutter.bottom'];
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Stop the coords array from growing uncontrollably
|
|
427
|
+
*/
|
|
428
|
+
this.coords = [];
|
|
429
|
+
this.coords2 = [];
|
|
430
|
+
this.coordsText = [];
|
|
431
|
+
this.max = 0;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Check for chart.xmin in stacked charts
|
|
435
|
+
*/
|
|
436
|
+
if (prop['chart.xmin'] > 0 && prop['chart.grouping'] == 'stacked') {
|
|
437
|
+
alert('[HBAR] Using chart.xmin is not supported with stacked charts, resetting chart.xmin to zero');
|
|
438
|
+
this.Set('chart.xmin', 0);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Work out a few things. They need to be here because they depend on things you can change before you
|
|
443
|
+
* call Draw() but after you instantiate the object
|
|
444
|
+
*/
|
|
445
|
+
this.graphwidth = ca.width - this.gutterLeft - this.gutterRight;
|
|
446
|
+
this.graphheight = ca.height - this.gutterTop - this.gutterBottom;
|
|
447
|
+
this.halfgrapharea = this.grapharea / 2;
|
|
448
|
+
this.halfTextHeight = prop['chart.text.size'] / 2;
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
// Progressively Draw the chart
|
|
456
|
+
RG.Background.draw(this);
|
|
457
|
+
|
|
458
|
+
this.Drawbars();
|
|
459
|
+
this.DrawAxes();
|
|
460
|
+
this.DrawLabels();
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
// Draw the key if necessary
|
|
464
|
+
if (prop['chart.key'] && prop['chart.key'].length) {
|
|
465
|
+
RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Setup the context menu if required
|
|
472
|
+
*/
|
|
473
|
+
if (prop['chart.contextmenu']) {
|
|
474
|
+
RG.ShowContext(this);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Draw "in graph" labels
|
|
481
|
+
*/
|
|
482
|
+
RG.DrawInGraphLabels(this);
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* This function enables resizing
|
|
487
|
+
*/
|
|
488
|
+
if (prop['chart.resizable']) {
|
|
489
|
+
RG.AllowResizing(this);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* This installs the event listeners
|
|
495
|
+
*/
|
|
496
|
+
RG.InstallEventListeners(this);
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Fire the onfirstdraw event
|
|
501
|
+
*/
|
|
502
|
+
if (this.firstDraw) {
|
|
503
|
+
RG.fireCustomEvent(this, 'onfirstdraw');
|
|
504
|
+
this.firstDraw = false;
|
|
505
|
+
this.firstDrawFunc();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Fire the RGraph ondraw event
|
|
512
|
+
*/
|
|
513
|
+
RG.FireCustomEvent(this, 'ondraw');
|
|
514
|
+
|
|
515
|
+
return this;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Used in chaining. Runs a function there and then - not waiting for
|
|
522
|
+
* the events to fire (eg the onbeforedraw event)
|
|
523
|
+
*
|
|
524
|
+
* @param function func The function to execute
|
|
525
|
+
*/
|
|
526
|
+
this.exec = function (func)
|
|
527
|
+
{
|
|
528
|
+
func(this);
|
|
529
|
+
|
|
530
|
+
return this;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* This draws the axes
|
|
538
|
+
*/
|
|
539
|
+
this.drawAxes =
|
|
540
|
+
this.DrawAxes = function ()
|
|
541
|
+
{
|
|
542
|
+
var halfway = ma.round((this.graphwidth / 2) + this.gutterLeft);
|
|
543
|
+
|
|
544
|
+
co.beginPath();
|
|
545
|
+
|
|
546
|
+
co.lineWidth = prop['chart.axis.linewidth'] ? prop['chart.axis.linewidth'] + 0.001 : 1.001;
|
|
547
|
+
co.strokeStyle = prop['chart.axis.color'];
|
|
548
|
+
|
|
549
|
+
// Draw the Y axis
|
|
550
|
+
if (prop['chart.noyaxis'] == false && prop['chart.noaxes'] == false) {
|
|
551
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
552
|
+
co.moveTo(halfway, this.gutterTop);
|
|
553
|
+
co.lineTo(halfway, ca.height - this.gutterBottom);
|
|
554
|
+
|
|
555
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
556
|
+
co.moveTo(ca.width - this.gutterRight, this.gutterTop);
|
|
557
|
+
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
|
|
558
|
+
|
|
559
|
+
} else {
|
|
560
|
+
co.moveTo(this.gutterLeft, this.gutterTop);
|
|
561
|
+
co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Draw the X axis
|
|
566
|
+
if (prop['chart.noxaxis'] == false && prop['chart.noaxes'] == false) {
|
|
567
|
+
co.moveTo(this.gutterLeft +0.001, ca.height - this.gutterBottom + 0.001);
|
|
568
|
+
co.lineTo(ca.width - this.gutterRight + 0.001, ca.height - this.gutterBottom + 0.001);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Draw the Y tickmarks
|
|
572
|
+
if ( prop['chart.noytickmarks'] == false
|
|
573
|
+
&& prop['chart.noyaxis'] == false
|
|
574
|
+
&& prop['chart.numyticks'] > 0
|
|
575
|
+
&& prop['chart.noaxes'] == false
|
|
576
|
+
) {
|
|
577
|
+
|
|
578
|
+
var yTickGap = (ca.height - this.gutterTop - this.gutterBottom) / (prop['chart.numyticks'] > 0 ? prop['chart.numyticks'] : this.data.length);
|
|
579
|
+
|
|
580
|
+
for (y=this.gutterTop; y<(ca.height - this.gutterBottom - 1); y+=yTickGap) {
|
|
581
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
582
|
+
co.moveTo(halfway + 3, ma.round(y));
|
|
583
|
+
co.lineTo(halfway - 3, ma.round(y));
|
|
584
|
+
|
|
585
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
586
|
+
co.moveTo(ca.width - this.gutterRight, ma.round(y));
|
|
587
|
+
co.lineTo(ca.width - this.gutterRight + 3, ma.round(y));
|
|
588
|
+
|
|
589
|
+
} else {
|
|
590
|
+
co.moveTo(this.gutterLeft, ma.round(y));
|
|
591
|
+
co.lineTo( this.gutterLeft - 3, ma.round(y));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// If the X axis isn't being shown draw the end tick
|
|
596
|
+
if (prop['chart.noxaxis'] == true) {
|
|
597
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
598
|
+
co.moveTo(halfway + 3, ma.round(y));
|
|
599
|
+
co.lineTo(halfway - 3, ma.round(y));
|
|
600
|
+
|
|
601
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
602
|
+
co.moveTo(ca.width - this.gutterRight, ma.round(y));
|
|
603
|
+
co.lineTo(ca.width - this.gutterRight + 3, ma.round(y));
|
|
604
|
+
|
|
605
|
+
} else {
|
|
606
|
+
co.moveTo(this.gutterLeft, ma.round(y));
|
|
607
|
+
co.lineTo( this.gutterLeft - 3, ma.round(y));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
// Draw the X tickmarks
|
|
614
|
+
if ( prop['chart.noxtickmarks'] == false
|
|
615
|
+
&& prop['chart.noxaxis'] == false
|
|
616
|
+
&& prop['chart.numxticks'] > 0
|
|
617
|
+
&& prop['chart.noaxes'] == false) {
|
|
618
|
+
|
|
619
|
+
xTickGap = (ca.width - this.gutterLeft - this.gutterRight ) / prop['chart.numxticks'];
|
|
620
|
+
yStart = ca.height - this.gutterBottom;
|
|
621
|
+
yEnd = (ca.height - this.gutterBottom) + 3;
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
var i = prop['chart.numxticks']
|
|
628
|
+
|
|
629
|
+
while(i--) {
|
|
630
|
+
|
|
631
|
+
var x = ca.width - this.gutterRight - (i * xTickGap);
|
|
632
|
+
|
|
633
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
634
|
+
x -= xTickGap;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
co.moveTo(ma.round(x), yStart);
|
|
638
|
+
co.lineTo(ma.round(x), yEnd);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
if (prop['chart.yaxispos'] === 'center') {
|
|
644
|
+
var i = 5; while (i--) {
|
|
645
|
+
var x = this.gutterLeft + (xTickGap * i);
|
|
646
|
+
|
|
647
|
+
co.moveTo(ma.round(x), yStart);
|
|
648
|
+
co.lineTo(ma.round(x), yEnd);
|
|
649
|
+
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
// If the Y axis isn't being shown draw the end tick
|
|
658
|
+
if (prop['chart.noyaxis'] == true) {
|
|
659
|
+
co.moveTo(this.gutterLeft, ma.round(yStart));
|
|
660
|
+
co.lineTo( this.gutterLeft, ma.round(yEnd));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
co.stroke();
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Reset the linewidth
|
|
667
|
+
*/
|
|
668
|
+
co.lineWidth = 1;
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* This draws the labels for the graph
|
|
676
|
+
*/
|
|
677
|
+
this.drawLabels =
|
|
678
|
+
this.DrawLabels = function ()
|
|
679
|
+
{
|
|
680
|
+
var units_pre = prop['chart.units.pre'];
|
|
681
|
+
var units_post = prop['chart.units.post'];
|
|
682
|
+
var text_size = prop['chart.text.size'];
|
|
683
|
+
var font = prop['chart.text.font'];
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Set the units to blank if they're to be used for ingraph labels only
|
|
689
|
+
*/
|
|
690
|
+
if (prop['chart.units.ingraph']) {
|
|
691
|
+
units_pre = '';
|
|
692
|
+
units_post = '';
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Draw the X axis labels
|
|
698
|
+
*/
|
|
699
|
+
if (prop['chart.xlabels']) {
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Specific X labels
|
|
703
|
+
*/
|
|
704
|
+
if (RG.isArray(prop['chart.xlabels.specific'])) {
|
|
705
|
+
|
|
706
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
707
|
+
|
|
708
|
+
var halfGraphWidth = this.graphwidth / 2;
|
|
709
|
+
var labels = prop['chart.xlabels.specific'];
|
|
710
|
+
var interval = (this.graphwidth / 2) / (labels.length - 1);
|
|
711
|
+
|
|
712
|
+
co.fillStyle = prop['chart.text.color'];
|
|
713
|
+
|
|
714
|
+
for (var i=0; i<labels.length; i+=1) {
|
|
715
|
+
RG.Text2(this, {'font':font,
|
|
716
|
+
'size':text_size,
|
|
717
|
+
'x':this.gutterLeft + halfGraphWidth + (interval * i),
|
|
718
|
+
'y':ca.height - this.gutterBottom,
|
|
719
|
+
'text':labels[i],
|
|
720
|
+
'valign':'top',
|
|
721
|
+
'halign':'center',
|
|
722
|
+
'tag': 'scale'});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
for (var i=(labels.length - 1); i>0; i-=1) {
|
|
726
|
+
RG.Text2(this, {'font':font,
|
|
727
|
+
'size':text_size,
|
|
728
|
+
'x':this.gutterLeft + (interval * (labels.length - i - 1)),
|
|
729
|
+
'y':ca.height - this.gutterBottom,
|
|
730
|
+
'text':labels[i],
|
|
731
|
+
'valign':'top',
|
|
732
|
+
'halign':'center',
|
|
733
|
+
'tag': 'scale'});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
737
|
+
|
|
738
|
+
var labels = prop['chart.xlabels.specific'];
|
|
739
|
+
var interval = this.graphwidth / (labels.length - 1);
|
|
740
|
+
|
|
741
|
+
co.fillStyle = prop['chart.text.color'];
|
|
742
|
+
|
|
743
|
+
for (var i=0; i<labels.length; i+=1) {
|
|
744
|
+
RG.Text2(this, {'font':font,
|
|
745
|
+
'size':text_size,
|
|
746
|
+
'x':this.gutterLeft + (interval * i),
|
|
747
|
+
'y':ca.height - this.gutterBottom,
|
|
748
|
+
'text':labels[labels.length - i - 1],
|
|
749
|
+
'valign':'top',
|
|
750
|
+
'halign':'center',
|
|
751
|
+
'tag': 'scale'});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
} else {
|
|
755
|
+
|
|
756
|
+
var labels = prop['chart.xlabels.specific'];
|
|
757
|
+
var interval = this.graphwidth / (labels.length - 1);
|
|
758
|
+
|
|
759
|
+
co.fillStyle = prop['chart.text.color'];
|
|
760
|
+
|
|
761
|
+
for (var i=0; i<labels.length; i+=1) {
|
|
762
|
+
RG.Text2(this, {'font':font,
|
|
763
|
+
'size':text_size,
|
|
764
|
+
'x':this.gutterLeft + (interval * i),
|
|
765
|
+
'y':ca.height - this.gutterBottom,
|
|
766
|
+
'text':labels[i],
|
|
767
|
+
'valign':'top',
|
|
768
|
+
'halign':'center',
|
|
769
|
+
'tag': 'scale'});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Draw an X scale
|
|
775
|
+
*/
|
|
776
|
+
} else {
|
|
777
|
+
|
|
778
|
+
var gap = 7;
|
|
779
|
+
|
|
780
|
+
co.beginPath();
|
|
781
|
+
co.fillStyle = prop['chart.text.color'];
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
785
|
+
|
|
786
|
+
for (var i=0; i<this.scale2.labels.length; ++i) {
|
|
787
|
+
RG.Text2(this, {'font':font,
|
|
788
|
+
'size':text_size,
|
|
789
|
+
'x':this.gutterLeft + (this.graphwidth / 2) - ((this.graphwidth / 2) * ((i+1)/this.scale2.labels.length)),
|
|
790
|
+
'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
|
|
791
|
+
'text':'-' + this.scale2.labels[i],
|
|
792
|
+
'valign':'center',
|
|
793
|
+
'halign':'center',
|
|
794
|
+
'tag': 'scale'});
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
for (var i=0; i<this.scale2.labels.length; ++i) {
|
|
798
|
+
RG.Text2(this, {'font':font,
|
|
799
|
+
'size':text_size,
|
|
800
|
+
'x':this.gutterLeft + ((this.graphwidth / 2) * ((i+1)/this.scale2.labels.length)) + (this.graphwidth / 2),
|
|
801
|
+
'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
|
|
802
|
+
'text':this.scale2.labels[i],
|
|
803
|
+
'valign':'center',
|
|
804
|
+
'halign':'center',
|
|
805
|
+
'tag': 'scale'});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
}else if (prop['chart.yaxispos'] == 'right') {
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
|
|
812
|
+
RG.Text2(this, {'font':font,
|
|
813
|
+
'size':text_size,
|
|
814
|
+
'x':this.gutterLeft + (i * (this.graphwidth / len)),
|
|
815
|
+
'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
|
|
816
|
+
'text':'-' + this.scale2.labels[len - 1 - i],
|
|
817
|
+
'valign':'center',
|
|
818
|
+
'halign':'center',
|
|
819
|
+
'tag': 'scale'
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
} else {
|
|
824
|
+
for (var i=0,len=this.scale2.labels.length; i<len; ++i) {
|
|
825
|
+
RG.Text2(this, {'font':font,
|
|
826
|
+
'size':text_size,
|
|
827
|
+
'x':this.gutterLeft + (this.graphwidth * ((i+1)/len)),
|
|
828
|
+
'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
|
|
829
|
+
'text':this.scale2.labels[i],
|
|
830
|
+
'valign':'center',
|
|
831
|
+
'halign':'center',
|
|
832
|
+
'tag': 'scale'
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* If xmin is not zero - draw that
|
|
839
|
+
*/
|
|
840
|
+
if (prop['chart.xmin'] > 0 || prop['chart.noyaxis'] == true) {
|
|
841
|
+
|
|
842
|
+
var x = prop['chart.yaxispos'] == 'center' ? this.gutterLeft + (this.graphwidth / 2): this.gutterLeft;
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Y axis on the right
|
|
846
|
+
*/
|
|
847
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
848
|
+
var x = ca.width - this.gutterRight;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
RG.Text2(this, {'font':font,
|
|
852
|
+
'size':text_size,
|
|
853
|
+
'x':x,
|
|
854
|
+
'y':this.gutterTop + this.halfTextHeight + this.graphheight + gap,
|
|
855
|
+
'text':RG.number_format(this, prop['chart.xmin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
|
856
|
+
'valign':'center',
|
|
857
|
+
'halign':'center',
|
|
858
|
+
'tag': 'scale'
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
co.fill();
|
|
863
|
+
co.stroke();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* The Y axis labels
|
|
869
|
+
*/
|
|
870
|
+
if (typeof(prop['chart.labels']) == 'object') {
|
|
871
|
+
|
|
872
|
+
var xOffset = 5,
|
|
873
|
+
font = prop['chart.text.font'],
|
|
874
|
+
color = prop['chart.labels.color'] || prop['chart.text.color'],
|
|
875
|
+
bold = prop['chart.labels.bold']
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
// Draw the X axis labels
|
|
879
|
+
co.fillStyle = color;
|
|
880
|
+
|
|
881
|
+
// How high is each bar
|
|
882
|
+
var barHeight = (ca.height - this.gutterTop - this.gutterBottom ) / prop['chart.labels'].length;
|
|
883
|
+
|
|
884
|
+
// Reset the yTickGap
|
|
885
|
+
yTickGap = (ca.height - this.gutterTop - this.gutterBottom) / prop['chart.labels'].length
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* If the Y axis is on the right set the alignment and the X position, otherwise on the left
|
|
889
|
+
*/
|
|
890
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
891
|
+
var x = ca.width - this.gutterRight + xOffset;
|
|
892
|
+
var halign = 'left'
|
|
893
|
+
} else {
|
|
894
|
+
var x = this.gutterLeft - xOffset;
|
|
895
|
+
var halign = 'right'
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Draw the X tickmarks
|
|
899
|
+
var i=0;
|
|
900
|
+
for (y=this.gutterTop + (yTickGap / 2); y<=ca.height - this.gutterBottom; y+=yTickGap) {
|
|
901
|
+
|
|
902
|
+
RG.text2(this, {
|
|
903
|
+
'font': font,
|
|
904
|
+
'size': prop['chart.text.size'],
|
|
905
|
+
'bold': bold,
|
|
906
|
+
'x': x,
|
|
907
|
+
'y': y,
|
|
908
|
+
'text': String(prop['chart.labels'][i++]),
|
|
909
|
+
'halign': halign,
|
|
910
|
+
'valign': 'center',
|
|
911
|
+
'tag': 'labels'
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* This function draws the bars
|
|
922
|
+
*/
|
|
923
|
+
this.drawbars =
|
|
924
|
+
this.Drawbars = function ()
|
|
925
|
+
{
|
|
926
|
+
co.lineWidth = prop['chart.linewidth'];
|
|
927
|
+
co.strokeStyle = prop['chart.strokestyle'];
|
|
928
|
+
co.fillStyle = prop['chart.colors'][0];
|
|
929
|
+
var prevX = 0;
|
|
930
|
+
var prevY = 0;
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Work out the max value
|
|
934
|
+
*/
|
|
935
|
+
if (prop['chart.xmax']) {
|
|
936
|
+
|
|
937
|
+
this.scale2 = RG.getScale2(this, {'max':prop['chart.xmax'],
|
|
938
|
+
'min':prop['chart.xmin'],
|
|
939
|
+
'scale.decimals':Number(prop['chart.scale.decimals']),
|
|
940
|
+
'scale.point':prop['chart.scale.point'],
|
|
941
|
+
'scale.thousand':prop['chart.scale.thousand'],
|
|
942
|
+
'scale.round':prop['chart.scale.round'],
|
|
943
|
+
'units.pre':prop['chart.units.pre'],
|
|
944
|
+
'units.post':prop['chart.units.post'],
|
|
945
|
+
'ylabels.count':prop['chart.xlabels.count'],
|
|
946
|
+
'strict':true
|
|
947
|
+
});
|
|
948
|
+
this.max = this.scale2.max;
|
|
949
|
+
|
|
950
|
+
} else {
|
|
951
|
+
|
|
952
|
+
var grouping = prop['chart.grouping'];
|
|
953
|
+
|
|
954
|
+
for (i=0; i<this.data.length; ++i) {
|
|
955
|
+
if (typeof(this.data[i]) == 'object') {
|
|
956
|
+
var value = grouping == 'grouped' ? Number(RG.array_max(this.data[i], true)) : Number(RG.array_sum(this.data[i])) ;
|
|
957
|
+
} else {
|
|
958
|
+
var value = Number(Math.abs(this.data[i]));
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
this.max = ma.max(Math.abs(this.max), Math.abs(value));
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
this.scale2 = RG.getScale2(this, {'max':this.max,
|
|
965
|
+
'min':prop['chart.xmin'],
|
|
966
|
+
'scale.decimals':Number(prop['chart.scale.decimals']),
|
|
967
|
+
'scale.point':prop['chart.scale.point'],
|
|
968
|
+
'scale.thousand':prop['chart.scale.thousand'],
|
|
969
|
+
'scale.round':prop['chart.scale.round'],
|
|
970
|
+
'units.pre':prop['chart.units.pre'],
|
|
971
|
+
'units.post':prop['chart.units.post'],
|
|
972
|
+
'ylabels.count':prop['chart.xlabels.count']
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
this.max = this.scale2.max;
|
|
977
|
+
this.min = this.scale2.min;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (prop['chart.scale.decimals'] == null && Number(this.max) == 1) {
|
|
981
|
+
this.Set('chart.scale.decimals', 1);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* This is here to facilitate sequential colors
|
|
986
|
+
*/
|
|
987
|
+
var colorIdx = 0;
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* The bars are drawn HERE
|
|
994
|
+
*/
|
|
995
|
+
var graphwidth = (ca.width - this.gutterLeft - this.gutterRight);
|
|
996
|
+
var halfwidth = graphwidth / 2;
|
|
997
|
+
|
|
998
|
+
for (i=0,len=this.data.length; i<len; ++i) {
|
|
999
|
+
|
|
1000
|
+
// Work out the width and height
|
|
1001
|
+
var width = ma.abs((this.data[i] / this.max) * graphwidth);
|
|
1002
|
+
var height = this.graphheight / this.data.length;
|
|
1003
|
+
|
|
1004
|
+
var orig_height = height;
|
|
1005
|
+
|
|
1006
|
+
var x = this.gutterLeft;
|
|
1007
|
+
var y = this.gutterTop + (i * height);
|
|
1008
|
+
var vmargin = prop['chart.vmargin'];
|
|
1009
|
+
|
|
1010
|
+
// Account for the Y axis being on the right hand side
|
|
1011
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
1012
|
+
x = ca.width - this.gutterRight - ma.abs(width);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
|
|
1016
|
+
if (width < 0) {
|
|
1017
|
+
x -= width;
|
|
1018
|
+
width = ma.abs(width);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Turn on the shadow if need be
|
|
1023
|
+
*/
|
|
1024
|
+
if (prop['chart.shadow']) {
|
|
1025
|
+
co.shadowColor = prop['chart.shadow.color'];
|
|
1026
|
+
co.shadowBlur = prop['chart.shadow.blur'];
|
|
1027
|
+
co.shadowOffsetX = prop['chart.shadow.offsetx'];
|
|
1028
|
+
co.shadowOffsetY = prop['chart.shadow.offsety'];
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Draw the bar
|
|
1033
|
+
*/
|
|
1034
|
+
co.beginPath();
|
|
1035
|
+
if (typeof(this.data[i]) == 'number') {
|
|
1036
|
+
|
|
1037
|
+
var barHeight = height - (2 * vmargin);
|
|
1038
|
+
var barWidth = ((this.data[i] - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * this.graphwidth;
|
|
1039
|
+
var barX = this.gutterLeft;
|
|
1040
|
+
|
|
1041
|
+
// Account for Y axis pos
|
|
1042
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
1043
|
+
barWidth /= 2;
|
|
1044
|
+
barX += halfwidth;
|
|
1045
|
+
|
|
1046
|
+
if (this.data[i] < 0) {
|
|
1047
|
+
barWidth = (Math.abs(this.data[i]) - prop['chart.xmin']) / (this.max - prop['chart.xmin']);
|
|
1048
|
+
barWidth = barWidth * (this.graphwidth / 2);
|
|
1049
|
+
barX = ((this.graphwidth / 2) + this.gutterLeft) - barWidth;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
1053
|
+
|
|
1054
|
+
barWidth = Math.abs(barWidth);
|
|
1055
|
+
barX = ca.width - this.gutterRight - barWidth;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// Set the fill color
|
|
1059
|
+
co.strokeStyle = prop['chart.strokestyle'];
|
|
1060
|
+
co.fillStyle = prop['chart.colors'][0];
|
|
1061
|
+
|
|
1062
|
+
// Sequential colors
|
|
1063
|
+
if (prop['chart.colors.sequential']) {
|
|
1064
|
+
co.fillStyle = prop['chart.colors'][colorIdx++];
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
co.strokeRect(barX, this.gutterTop + (i * height) + prop['chart.vmargin'], barWidth, barHeight);
|
|
1068
|
+
co.fillRect(barX, this.gutterTop + (i * height) + prop['chart.vmargin'], barWidth, barHeight);
|
|
1069
|
+
|
|
1070
|
+
this.coords.push([barX,
|
|
1071
|
+
y + vmargin,
|
|
1072
|
+
barWidth,
|
|
1073
|
+
height - (2 * vmargin),
|
|
1074
|
+
co.fillStyle,
|
|
1075
|
+
this.data[i],
|
|
1076
|
+
true]);
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Stacked bar chart
|
|
1080
|
+
*/
|
|
1081
|
+
} else if (typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'stacked') {
|
|
1082
|
+
|
|
1083
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
1084
|
+
alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped');
|
|
1085
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
1086
|
+
var x = ca.width - this.gutterRight
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
var barHeight = height - (2 * vmargin);
|
|
1090
|
+
|
|
1091
|
+
if (typeof this.coords2[i] == 'undefined') {
|
|
1092
|
+
this.coords2[i] = [];
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
for (j=0; j<this.data[i].length; ++j) {
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
// Set the fill/stroke colors
|
|
1099
|
+
co.strokeStyle = prop['chart.strokestyle'];
|
|
1100
|
+
co.fillStyle = prop['chart.colors'][j];
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
// Sequential colors
|
|
1104
|
+
if (prop['chart.colors.sequential']) {
|
|
1105
|
+
co.fillStyle = prop['chart.colors'][colorIdx++];
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
var width = (((this.data[i][j]) / (this.max))) * this.graphwidth;
|
|
1110
|
+
var totalWidth = (RG.arraySum(this.data[i]) / this.max) * this.graphwidth;
|
|
1111
|
+
|
|
1112
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
1113
|
+
x -= width;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
co.strokeRect(x, this.gutterTop + prop['chart.vmargin'] + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
|
|
1119
|
+
co.fillRect(x, this.gutterTop + prop['chart.vmargin'] + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Store the coords for tooltips
|
|
1123
|
+
*/
|
|
1124
|
+
|
|
1125
|
+
// The last property of this array is a boolean which tells you whether the value is the last or not
|
|
1126
|
+
this.coords.push([x,
|
|
1127
|
+
y + vmargin,
|
|
1128
|
+
width,
|
|
1129
|
+
height - (2 * vmargin),
|
|
1130
|
+
co.fillStyle,
|
|
1131
|
+
RG.array_sum(this.data[i]),
|
|
1132
|
+
j == (this.data[i].length - 1)
|
|
1133
|
+
]);
|
|
1134
|
+
this.coords2[i].push([x,
|
|
1135
|
+
y + vmargin,
|
|
1136
|
+
width,
|
|
1137
|
+
height - (2 * vmargin),
|
|
1138
|
+
co.fillStyle,
|
|
1139
|
+
RG.array_sum(this.data[i]),
|
|
1140
|
+
j == (this.data[i].length - 1)
|
|
1141
|
+
]);
|
|
1142
|
+
|
|
1143
|
+
if (prop['chart.yaxispos'] !== 'right') {
|
|
1144
|
+
x += width;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* A grouped bar chart
|
|
1150
|
+
*/
|
|
1151
|
+
} else if (typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'grouped') {
|
|
1152
|
+
|
|
1153
|
+
var vmarginGrouped = prop['chart.vmargin.grouped'];
|
|
1154
|
+
var individualBarHeight = ((height - (2 * vmargin) - ((this.data[i].length - 1) * vmarginGrouped)) / this.data[i].length)
|
|
1155
|
+
|
|
1156
|
+
if (typeof this.coords2[i] == 'undefined') {
|
|
1157
|
+
this.coords2[i] = [];
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
for (j=0; j<this.data[i].length; ++j) {
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Turn on the shadow if need be
|
|
1165
|
+
*/
|
|
1166
|
+
if (prop['chart.shadow']) {
|
|
1167
|
+
RG.setShadow(this, prop['chart.shadow.color'], prop['chart.shadow.offsetx'], prop['chart.shadow.offsety'], prop['chart.shadow.blur']);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Set the fill/stroke colors
|
|
1171
|
+
co.strokeStyle = prop['chart.strokestyle'];
|
|
1172
|
+
co.fillStyle = prop['chart.colors'][j];
|
|
1173
|
+
|
|
1174
|
+
// Sequential colors
|
|
1175
|
+
if (prop['chart.colors.sequential']) {
|
|
1176
|
+
co.fillStyle = prop['chart.colors'][colorIdx++];
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
var startY = this.gutterTop + (height * i) + (individualBarHeight * j) + vmargin + (vmarginGrouped * j);
|
|
1182
|
+
var width = ((this.data[i][j] - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * (ca.width - this.gutterLeft - this.gutterRight );
|
|
1183
|
+
var startX = this.gutterLeft;
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
// Account for the Y axis being in the middle
|
|
1188
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
1189
|
+
width /= 2;
|
|
1190
|
+
startX += halfwidth;
|
|
1191
|
+
|
|
1192
|
+
// Account for the Y axis being on the right
|
|
1193
|
+
} else if (prop['chart.yaxispos'] == 'right') {
|
|
1194
|
+
width = ma.abs(width);
|
|
1195
|
+
startX = ca.width - this.gutterRight - ma.abs(width);;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
if (width < 0) {
|
|
1199
|
+
startX += width;
|
|
1200
|
+
width *= -1;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
co.strokeRect(startX, startY, width, individualBarHeight);
|
|
1204
|
+
co.fillRect(startX, startY, width, individualBarHeight);
|
|
1205
|
+
|
|
1206
|
+
this.coords.push([startX,
|
|
1207
|
+
startY,
|
|
1208
|
+
width,
|
|
1209
|
+
individualBarHeight,
|
|
1210
|
+
co.fillStyle,
|
|
1211
|
+
this.data[i][j],
|
|
1212
|
+
true]);
|
|
1213
|
+
|
|
1214
|
+
this.coords2[i].push([startX,
|
|
1215
|
+
startY,
|
|
1216
|
+
width,
|
|
1217
|
+
individualBarHeight,
|
|
1218
|
+
co.fillStyle,
|
|
1219
|
+
this.data[i][j],
|
|
1220
|
+
true]);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
startY += vmargin;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
co.closePath();
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
co.stroke();
|
|
1230
|
+
co.fill();
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Now the bars are stroke()ed, turn off the shadow
|
|
1236
|
+
*/
|
|
1237
|
+
RG.NoShadow(this);
|
|
1238
|
+
|
|
1239
|
+
this.RedrawBars();
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* This function goes over the bars after they been drawn, so that upwards shadows are underneath the bars
|
|
1247
|
+
*/
|
|
1248
|
+
this.redrawBars =
|
|
1249
|
+
this.RedrawBars = function ()
|
|
1250
|
+
{
|
|
1251
|
+
if (prop['chart.noredraw']) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
var coords = this.coords;
|
|
1256
|
+
|
|
1257
|
+
var font = prop['chart.text.font'];
|
|
1258
|
+
var size = prop['chart.text.size'];
|
|
1259
|
+
var color = prop['chart.text.color'];
|
|
1260
|
+
|
|
1261
|
+
RG.noShadow(this);
|
|
1262
|
+
co.strokeStyle = prop['chart.strokestyle'];
|
|
1263
|
+
|
|
1264
|
+
for (var i=0; i<coords.length; ++i) {
|
|
1265
|
+
|
|
1266
|
+
if (prop['chart.shadow']) {
|
|
1267
|
+
co.beginPath();
|
|
1268
|
+
co.strokeStyle = prop['chart.strokestyle'];
|
|
1269
|
+
co.fillStyle = coords[i][4];
|
|
1270
|
+
co.lineWidth = prop['chart.linewidth'];
|
|
1271
|
+
co.rect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
|
|
1272
|
+
co.stroke();
|
|
1273
|
+
co.fill();
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Draw labels "above" the bar
|
|
1278
|
+
*/
|
|
1279
|
+
var halign = 'left';
|
|
1280
|
+
if (prop['chart.labels.above'] && coords[i][6]) {
|
|
1281
|
+
|
|
1282
|
+
co.fillStyle = prop['chart.text.color'];
|
|
1283
|
+
co.strokeStyle = 'black';
|
|
1284
|
+
RG.noShadow(this);
|
|
1285
|
+
|
|
1286
|
+
var border = (coords[i][0] + coords[i][2] + 7 + co.measureText(prop['chart.units.pre'] + this.coords[i][5] + prop['chart.units.post']).width) > ca.width ? true : false;
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Default to the value - then check for specific labels
|
|
1290
|
+
*/
|
|
1291
|
+
var text = RG.numberFormat(this, (this.coords[i][5]).toFixed(prop['chart.labels.above.decimals']), prop['chart.units.pre'], prop['chart.units.post']);
|
|
1292
|
+
if (typeof prop['chart.labels.above.specific'] == 'object' && prop['chart.labels.above.specific'] && prop['chart.labels.above.specific'][i]) {
|
|
1293
|
+
text = prop['chart.labels.above.specific'][i];
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
var x = coords[i][0] + coords[i][2] + 5;
|
|
1297
|
+
var y = coords[i][1] + (coords[i][3] / 2);
|
|
1298
|
+
|
|
1299
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
1300
|
+
x = coords[i][0] - 5;
|
|
1301
|
+
halign = 'right';
|
|
1302
|
+
} else if (prop['chart.yaxispos'] === 'center' && this.data_arr[i] < 0) {
|
|
1303
|
+
x = coords[i][0] - 5;
|
|
1304
|
+
halign = 'right';
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
RG.Text2(this, {'font':font,
|
|
1308
|
+
'size':size,
|
|
1309
|
+
'x':x,
|
|
1310
|
+
'y':y,
|
|
1311
|
+
'text': text,
|
|
1312
|
+
'valign':'center',
|
|
1313
|
+
'halign': halign,
|
|
1314
|
+
'tag': 'labels.above'
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
/**
|
|
1324
|
+
* This function can be used to get the appropriate bar information (if any)
|
|
1325
|
+
*
|
|
1326
|
+
* @param e Event object
|
|
1327
|
+
* @return Appriate bar information (if any)
|
|
1328
|
+
*/
|
|
1329
|
+
this.getShape =
|
|
1330
|
+
this.getBar = function (e)
|
|
1331
|
+
{
|
|
1332
|
+
var mouseCoords = RG.getMouseXY(e);
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* Loop through the bars determining if the mouse is over a bar
|
|
1336
|
+
*/
|
|
1337
|
+
for (var i=0,len=this.coords.length; i<len; i++) {
|
|
1338
|
+
|
|
1339
|
+
var mouseX = mouseCoords[0]; // In relation to the canvas
|
|
1340
|
+
var mouseY = mouseCoords[1]; // In relation to the canvas
|
|
1341
|
+
var left = this.coords[i][0];
|
|
1342
|
+
var top = this.coords[i][1];
|
|
1343
|
+
var width = this.coords[i][2];
|
|
1344
|
+
var height = this.coords[i][3];
|
|
1345
|
+
var idx = i;
|
|
1346
|
+
|
|
1347
|
+
if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
|
|
1348
|
+
|
|
1349
|
+
var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
|
|
1350
|
+
|
|
1351
|
+
return {
|
|
1352
|
+
0: this, 'object': this,
|
|
1353
|
+
1: left, 'x': left,
|
|
1354
|
+
2: top, 'y': top,
|
|
1355
|
+
3: width, 'width': width,
|
|
1356
|
+
4: height, 'height': height,
|
|
1357
|
+
5: idx, 'index': idx,
|
|
1358
|
+
'tooltip': tooltip
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* When you click on the chart, this method can return the X value at that point. It works for any point on the
|
|
1369
|
+
* chart (that is inside the gutters) - not just points within the Bars.
|
|
1370
|
+
*
|
|
1371
|
+
* @param object e The event object
|
|
1372
|
+
*/
|
|
1373
|
+
this.getValue = function (arg)
|
|
1374
|
+
{
|
|
1375
|
+
if (arg.length == 2) {
|
|
1376
|
+
var mouseX = arg[0];
|
|
1377
|
+
var mouseY = arg[1];
|
|
1378
|
+
} else {
|
|
1379
|
+
var mouseCoords = RG.getMouseXY(arg);
|
|
1380
|
+
var mouseX = mouseCoords[0];
|
|
1381
|
+
var mouseY = mouseCoords[1];
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
if ( mouseY < this.gutterTop
|
|
1385
|
+
|| mouseY > (ca.height - this.gutterBottom)
|
|
1386
|
+
|| mouseX < this.gutterLeft
|
|
1387
|
+
|| mouseX > (ca.width - this.gutterRight)
|
|
1388
|
+
) {
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
1393
|
+
var value = ((mouseX - this.gutterLeft) / (this.graphwidth / 2)) * (this.max - prop['chart.xmin']);
|
|
1394
|
+
value = value - this.max
|
|
1395
|
+
|
|
1396
|
+
// Special case if xmin is defined
|
|
1397
|
+
if (prop['chart.xmin'] > 0) {
|
|
1398
|
+
value = ((mouseX - this.gutterLeft - (this.graphwidth / 2)) / (this.graphwidth / 2)) * (this.max - prop['chart.xmin']);
|
|
1399
|
+
value += prop['chart.xmin'];
|
|
1400
|
+
|
|
1401
|
+
if (mouseX < (this.gutterLeft + (this.graphwidth / 2))) {
|
|
1402
|
+
value -= (2 * prop['chart.xmin']);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
} else {
|
|
1406
|
+
var value = ((mouseX - this.gutterLeft) / this.graphwidth) * (this.max - prop['chart.xmin']);
|
|
1407
|
+
value += prop['chart.xmin'];
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
return value;
|
|
1411
|
+
};
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Each object type has its own Highlight() function which highlights the appropriate shape
|
|
1418
|
+
*
|
|
1419
|
+
* @param object shape The shape to highlight
|
|
1420
|
+
*/
|
|
1421
|
+
this.highlight =
|
|
1422
|
+
this.Highlight = function (shape)
|
|
1423
|
+
{
|
|
1424
|
+
// Add the new highlight
|
|
1425
|
+
RG.Highlight.Rect(this, shape);
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* The getObjectByXY() worker method. Don't call this call:
|
|
1433
|
+
*
|
|
1434
|
+
* RG.ObjectRegistry.getObjectByXY(e)
|
|
1435
|
+
*
|
|
1436
|
+
* @param object e The event object
|
|
1437
|
+
*/
|
|
1438
|
+
this.getObjectByXY = function (e)
|
|
1439
|
+
{
|
|
1440
|
+
var mouseXY = RG.getMouseXY(e);
|
|
1441
|
+
|
|
1442
|
+
if (
|
|
1443
|
+
mouseXY[0] > this.gutterLeft
|
|
1444
|
+
&& mouseXY[0] < (ca.width - this.gutterRight)
|
|
1445
|
+
&& mouseXY[1] > this.gutterTop
|
|
1446
|
+
&& mouseXY[1] < (ca.height - this.gutterBottom)
|
|
1447
|
+
) {
|
|
1448
|
+
|
|
1449
|
+
return this;
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* This function positions a tooltip when it is displayed
|
|
1458
|
+
*
|
|
1459
|
+
* @param obj object The chart object
|
|
1460
|
+
* @param int x The X coordinate specified for the tooltip
|
|
1461
|
+
* @param int y The Y coordinate specified for the tooltip
|
|
1462
|
+
* @param objec tooltip The tooltips DIV element
|
|
1463
|
+
*/
|
|
1464
|
+
this.positionTooltip = function (obj, x, y, tooltip, idx)
|
|
1465
|
+
{
|
|
1466
|
+
var coordX = obj.coords[tooltip.__index__][0];
|
|
1467
|
+
var coordY = obj.coords[tooltip.__index__][1];
|
|
1468
|
+
var coordW = obj.coords[tooltip.__index__][2];
|
|
1469
|
+
var coordH = obj.coords[tooltip.__index__][3];
|
|
1470
|
+
var canvasXY = RG.getCanvasXY(obj.canvas);
|
|
1471
|
+
var gutterLeft = obj.gutterLeft;
|
|
1472
|
+
var gutterTop = obj.gutterTop;
|
|
1473
|
+
var width = tooltip.offsetWidth;
|
|
1474
|
+
var height = tooltip.offsetHeight;
|
|
1475
|
+
|
|
1476
|
+
// Set the top position
|
|
1477
|
+
tooltip.style.left = 0;
|
|
1478
|
+
tooltip.style.top = canvasXY[1] + coordY + (coordH / 2) - height + 'px';
|
|
1479
|
+
|
|
1480
|
+
// By default any overflow is hidden
|
|
1481
|
+
tooltip.style.overflow = '';
|
|
1482
|
+
|
|
1483
|
+
// The arrow
|
|
1484
|
+
var img = new Image();
|
|
1485
|
+
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
|
|
1486
|
+
img.style.position = 'absolute';
|
|
1487
|
+
img.id = '__rgraph_tooltip_pointer__';
|
|
1488
|
+
img.style.top = (tooltip.offsetHeight - 2) + 'px';
|
|
1489
|
+
tooltip.appendChild(img);
|
|
1490
|
+
|
|
1491
|
+
// Reposition the tooltip if at the edges:
|
|
1492
|
+
|
|
1493
|
+
// LEFT edge
|
|
1494
|
+
if ((canvasXY[0] + coordX + (coordW / 2) - (width / 2)) < 10) {
|
|
1495
|
+
tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + (coordW / 2) + 'px';
|
|
1496
|
+
img.style.left = ((width * 0.1) - 8.5) + 'px';
|
|
1497
|
+
|
|
1498
|
+
// RIGHT edge
|
|
1499
|
+
} else if ((canvasXY[0] + (coordW / 2) + coordX + (width / 2)) > doc.body.offsetWidth) {
|
|
1500
|
+
tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + (coordW / 2) + 'px';
|
|
1501
|
+
img.style.left = ((width * 0.9) - 8.5) + 'px';
|
|
1502
|
+
|
|
1503
|
+
// Default positioning - CENTERED
|
|
1504
|
+
} else {
|
|
1505
|
+
tooltip.style.left = (canvasXY[0] + coordX + (coordW / 2) - (width * 0.5)) + 'px';
|
|
1506
|
+
img.style.left = ((width * 0.5) - 8.5) + 'px';
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Returns the appropriate Y coord for the given value
|
|
1515
|
+
*
|
|
1516
|
+
* @param number value The value to get the coord for
|
|
1517
|
+
*/
|
|
1518
|
+
this.getXCoord = function (value)
|
|
1519
|
+
{
|
|
1520
|
+
|
|
1521
|
+
if (prop['chart.yaxispos'] == 'center') {
|
|
1522
|
+
|
|
1523
|
+
// Range checking
|
|
1524
|
+
if (value > this.max || value < (-1 * this.max)) {
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
var width = (ca.width - prop['chart.gutter.left'] - prop['chart.gutter.right']) / 2;
|
|
1529
|
+
var coord = (((value - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * width) + width;
|
|
1530
|
+
|
|
1531
|
+
coord = prop['chart.gutter.left'] + coord;
|
|
1532
|
+
} else {
|
|
1533
|
+
|
|
1534
|
+
// Range checking
|
|
1535
|
+
if (value > this.max || value < 0) {
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
var width = ca.width - prop['chart.gutter.left'] - prop['chart.gutter.right'];
|
|
1540
|
+
var coord = ((value - prop['chart.xmin']) / (this.max - prop['chart.xmin'])) * width;
|
|
1541
|
+
|
|
1542
|
+
coord = prop['chart.gutter.left'] + coord;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
return coord;
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
/**
|
|
1552
|
+
*
|
|
1553
|
+
*/
|
|
1554
|
+
this.parseColors = function ()
|
|
1555
|
+
{
|
|
1556
|
+
// Save the original colors so that they can be restored when the canvas is reset
|
|
1557
|
+
if (this.original_colors.length === 0) {
|
|
1558
|
+
//this.original_colors['chart.'] = RG.array_clone(prop['chart.']);
|
|
1559
|
+
this.original_colors['chart.colors'] = RG.array_clone(prop['chart.colors']);
|
|
1560
|
+
this.original_colors['chart.background.grid.color'] = RG.array_clone(prop['chart.background.grid.color']);
|
|
1561
|
+
this.original_colors['chart.background.color'] = RG.array_clone(prop['chart.background.color']);
|
|
1562
|
+
this.original_colors['chart.background.barcolor1'] = RG.array_clone(prop['chart.background.barcolor1']);
|
|
1563
|
+
this.original_colors['chart.background.barcolor2'] = RG.array_clone(prop['chart.background.barcolor2']);
|
|
1564
|
+
this.original_colors['chart.text.color'] = RG.array_clone(prop['chart.text.color']);
|
|
1565
|
+
this.original_colors['chart.labels.colors'] = RG.array_clone(prop['chart.labels.colors']);
|
|
1566
|
+
this.original_colors['chart.strokestyle'] = RG.array_clone(prop['chart.strokestyle']);
|
|
1567
|
+
this.original_colors['chart.axis.color'] = RG.array_clone(prop['chart.axis.color']);
|
|
1568
|
+
this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
|
|
1569
|
+
this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
|
|
1570
|
+
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
var colors = prop['chart.colors'];
|
|
1574
|
+
|
|
1575
|
+
for (var i=0; i<colors.length; ++i) {
|
|
1576
|
+
colors[i] = this.parseSingleColorForGradient(colors[i]);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
|
|
1580
|
+
prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
|
|
1581
|
+
prop['chart.background.barcolor1'] = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
|
|
1582
|
+
prop['chart.background.barcolor2'] = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
|
|
1583
|
+
prop['chart.text.color'] = this.parseSingleColorForGradient(prop['chart.text.color']);
|
|
1584
|
+
prop['chart.labels.colors'] = this.parseSingleColorForGradient(prop['chart.labels.colors']);
|
|
1585
|
+
prop['chart.strokestyle'] = this.parseSingleColorForGradient(prop['chart.strokestyle']);
|
|
1586
|
+
prop['chart.axis.color'] = this.parseSingleColorForGradient(prop['chart.axis.color']);
|
|
1587
|
+
prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
|
|
1588
|
+
prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Use this function to reset the object to the post-constructor state. Eg reset colors if
|
|
1596
|
+
* need be etc
|
|
1597
|
+
*/
|
|
1598
|
+
this.reset = function ()
|
|
1599
|
+
{
|
|
1600
|
+
};
|
|
1601
|
+
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
* This parses a single color value
|
|
1606
|
+
*/
|
|
1607
|
+
this.parseSingleColorForGradient = function (color)
|
|
1608
|
+
{
|
|
1609
|
+
if (!color || typeof(color) != 'string') {
|
|
1610
|
+
return color;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
if (color.match(/^gradient\((.*)\)$/i)) {
|
|
1614
|
+
|
|
1615
|
+
var parts = RegExp.$1.split(':');
|
|
1616
|
+
|
|
1617
|
+
if (prop['chart.yaxispos'] === 'right') {
|
|
1618
|
+
parts = RG.arrayReverse(parts);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// Create the gradient
|
|
1622
|
+
var grad = co.createLinearGradient(prop['chart.gutter.left'],0,ca.width - prop['chart.gutter.right'],0);
|
|
1623
|
+
|
|
1624
|
+
var diff = 1 / (parts.length - 1);
|
|
1625
|
+
|
|
1626
|
+
grad.addColorStop(0, RG.trim(parts[0]));
|
|
1627
|
+
|
|
1628
|
+
for (var j=1; j<parts.length; ++j) {
|
|
1629
|
+
grad.addColorStop(j * diff, RG.trim(parts[j]));
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
return grad ? grad : color;
|
|
1634
|
+
};
|
|
1635
|
+
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* This function handles highlighting an entire data-series for the interactive
|
|
1641
|
+
* key
|
|
1642
|
+
*
|
|
1643
|
+
* @param int index The index of the data series to be highlighted
|
|
1644
|
+
*/
|
|
1645
|
+
this.interactiveKeyHighlight = function (index)
|
|
1646
|
+
{
|
|
1647
|
+
var obj = this;
|
|
1648
|
+
|
|
1649
|
+
this.coords2.forEach(function (value, idx, arr)
|
|
1650
|
+
{
|
|
1651
|
+
var shape = obj.coords2[idx][index]
|
|
1652
|
+
var pre_linewidth = co.lineWidth;
|
|
1653
|
+
co.lineWidth = 2;
|
|
1654
|
+
co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
|
|
1655
|
+
co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
|
|
1656
|
+
co.fillRect(shape[0], shape[1], shape[2], shape[3]);
|
|
1657
|
+
co.strokeRect(shape[0], shape[1], shape[2], shape[3]);
|
|
1658
|
+
|
|
1659
|
+
// Reset the lineWidth
|
|
1660
|
+
co.lineWidth = pre_linewidth;
|
|
1661
|
+
});
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
|
|
1665
|
+
|
|
1666
|
+
|
|
1667
|
+
/**
|
|
1668
|
+
* Using a function to add events makes it easier to facilitate method chaining
|
|
1669
|
+
*
|
|
1670
|
+
* @param string type The type of even to add
|
|
1671
|
+
* @param function func
|
|
1672
|
+
*/
|
|
1673
|
+
this.on = function (type, func)
|
|
1674
|
+
{
|
|
1675
|
+
if (type.substr(0,2) !== 'on') {
|
|
1676
|
+
type = 'on' + type;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
this[type] = func;
|
|
1680
|
+
|
|
1681
|
+
return this;
|
|
1682
|
+
};
|
|
1683
|
+
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* This function runs once only
|
|
1689
|
+
* (put at the end of the file (before any effects))
|
|
1690
|
+
*/
|
|
1691
|
+
this.firstDrawFunc = function ()
|
|
1692
|
+
{
|
|
1693
|
+
};
|
|
1694
|
+
|
|
1695
|
+
|
|
1696
|
+
|
|
1697
|
+
|
|
1698
|
+
/**
|
|
1699
|
+
* Grow
|
|
1700
|
+
*
|
|
1701
|
+
* The HBar chart Grow effect gradually increases the values of the bars
|
|
1702
|
+
*
|
|
1703
|
+
* @param object OPTIONAL Options for the effect. You can pass frames here
|
|
1704
|
+
* @param function OPTIONAL A callback function
|
|
1705
|
+
*/
|
|
1706
|
+
this.grow = function ()
|
|
1707
|
+
{
|
|
1708
|
+
var obj = this;
|
|
1709
|
+
var opt = arguments[0] || {};
|
|
1710
|
+
var frames = opt.frames || 30;
|
|
1711
|
+
var frame = 0;
|
|
1712
|
+
var callback = arguments[1] || function () {};
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
// Save the data
|
|
1716
|
+
obj.original_data = RG.array_clone(obj.data);
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
// Stop the scale from changing by setting chart.ymax (if it's not already set)
|
|
1720
|
+
if (obj.Get('chart.xmax') == 0) {
|
|
1721
|
+
|
|
1722
|
+
var xmax = 0;
|
|
1723
|
+
|
|
1724
|
+
for (var i=0; i<obj.data.length; ++i) {
|
|
1725
|
+
if (RG.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'stacked') {
|
|
1726
|
+
xmax = Math.max(xmax, RG.array_sum(obj.data[i]));
|
|
1727
|
+
} else if (RG.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'grouped') {
|
|
1728
|
+
xmax = ma.max(xmax, RG.array_max(obj.data[i]));
|
|
1729
|
+
} else {
|
|
1730
|
+
xmax = ma.max(xmax, RG.array_max(obj.data[i]));
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
var scale2 = RG.getScale2(obj, {'max':xmax});
|
|
1735
|
+
obj.Set('chart.xmax', scale2.max);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
function iterator ()
|
|
1739
|
+
{
|
|
1740
|
+
// Alter the Bar chart data depending on the frame
|
|
1741
|
+
for (var j=0,len=obj.original_data.length; j<len; ++j) {
|
|
1742
|
+
|
|
1743
|
+
// This stops the animation from being completely linear
|
|
1744
|
+
var easingFactor = RG.Effects.getEasingMultiplier(frames, frame);
|
|
1745
|
+
|
|
1746
|
+
if (typeof obj.data[j] === 'object') {
|
|
1747
|
+
for (var k=0,len2=obj.data[j].length; k<len2; ++k) {
|
|
1748
|
+
obj.data[j][k] = obj.original_data[j][k] * easingFactor;
|
|
1749
|
+
}
|
|
1750
|
+
} else {
|
|
1751
|
+
obj.data[j] = obj.original_data[j] * easingFactor;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
|
|
1757
|
+
RG.redrawCanvas(obj.canvas);
|
|
1758
|
+
|
|
1759
|
+
if (frame < frames) {
|
|
1760
|
+
frame += 1;
|
|
1761
|
+
RG.Effects.updateCanvas(iterator);
|
|
1762
|
+
} else {
|
|
1763
|
+
callback(obj);
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
iterator();
|
|
1768
|
+
|
|
1769
|
+
return this;
|
|
1770
|
+
};
|
|
1771
|
+
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
RG.att(ca);
|
|
1775
|
+
|
|
1776
|
+
|
|
1777
|
+
|
|
1778
|
+
|
|
1779
|
+
/**
|
|
1780
|
+
* Charts are now always registered
|
|
1781
|
+
*/
|
|
1782
|
+
RG.Register(this);
|
|
1783
|
+
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* This is the 'end' of the constructor so if the first argument
|
|
1789
|
+
* contains configuration data - handle that.
|
|
1790
|
+
*/
|
|
1791
|
+
if (parseConfObjectForOptions) {
|
|
1792
|
+
RG.parseObjectStyleConfig(this, conf.options);
|
|
1793
|
+
}
|
|
1794
|
+
};
|