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.
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
@@ -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
+ };