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,2003 @@
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 bi-polar/age frequency constructor.
20
+ *
21
+ * @param string id The id of the canvas
22
+ * @param array left The left set of data points
23
+ * @param array right The right set of data points
24
+ *
25
+ * REMEMBER If ymin is implemented you need to update the .getValue() method
26
+ */
27
+ RGraph.Bipolar = function (conf)
28
+ {
29
+ /**
30
+ * Allow for object config style
31
+ */
32
+ if ( typeof conf === 'object'
33
+ && typeof conf.left === 'object'
34
+ && typeof conf.right === 'object'
35
+ && typeof conf.id === 'string') {
36
+
37
+ var id = conf.id,
38
+ canvas = document.getElementById(id),
39
+ left = conf.left,
40
+ right = conf.right,
41
+ parseConfObjectForOptions = true // Set this so the config is parsed (at the end of the constructor)
42
+
43
+ } else {
44
+
45
+ var id = conf,
46
+ canvas = document.getElementById(id),
47
+ left = arguments[1],
48
+ right = arguments[2]
49
+ }
50
+
51
+
52
+
53
+ // Get the canvas and context objects
54
+ this.id = id;
55
+ this.canvas = canvas;
56
+ this.context = this.canvas.getContext('2d');
57
+ this.canvas.__object__ = this;
58
+ this.type = 'bipolar';
59
+ this.coords = [];
60
+ this.coordsLeft = [];
61
+ this.coordsRight = [];
62
+ this.max = 0;
63
+ this.isRGraph = true;
64
+ this.uid = RGraph.CreateUID();
65
+ this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
66
+ this.coordsText = [];
67
+ this.original_colors = [];
68
+ this.firstDraw = true; // After the first draw this will be false
69
+
70
+
71
+ /**
72
+ * Compatibility with older browsers
73
+ */
74
+ //RGraph.OldBrowserCompat(this.context);
75
+
76
+
77
+ // The left and right data respectively
78
+ this.left = left;
79
+ this.right = right;
80
+ this.data = [left, right];
81
+
82
+ this.properties =
83
+ {
84
+ 'chart.background.grid': true,
85
+ 'chart.background.grid.color': '#ddd',
86
+ 'chart.background.grid.vlines': true,
87
+ 'chart.background.grid.hlines': true,
88
+ 'chart.background.grid.linewidth': 1,
89
+ 'chart.background.grid.autofit.numvlines': null,
90
+ 'chart.background.grid.autofit.numhlines': null,
91
+ 'chart.margin': 2,
92
+ 'chart.xtickinterval': null,
93
+ 'chart.labels': [],
94
+ 'chart.labels.color': null,
95
+ 'chart.labels.above': false,
96
+ 'chart.text.size': 12,
97
+ 'chart.text.color': 'black', // (Simple) gradients are not supported
98
+ 'chart.text.font': 'Arial',
99
+ 'chart.title.left': '',
100
+ 'chart.title.right': '',
101
+ 'chart.gutter.center': 60,
102
+ 'chart.gutter.left': 25,
103
+ 'chart.gutter.right': 25,
104
+ 'chart.gutter.top': 25,
105
+ 'chart.gutter.bottom': 30,
106
+ 'chart.title': '',
107
+ 'chart.title.background': null,
108
+ 'chart.title.hpos': null,
109
+ 'chart.title.vpos': null,
110
+ 'chart.title.bold': true,
111
+ 'chart.title.font': null,
112
+ 'chart.title.x': null,
113
+ 'chart.title.y': null,
114
+ 'chart.title.halign': null,
115
+ 'chart.title.valign': null,
116
+ 'chart.colors': ['#0f0'],
117
+ 'chart.colors.sequential': false,
118
+ 'chart.contextmenu': null,
119
+ 'chart.tooltips': null,
120
+ 'chart.tooltips.effect': 'fade',
121
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
122
+ 'chart.tooltips.highlight': true,
123
+ 'chart.tooltips.event': 'onclick',
124
+ 'chart.highlight.stroke': 'rgba(0,0,0,0)',
125
+ 'chart.highlight.fill': 'rgba(255,255,255,0.7)',
126
+ 'chart.units.pre': '',
127
+ 'chart.units.post': '',
128
+ 'chart.shadow': false,
129
+ 'chart.shadow.color': '#666',
130
+ 'chart.shadow.offsetx': 3,
131
+ 'chart.shadow.offsety': 3,
132
+ 'chart.shadow.blur': 3,
133
+ 'chart.annotatable': false,
134
+ 'chart.annotate.color': 'black',
135
+ 'chart.xmax': null,
136
+ 'chart.xmin': 0,
137
+ 'chart.scale.zerostart': false,
138
+ 'chart.scale.decimals': null,
139
+ 'chart.scale.point': '.',
140
+ 'chart.scale.thousand': ',',
141
+ 'chart.axis.color': 'black',
142
+ 'chart.zoom.factor': 1.5,
143
+ 'chart.zoom.fade.in': true,
144
+ 'chart.zoom.fade.out': true,
145
+ 'chart.zoom.hdir': 'right',
146
+ 'chart.zoom.vdir': 'down',
147
+ 'chart.zoom.frames': 25,
148
+ 'chart.zoom.delay': 16.666,
149
+ 'chart.zoom.shadow': true,
150
+ 'chart.zoom.background': true,
151
+ 'chart.zoom.action': 'zoom',
152
+ 'chart.resizable': false,
153
+ 'chart.resize.handle.background': null,
154
+ 'chart.strokestyle': 'rgba(0,0,0,0)',
155
+ 'chart.events.mousemove': null,
156
+ 'chart.events.click': null,
157
+ 'chart.linewidth': 1,
158
+ 'chart.noaxes': false,
159
+ 'chart.xlabels': true,
160
+ 'chart.numyticks': null,
161
+ 'chart.numxticks': 5,
162
+ 'chart.axis.linewidth': 1,
163
+ 'chart.labels.count': 5,
164
+ 'chart.variant.threed.offsetx': 10,
165
+ 'chart.variant.threed.offsety': 5,
166
+ 'chart.variant.threed.angle': 0.1
167
+ }
168
+
169
+ // Pad the arrays so they're the same size
170
+ while (this.left.length < this.right.length) this.left.push(null);
171
+ while (this.left.length > this.right.length) this.right.push(null);
172
+
173
+ /**
174
+ * Set the default for the number of Y tickmarks
175
+ */
176
+ this.properties['chart.numyticks'] = this.left.length;
177
+
178
+
179
+
180
+
181
+ /**
182
+ * Create the dollar objects so that functions can be added to them
183
+ */
184
+ var linear_data = RGraph.arrayLinearize(this.left, this.right);
185
+
186
+ for (var i=0; i<linear_data.length; ++i) {
187
+ this['$' + i] = {};
188
+ }
189
+
190
+
191
+ /**
192
+ * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
193
+ * done already
194
+ */
195
+ if (!this.canvas.__rgraph_aa_translated__) {
196
+ this.context.translate(0.5,0.5);
197
+
198
+ this.canvas.__rgraph_aa_translated__ = true;
199
+ }
200
+
201
+
202
+
203
+
204
+ // Short variable names
205
+ var RG = RGraph,
206
+ ca = this.canvas,
207
+ co = ca.getContext('2d'),
208
+ prop = this.properties,
209
+ pa = RG.Path,
210
+ pa2 = RG.path2,
211
+ win = window,
212
+ doc = document,
213
+ ma = Math
214
+
215
+
216
+
217
+ /**
218
+ * "Decorate" the object with the generic effects if the effects library has been included
219
+ */
220
+ if (RG.Effects && typeof RG.Effects.decorate === 'function') {
221
+ RG.Effects.decorate(this);
222
+ }
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+ /**
231
+ * The setter
232
+ *
233
+ * @param name string The name of the parameter to set
234
+ * @param value mixed The value of the paraneter
235
+ */
236
+ this.set =
237
+ this.Set = function (name)
238
+ {
239
+ var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
240
+
241
+
242
+ /**
243
+ * the number of arguments is only one and it's an
244
+ * object - parse it for configuration data and return.
245
+ */
246
+ if (arguments.length === 1 && typeof name === 'object') {
247
+ RG.parseObjectStyleConfig(this, name);
248
+ return this;
249
+ }
250
+
251
+
252
+
253
+
254
+
255
+ /**
256
+ * This should be done first - prepend the propertyy name with "chart." if necessary
257
+ */
258
+ if (name.substr(0,6) != 'chart.') {
259
+ name = 'chart.' + name;
260
+ }
261
+
262
+
263
+
264
+
265
+ // Convert uppercase letters to dot+lower case letter
266
+ name = name.replace(/([A-Z])/g, function (str)
267
+ {
268
+ return '.' + String(RegExp.$1).toLowerCase();
269
+ });
270
+
271
+
272
+
273
+
274
+
275
+
276
+ prop[name] = value;
277
+
278
+ return this;
279
+ };
280
+
281
+
282
+
283
+
284
+ /**
285
+ * The getter
286
+ *
287
+ * @param name string The name of the parameter to get
288
+ */
289
+ this.get =
290
+ this.Get = function (name)
291
+ {
292
+ /**
293
+ * This should be done first - prepend the property name with "chart." if necessary
294
+ */
295
+ if (name.substr(0,6) != 'chart.') {
296
+ name = 'chart.' + name;
297
+ }
298
+
299
+ // Convert uppercase letters to dot+lower case letter
300
+ name = name.replace(/([A-Z])/g, function (str)
301
+ {
302
+ return '.' + String(RegExp.$1).toLowerCase()
303
+ });
304
+
305
+ return this.properties[name.toLowerCase()];
306
+ };
307
+
308
+
309
+
310
+
311
+ /**
312
+ * Draws the graph
313
+ */
314
+ this.draw =
315
+ this.Draw = function ()
316
+ {
317
+ /**
318
+ * Fire the onbeforedraw event
319
+ */
320
+ RG.FireCustomEvent(this, 'onbeforedraw');
321
+
322
+
323
+ /**
324
+ * Parse the colors. This allows for simple gradient syntax
325
+ */
326
+ if (!this.colorsParsed) {
327
+ this.parseColors();
328
+
329
+ // Don't want to do this again
330
+ this.colorsParsed = true;
331
+ }
332
+
333
+
334
+ /**
335
+ * This is new in May 2011 and facilitates indiviual gutter settings,
336
+ * eg chart.gutter.left
337
+ */
338
+ this.gutterLeft = prop['chart.gutter.left'];
339
+ this.gutterRight = prop['chart.gutter.right'];
340
+ this.gutterTop = prop['chart.gutter.top'];
341
+ this.gutterBottom = prop['chart.gutter.bottom'];
342
+ this.gutterCenter = prop['chart.gutter.center'];
343
+
344
+
345
+
346
+ // Reset the data to what was initially supplied
347
+ this.left = this.data[0];
348
+ this.right = this.data[1];
349
+
350
+
351
+ /**
352
+ * Reset the coords array
353
+ */
354
+ this.coords = [];
355
+
356
+
357
+
358
+ /**
359
+ * Stop this growing uncontrollably
360
+ */
361
+ this.coordsText = [];
362
+
363
+
364
+ if (prop['chart.variant'] === '3d') {
365
+ co.setTransform(1,prop['chart.variant.threed.angle'],0,1,0.5,0.5);
366
+
367
+ }
368
+
369
+
370
+
371
+ // Some necessary variables
372
+ this.axisWidth = (ca.width - prop['chart.gutter.center'] - this.gutterLeft - this.gutterRight) / 2;
373
+ this.axisHeight = ca.height - this.gutterTop - this.gutterBottom;
374
+
375
+
376
+ // Reset the sequential index
377
+ this.sequentialFullIndex = 0;
378
+
379
+
380
+
381
+ this.getMax();
382
+ this.drawBackgroundGrid();
383
+ this.draw3DAxes();
384
+ this.drawAxes();
385
+ this.drawTicks();
386
+
387
+ co.save();
388
+ co.beginPath();
389
+ co.rect(this.gutterLeft, this.gutterTop - (prop['chart.variant.threed.offsety'] || 0), ca.width - this.gutterLeft - this.gutterRight, ca.height - this.gutterTop - this.gutterBottom + (2 * (prop['chart.variant.threed.offsety'] || 0)) );
390
+ co.clip();
391
+
392
+ this.drawLeftBars();
393
+ this.drawRightBars();
394
+
395
+ // Redraw the bars so that shadows on not on top
396
+ this.drawLeftBars({shadow: false});
397
+ this.drawRightBars({shadow: false});
398
+ co.restore();
399
+
400
+ this.drawAxes();
401
+
402
+ this.drawLabels();
403
+ this.drawTitles();
404
+
405
+
406
+ /**
407
+ * Setup the context menu if required
408
+ */
409
+ if (prop['chart.contextmenu']) {
410
+ RG.ShowContext(this);
411
+ }
412
+
413
+
414
+ /**
415
+ * This function enables resizing
416
+ */
417
+ if (prop['chart.resizable']) {
418
+ RG.AllowResizing(this);
419
+ }
420
+
421
+
422
+ /**
423
+ * This installs the event listeners
424
+ */
425
+ RG.InstallEventListeners(this);
426
+
427
+
428
+ /**
429
+ * Fire the onfirstdraw event
430
+ */
431
+ if (this.firstDraw) {
432
+ RG.fireCustomEvent(this, 'onfirstdraw');
433
+ this.firstDraw = false;
434
+ this.firstDrawFunc();
435
+ }
436
+
437
+
438
+ /**
439
+ * Fire the RGraph ondraw event
440
+ */
441
+ RG.FireCustomEvent(this, 'ondraw');
442
+
443
+ return this;
444
+ };
445
+
446
+
447
+
448
+ /**
449
+ * Used in chaining. Runs a function there and then - not waiting for
450
+ * the events to fire (eg the onbeforedraw event)
451
+ *
452
+ * @param function func The function to execute
453
+ */
454
+ this.exec = function (func)
455
+ {
456
+ func(this);
457
+
458
+ return this;
459
+ };
460
+
461
+
462
+
463
+
464
+ /**
465
+ * Draws the axes
466
+ */
467
+ this.draw3DAxes = function ()
468
+ {
469
+ if (prop['chart.variant'] === '3d') {
470
+ var offsetx = prop['chart.variant.threed.offsetx'],
471
+ offsety = prop['chart.variant.threed.offsety'];
472
+
473
+ // Set the linewidth
474
+ co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
475
+
476
+
477
+ // Draw the left set of axes
478
+ co.beginPath();
479
+ co.strokeStyle = prop['chart.axis.color'];
480
+
481
+ // Draw the horizontal 3d axis
482
+ // The left horizontal axis
483
+ pa(co, [
484
+ 'b',
485
+ 'm', this.gutterLeft, ma.round( ca.height - this.gutterBottom),
486
+ 'l', this.gutterLeft + offsetx, ma.round( ca.height - this.gutterBottom - offsety),
487
+ 'l', this.gutterLeft + offsetx + this.axisWidth, ma.round( ca.height - this.gutterBottom - offsety),
488
+ 'l', this.gutterLeft + this.axisWidth, ma.round( ca.height - this.gutterBottom),
489
+ 's', '#aaa',
490
+ 'f','#ddd'
491
+ ]);
492
+
493
+ // The left vertical axis
494
+ this.draw3DLeftVerticalAxis();
495
+
496
+
497
+
498
+
499
+ // Draw the right horizontal axes
500
+ pa(co, [
501
+ 'b',
502
+ 'm', this.gutterLeft + this.gutterCenter + this.axisWidth, ma.round( ca.height - this.gutterBottom),
503
+ 'l',this.gutterLeft + this.gutterCenter + this.axisWidth + offsetx, ma.round( ca.height - this.gutterBottom - offsety),
504
+ 'l',this.gutterLeft + this.gutterCenter + this.axisWidth + this.axisWidth + offsetx, ma.round( ca.height - this.gutterBottom - offsety),
505
+ 'l',this.gutterLeft + this.gutterCenter + this.axisWidth + this.axisWidth, ma.round( ca.height - this.gutterBottom),
506
+ 's', '#aaa',
507
+ 'f','#ddd'
508
+ ]);
509
+
510
+
511
+
512
+
513
+ // Draw the right vertical axes
514
+ pa(co, [
515
+ 'b',
516
+ 'm', this.gutterLeft + this.gutterCenter + this.axisWidth, ca.height - this.gutterBottom,
517
+ 'l', this.gutterLeft + this.gutterCenter + this.axisWidth, ca.height - this.gutterBottom - this.axisHeight,
518
+ 'l',this.gutterLeft + this.gutterCenter + this.axisWidth + offsetx, ca.height - this.gutterBottom - this.axisHeight - offsety,
519
+ 'l',this.gutterLeft + this.gutterCenter + this.axisWidth + offsetx, ca.height - this.gutterBottom - offsety,
520
+ 's', '#aaa',
521
+ 'f','#ddd'
522
+ ]);
523
+ }
524
+ }
525
+
526
+
527
+
528
+
529
+ this.draw3DLeftVerticalAxis = function ()
530
+ {
531
+ if (prop['chart.variant'] === '3d') {
532
+ var offsetx = prop['chart.variant.threed.offsetx'],
533
+ offsety = prop['chart.variant.threed.offsety'];
534
+
535
+ // The left vertical axis
536
+ pa(co, [
537
+ 'b',
538
+ 'm', this.gutterLeft + this.axisWidth, this.gutterTop,
539
+ 'l', this.gutterLeft + this.axisWidth + offsetx, this.gutterTop - offsety,
540
+ 'l', this.gutterLeft + this.axisWidth + offsetx, ca.height - this.gutterBottom - offsety,
541
+ 'l',this.gutterLeft + this.axisWidth, ca.height - this.gutterBottom,
542
+ 's', '#aaa',
543
+ 'f','#ddd'
544
+ ]);
545
+ }
546
+ };
547
+
548
+
549
+
550
+
551
+ /**
552
+ * Draws the axes
553
+ */
554
+ this.drawAxes =
555
+ this.DrawAxes = function ()
556
+ {
557
+ // Set the linewidth
558
+ co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
559
+
560
+
561
+ // Draw the left set of axes
562
+ co.beginPath();
563
+ co.strokeStyle = prop['chart.axis.color'];
564
+
565
+ this.axisWidth = (ca.width - prop['chart.gutter.center'] - this.gutterLeft - this.gutterRight) / 2;
566
+ this.axisHeight = ca.height - this.gutterTop - this.gutterBottom;
567
+
568
+
569
+ // This must be here so that the two above variables are calculated
570
+ if (prop['chart.noaxes']) {
571
+ return;
572
+ }
573
+
574
+ co.moveTo(this.gutterLeft, Math.round( ca.height - this.gutterBottom));
575
+ co.lineTo(this.gutterLeft + this.axisWidth, Math.round( ca.height - this.gutterBottom));
576
+
577
+ co.moveTo(ma.round( this.gutterLeft + this.axisWidth), ca.height - this.gutterBottom);
578
+ co.lineTo(ma.round( this.gutterLeft + this.axisWidth), this.gutterTop);
579
+
580
+ co.stroke();
581
+
582
+
583
+ // Draw the right set of axes
584
+ co.beginPath();
585
+
586
+ var x = this.gutterLeft + this.axisWidth + prop['chart.gutter.center'];
587
+
588
+ co.moveTo(Math.round( x), this.gutterTop);
589
+ co.lineTo(Math.round( x), ca.height - this.gutterBottom);
590
+
591
+ co.moveTo(Math.round( x), Math.round( ca.height - this.gutterBottom));
592
+ co.lineTo(ca.width - this.gutterRight, Math.round( ca.height - this.gutterBottom));
593
+
594
+ co.stroke();
595
+ };
596
+
597
+
598
+
599
+
600
+ /**
601
+ * Draws the tick marks on the axes
602
+ */
603
+ this.drawTicks =
604
+ this.DrawTicks = function ()
605
+ {
606
+ // Set the linewidth
607
+ co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
608
+
609
+ var numDataPoints = this.left.length;
610
+ var barHeight = ( (ca.height - this.gutterTop - this.gutterBottom)- (this.left.length * (prop['chart.margin'] * 2) )) / numDataPoints;
611
+
612
+ // Store this for later
613
+ this.barHeight = barHeight;
614
+
615
+ // If no axes - no tickmarks
616
+ if (prop['chart.noaxes']) {
617
+ return;
618
+ }
619
+
620
+ // Draw the left Y tick marks
621
+ if (prop['chart.numyticks'] > 0) {
622
+ co.beginPath();
623
+ for (var i=0; i<prop['chart.numyticks']; ++i) {
624
+ var y = prop['chart.gutter.top'] + (((ca.height - this.gutterTop - this.gutterBottom) / prop['chart.numyticks']) * i);
625
+ co.moveTo(this.gutterLeft + this.axisWidth , y);
626
+ co.lineTo(this.gutterLeft + this.axisWidth + 3, y);
627
+ }
628
+ co.stroke();
629
+
630
+ //Draw the right axis Y tick marks
631
+ co.beginPath();
632
+ for (var i=0; i<prop['chart.numyticks']; ++i) {
633
+ var y = prop['chart.gutter.top'] + (((ca.height - this.gutterTop - this.gutterBottom) / prop['chart.numyticks']) * i);
634
+ co.moveTo(this.gutterLeft + this.axisWidth + prop['chart.gutter.center'], y);
635
+ co.lineTo(this.gutterLeft + this.axisWidth + prop['chart.gutter.center'] - 3, y);
636
+ }
637
+ co.stroke();
638
+ }
639
+
640
+
641
+
642
+ /**
643
+ * X tickmarks
644
+ */
645
+ if (prop['chart.numxticks'] > 0) {
646
+ var xInterval = this.axisWidth / prop['chart.numxticks'];
647
+
648
+ // Is chart.xtickinterval specified ? If so, use that.
649
+ if (typeof(prop['chart.xtickinterval']) == 'number') {
650
+ xInterval = prop['chart.xtickinterval'];
651
+ }
652
+
653
+
654
+ // Draw the left sides X tick marks
655
+ for (i=this.gutterLeft; i<(this.gutterLeft + this.axisWidth); i+=xInterval) {
656
+ co.beginPath();
657
+ co.moveTo(Math.round( i), ca.height - this.gutterBottom);
658
+ co.lineTo(Math.round( i), (ca.height - this.gutterBottom) + 4);
659
+ co.closePath();
660
+
661
+ co.stroke();
662
+ }
663
+
664
+ // Draw the right sides X tick marks
665
+ var stoppingPoint = ca.width - this.gutterRight;
666
+
667
+ for (i=(this.gutterLeft + this.axisWidth + prop['chart.gutter.center'] + xInterval); i<=stoppingPoint; i+=xInterval) {
668
+ co.beginPath();
669
+ co.moveTo(Math.round(i), ca.height - this.gutterBottom);
670
+ co.lineTo(Math.round(i), (ca.height - this.gutterBottom) + 4);
671
+ co.closePath();
672
+
673
+ co.stroke();
674
+ }
675
+ }
676
+ };
677
+
678
+
679
+
680
+
681
+ /**
682
+ * Figures out the maximum value, or if defined, uses xmax
683
+ */
684
+ this.getMax =
685
+ this.GetMax = function()
686
+ {
687
+ var dec = prop['chart.scale.decimals'];
688
+
689
+ // chart.xmax defined
690
+ if (prop['chart.xmax']) {
691
+
692
+ var max = prop['chart.xmax'];
693
+ var min = prop['chart.xmin'];
694
+
695
+ this.scale2 = RG.getScale2(this, {
696
+ 'max':max,
697
+ 'min':min,
698
+ 'strict': true,
699
+ 'scale.thousand':prop['chart.scale.thousand'],
700
+ 'scale.point':prop['chart.scale.point'],
701
+ 'scale.decimals':prop['chart.scale.decimals'],
702
+ 'ylabels.count':prop['chart.labels.count'],
703
+ 'scale.round':prop['chart.scale.round'],
704
+ 'units.pre': prop['chart.units.pre'],
705
+ 'units.post': prop['chart.units.post']
706
+ });
707
+ this.max = this.scale2.max;
708
+ this.min = this.scale2.min;
709
+
710
+
711
+ /**
712
+ * Generate the scale ourselves
713
+ */
714
+ } else {
715
+
716
+ var max = Math.max(RG.array_max(this.left), RG.array_max(this.right));
717
+
718
+ this.scale2 = RG.getScale2(this, {
719
+ 'max':max,
720
+ //'strict': true,
721
+ 'min':prop['chart.xmin'],
722
+ 'scale.thousand':prop['chart.scale.thousand'],
723
+ 'scale.point':prop['chart.scale.point'],
724
+ 'scale.decimals':prop['chart.scale.decimals'],
725
+ 'ylabels.count':prop['chart.labels.count'],
726
+ 'scale.round':prop['chart.scale.round'],
727
+ 'units.pre': prop['chart.units.pre'],
728
+ 'units.post': prop['chart.units.post']
729
+ });
730
+
731
+
732
+ this.max = this.scale2.max;
733
+ this.min = this.scale2.min;
734
+ }
735
+
736
+ // Don't need to return it as it is stored in this.max
737
+ };
738
+
739
+
740
+
741
+
742
+ /**
743
+ * Function to draw the left hand bars
744
+ */
745
+ this.drawLeftBars =
746
+ this.DrawLeftBars = function ()
747
+ {
748
+ var opt = {};
749
+
750
+ if (typeof arguments[0] === 'object') {
751
+ opt.shadow = arguments[0].shadow;
752
+ } else {
753
+ opt.shadow = true;
754
+ }
755
+
756
+ var offsetx = prop['chart.variant.threed.offsetx'],
757
+ offsety = prop['chart.variant.threed.offsety'];
758
+
759
+ // Set the stroke colour
760
+ co.strokeStyle = prop['chart.strokestyle'];
761
+
762
+ // Set the linewidth
763
+ co.lineWidth = prop['chart.linewidth'];
764
+
765
+ for (var i=(this.left.length - 1); i>=0; i-=1) {
766
+
767
+ /**
768
+ * Turn on a shadow if requested
769
+ */
770
+ if (prop['chart.shadow'] && prop['chart.variant'] !== '3d' && opt.shadow) {
771
+ co.shadowColor = prop['chart.shadow.color'];
772
+ co.shadowBlur = prop['chart.shadow.blur'];
773
+ co.shadowOffsetX = prop['chart.shadow.offsetx'];
774
+ co.shadowOffsetY = prop['chart.shadow.offsety'];
775
+ }
776
+
777
+
778
+
779
+
780
+ // If chart.colors.sequential is specified - handle that
781
+ // ** There's another instance of this further down **
782
+ if (prop['chart.colors.sequential']) {
783
+ co.fillStyle = prop['chart.colors'][i];
784
+
785
+ } else {
786
+ co.fillStyle = prop['chart.colors'][0];
787
+ }
788
+
789
+
790
+
791
+
792
+ /**
793
+ * Work out the coordinates
794
+ */
795
+
796
+ var width = (( (this.left[i] - this.min) / (this.max - this.min)) * this.axisWidth);
797
+
798
+ var coords = [
799
+ ma.round( this.gutterLeft + this.axisWidth - width),
800
+ ma.round( this.gutterTop + (i * ( this.axisHeight / this.left.length)) + prop['chart.margin']),
801
+ width,
802
+ this.barHeight
803
+ ];
804
+
805
+ // Draw the IE shadow if necessary
806
+ if (RG.ISOLD && prop['chart.shadow']) {
807
+ this.drawIEShadow(coords);
808
+ }
809
+
810
+
811
+ if (this.left[i] !== null) {
812
+ co.strokeRect(coords[0], coords[1], coords[2], coords[3]);
813
+ co.fillRect(coords[0], coords[1], coords[2], coords[3]);
814
+ }
815
+
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+
831
+ // Draw the 3D sides if required
832
+ if (prop['chart.variant'] === '3d' && this.left[i] !== null) {
833
+
834
+ // If the shadow is enabled draw the backface for
835
+ // (that we don't actually see
836
+ if (prop['chart.shadow'] && opt.shadow) {
837
+
838
+ co.shadowColor = prop['chart.shadow.color'];
839
+ co.shadowBlur = prop['chart.shadow.blur'];
840
+ co.shadowOffsetX = prop['chart.shadow.offsetx'];
841
+ co.shadowOffsetY = prop['chart.shadow.offsety'];
842
+
843
+
844
+ pa(co, [
845
+ 'b',
846
+ 'm',coords[0] + offsetx, coords[1] - offsety,
847
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety,
848
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety + coords[3],
849
+ 'l',coords[0] + offsetx,coords[1] - offsety + coords[3],
850
+ 'f', 'black',
851
+ 'sc', 'rgba(0,0,0,0)',
852
+ 'sx', 0,
853
+ 'sy', 0,
854
+ 'sb', 0
855
+ ]);
856
+ }
857
+
858
+
859
+
860
+ // If chart.colors.sequential is specified - handle that (again)
861
+ //
862
+ // ** There's another instance of this further up **
863
+ if (prop['chart.colors.sequential']) {
864
+ co.fillStyle = prop['chart.colors'][i];
865
+
866
+ } else {
867
+ co.fillStyle = prop['chart.colors'][0];
868
+ }
869
+
870
+ pa(co, [
871
+ 'b',
872
+ 'm',coords[0],coords[1],
873
+ 'l',coords[0] + offsetx, coords[1] - offsety,
874
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety,
875
+ 'l',coords[0] + coords[2], coords[1],
876
+ 'f', null
877
+ ]);
878
+
879
+ pa(co, [
880
+ 'b',
881
+ 'm',coords[0],coords[1],
882
+ 'l',coords[0] + offsetx, coords[1] - offsety,
883
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety,
884
+ 'l',coords[0] + coords[2], coords[1],
885
+ 'f', 'rgba(255,255,255,0.4)'
886
+ ]);
887
+ }
888
+
889
+ this.draw3DLeftVerticalAxis();
890
+
891
+
892
+
893
+
894
+
895
+
896
+
897
+
898
+
899
+
900
+
901
+
902
+
903
+
904
+
905
+ // Add the coordinates to the coords array
906
+ this.coords.push([coords[0],coords[1],coords[2],coords[3]]);
907
+ this.coordsLeft.push([coords[0],coords[1],coords[2],coords[3]]);
908
+ }
909
+
910
+ /**
911
+ * Turn off any shadow
912
+ */
913
+ RG.noShadow(this);
914
+
915
+ // Reset the linewidth
916
+ co.lineWidth = 1;
917
+ };
918
+
919
+
920
+
921
+
922
+ /**
923
+ * Function to draw the right hand bars
924
+ */
925
+ this.drawRightBars =
926
+ this.DrawRightBars = function ()
927
+ {
928
+ var opt = {};
929
+
930
+ if (typeof arguments[0] === 'object') {
931
+ opt.shadow = arguments[0].shadow;
932
+ } else {
933
+ opt.shadow = true;
934
+ }
935
+
936
+ var offsetx = prop['chart.variant.threed.offsetx'],
937
+ offsety = prop['chart.variant.threed.offsety'];
938
+
939
+
940
+
941
+
942
+ // Set the stroke colour
943
+ co.strokeStyle = prop['chart.strokestyle'];
944
+
945
+ // Set the linewidth
946
+ co.lineWidth = prop['chart.linewidth'];
947
+
948
+ /**
949
+ * Turn on a shadow if requested
950
+ */
951
+ if (prop['chart.shadow'] && prop['chart.variant'] !== '3d' && opt.shadow) {
952
+ co.shadowColor = prop['chart.shadow.color'];
953
+ co.shadowBlur = prop['chart.shadow.blur'];
954
+ co.shadowOffsetX = prop['chart.shadow.offsetx'];
955
+ co.shadowOffsetY = prop['chart.shadow.offsety'];
956
+ }
957
+
958
+ for (var i=(this.right.length - 1); i>=0; i-=1) {
959
+
960
+
961
+ // If chart.colors.sequential is specified - handle that
962
+ if (prop['chart.colors.sequential']) {
963
+ co.fillStyle = prop['chart.colors'][i];
964
+
965
+ } else {
966
+ co.fillStyle = prop['chart.colors'][0];
967
+ }
968
+
969
+
970
+ var width = (((this.right[i] - this.min) / (this.max - this.min)) * this.axisWidth);
971
+
972
+ var coords = [
973
+ ma.round( this.gutterLeft + this.axisWidth + prop['chart.gutter.center']),
974
+ ma.round( prop['chart.margin'] + (i * (this.axisHeight / this.right.length)) + this.gutterTop),
975
+ width,
976
+ this.barHeight
977
+ ];
978
+
979
+ // Draw the IE shadow if necessary
980
+ if (RG.ISOLD && prop['chart.shadow']) {
981
+ this.DrawIEShadow(coords);
982
+ }
983
+
984
+ if (this.right[i] !== null) {
985
+ co.strokeRect(ma.round( coords[0]), Math.round( coords[1]), coords[2], coords[3]);
986
+ co.fillRect(ma.round( coords[0]), Math.round( coords[1]), coords[2], coords[3]);
987
+ }
988
+
989
+
990
+
991
+
992
+
993
+
994
+
995
+
996
+
997
+
998
+
999
+
1000
+
1001
+ // Draw the 3D sides if required
1002
+ if (prop['chart.variant'] === '3d' && this.right[i] !== null) {
1003
+
1004
+ var color = co.fillStyle;
1005
+
1006
+
1007
+ // If the shadow is enabled draw the backface for
1008
+ // (that we don't actually see
1009
+ if (prop['chart.shadow'] && opt.shadow) {
1010
+
1011
+ co.shadowColor = prop['chart.shadow.color'];
1012
+ co.shadowBlur = prop['chart.shadow.blur'];
1013
+ co.shadowOffsetX = prop['chart.shadow.offsetx'];
1014
+ co.shadowOffsetY = prop['chart.shadow.offsety'];
1015
+
1016
+ pa(co, [
1017
+ 'b',
1018
+ 'm',coords[0] + offsetx, coords[1] - offsety,
1019
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety,
1020
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety + coords[3],
1021
+ 'l',coords[0] + offsetx,coords[1] - offsety + coords[3],
1022
+ 'f', 'black',
1023
+ 'sc', 'rgba(0,0,0,0)',
1024
+ 'sx', 0,
1025
+ 'sy', 0,
1026
+ 'sb', 0
1027
+ ]);
1028
+ }
1029
+
1030
+ // Draw the top
1031
+ pa(co, [
1032
+ 'b',
1033
+ 'm',coords[0],coords[1],
1034
+ 'l',coords[0] + offsetx, coords[1] - offsety,
1035
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety,
1036
+ 'l',coords[0] + coords[2], coords[1],
1037
+ 'f', color
1038
+ ]);
1039
+
1040
+
1041
+ // Draw the right hand side
1042
+ pa(co, [
1043
+ 'b',
1044
+ 'm',coords[0] + coords[2],coords[1],
1045
+ 'l',coords[0] + coords[2] + offsetx, coords[1] - offsety,
1046
+ 'l',coords[0] + coords[2] + offsetx, coords[1] - offsety + coords[3],
1047
+ 'l',coords[0] + coords[2],coords[1] + coords[3],
1048
+ 'f', color
1049
+ ]);
1050
+
1051
+ // Draw the LIGHTER top
1052
+ pa(co, [
1053
+ 'b',
1054
+ 'm',coords[0],coords[1],
1055
+ 'l',coords[0] + offsetx, coords[1] - offsety,
1056
+ 'l',coords[0] + offsetx + coords[2], coords[1] - offsety,
1057
+ 'l',coords[0] + coords[2], coords[1],
1058
+ 'f', 'rgba(255,255,255,0.6)'
1059
+ ]);
1060
+
1061
+
1062
+ // Draw the DARKER right hand side
1063
+ pa(co, [
1064
+ 'b',
1065
+ 'm',coords[0] + coords[2],coords[1],
1066
+ 'l',coords[0] + coords[2] + offsetx, coords[1] - offsety,
1067
+ 'l',coords[0] + coords[2] + offsetx, coords[1] - offsety + coords[3],
1068
+ 'l',coords[0] + coords[2],coords[1] + coords[3],
1069
+ 'f', 'rgba(0,0,0,0.3)'
1070
+ ]);
1071
+ }
1072
+
1073
+
1074
+
1075
+
1076
+
1077
+
1078
+
1079
+
1080
+
1081
+
1082
+
1083
+
1084
+
1085
+ /**
1086
+ * Add the coordinates to the coords array
1087
+ */
1088
+ this.coords.push([coords[0],coords[1],coords[2],coords[3]]);
1089
+ this.coordsRight.push([coords[0],coords[1],coords[2],coords[3]]);
1090
+ }
1091
+
1092
+
1093
+
1094
+
1095
+
1096
+
1097
+
1098
+
1099
+
1100
+
1101
+
1102
+
1103
+
1104
+
1105
+
1106
+
1107
+
1108
+
1109
+
1110
+
1111
+
1112
+
1113
+
1114
+
1115
+
1116
+
1117
+
1118
+ /**
1119
+ * Turn off any shadow
1120
+ */
1121
+ RG.NoShadow(this);
1122
+
1123
+ // Reset the linewidth
1124
+ co.lineWidth = 1;
1125
+ };
1126
+
1127
+
1128
+
1129
+
1130
+ /**
1131
+ * Draws the titles
1132
+ */
1133
+ this.drawLabels =
1134
+ this.DrawLabels = function ()
1135
+ {
1136
+
1137
+ var font = prop['chart.text.font'],
1138
+ color = prop['chart.labels.color'] || prop['chart.text.color'],
1139
+ size = prop['chart.text.size'],
1140
+ labels = prop['chart.labels'],
1141
+ barAreaHeight = ca.height - this.gutterTop - this.gutterBottom
1142
+
1143
+ co.fillStyle = color;
1144
+
1145
+ for (var i=0,len=labels.length; i<len; i+=1) {
1146
+ RG.Text2(this, {
1147
+ 'color': color,
1148
+ 'font':font,
1149
+ 'size':size,
1150
+ 'x':this.gutterLeft + this.axisWidth + (prop['chart.gutter.center'] / 2),
1151
+ 'y':this.gutterTop + ((barAreaHeight / labels.length) * (i)) + ((barAreaHeight / labels.length) / 2),
1152
+ 'text':String(labels[i] ? String(labels[i]) : ''),
1153
+ 'halign':'center',
1154
+ 'valign':'center',
1155
+ 'marker':false,
1156
+ 'tag': 'labels'
1157
+ });
1158
+ }
1159
+
1160
+
1161
+
1162
+ co.fillStyle = prop['chart.text.color'];
1163
+
1164
+
1165
+
1166
+ if (prop['chart.xlabels']) {
1167
+
1168
+ var grapharea = (ca.width - prop['chart.gutter.center'] - this.gutterLeft - this.gutterRight) / 2;
1169
+
1170
+ // Now draw the X labels for the left hand side
1171
+ for (var i=0; i<this.scale2.labels.length; ++i) {
1172
+ RG.text2(this, {
1173
+ 'font':font,
1174
+ 'size':size,
1175
+ 'x':this.gutterLeft + ((grapharea / this.scale2.labels.length) * i),
1176
+ 'y':ca.height - this.gutterBottom + 3,
1177
+ 'text':this.scale2.labels[this.scale2.labels.length - i - 1],
1178
+ 'valign':'top',
1179
+ 'halign':'center',
1180
+ 'tag': 'scale'
1181
+ });
1182
+
1183
+
1184
+
1185
+
1186
+ // Draw the scale for the right hand side
1187
+ RG.text2(this, {
1188
+ 'font':font,
1189
+ 'size':size,
1190
+ 'x':this.gutterLeft+ grapharea + prop['chart.gutter.center'] + ((grapharea / this.scale2.labels.length) * (i + 1)),
1191
+ 'y':ca.height - this.gutterBottom + 3,
1192
+ 'text':this.scale2.labels[i],
1193
+ 'valign':'top',
1194
+ 'halign':'center',
1195
+ 'tag': 'scale'
1196
+ });
1197
+ }
1198
+
1199
+
1200
+
1201
+
1202
+ // Draw zero?
1203
+ if (prop['chart.scale.zerostart']) {
1204
+ RG.text2(this, {
1205
+ 'font':font,
1206
+ 'size':size,
1207
+ 'x':this.gutterLeft + this.axisWidth,
1208
+ 'y':ca.height - this.gutterBottom + 3,
1209
+ 'text':'0',
1210
+ 'valign':'top',
1211
+ 'halign':'center',
1212
+ 'tag': 'scale'
1213
+ });
1214
+
1215
+
1216
+ RG.text2(this, {
1217
+ 'font':font,
1218
+ 'size':size,
1219
+ 'x':this.gutterLeft + this.axisWidth + this.gutterCenter,
1220
+ 'y':ca.height - this.gutterBottom + 3,
1221
+ 'text':'0',
1222
+ 'valign':'top',
1223
+ 'halign':'center',
1224
+ 'tag': 'scale'
1225
+ });
1226
+ }
1227
+ }
1228
+
1229
+ /**
1230
+ * Draw above labels
1231
+ */
1232
+ if (prop['chart.labels.above']) {
1233
+
1234
+ // Draw the left sides above labels
1235
+ for (var i=0; i<this.coordsLeft.length; ++i) {
1236
+
1237
+ if (typeof(this.left[i]) != 'number') {
1238
+ continue;
1239
+ }
1240
+
1241
+ var coords = this.coordsLeft[i];
1242
+ RG.Text2(this, {'font':font,
1243
+ 'size':size,
1244
+ 'x':coords[0] - 5,
1245
+ 'y':coords[1] + (coords[3] / 2),
1246
+ 'text':RG.number_format(this, this.left[i], prop['chart.units.pre'], prop['chart.units.post']),
1247
+ 'valign':'center',
1248
+ 'halign':'right',
1249
+ 'tag':'labels.above'
1250
+ });
1251
+ }
1252
+
1253
+ // Draw the right sides above labels
1254
+ for (i=0; i<this.coordsRight.length; ++i) {
1255
+
1256
+ if (typeof(this.right[i]) != 'number') {
1257
+ continue;
1258
+ }
1259
+
1260
+ var coords = this.coordsRight[i];
1261
+
1262
+ RG.Text2(this, {
1263
+ 'font':font,
1264
+ 'size':size,
1265
+ 'x':coords[0] + coords[2] + 5,
1266
+ 'y':coords[1] + (coords[3] / 2),
1267
+ 'text':RG.number_format(this, this.right[i], prop['chart.units.pre'], prop['chart.units.post']),
1268
+ 'valign':'center',
1269
+ 'halign':'left',
1270
+ 'tag': 'labels.above'
1271
+ });
1272
+ }
1273
+ }
1274
+ };
1275
+
1276
+
1277
+
1278
+
1279
+ /**
1280
+ * Draws the titles
1281
+ */
1282
+ this.drawTitles =
1283
+ this.DrawTitles = function ()
1284
+ {
1285
+ RG.Text2(this, {
1286
+ 'font':prop['chart.text.font'],
1287
+ 'size':prop['chart.text.size'],
1288
+ 'x':this.gutterLeft + 5,
1289
+ 'y':this.gutterTop - 5,
1290
+ 'text':String(prop['chart.title.left']),
1291
+ 'halign':'left',
1292
+ 'valign':'bottom',
1293
+ 'tag': 'title.left'
1294
+ });
1295
+
1296
+ RG.Text2(this, {
1297
+ 'font':prop['chart.text.font'],
1298
+ 'size':prop['chart.text.size'],
1299
+ 'x': ca.width - this.gutterRight - 5,
1300
+ 'y':this.gutterTop - 5,
1301
+ 'text':String(prop['chart.title.right']),
1302
+ 'halign':'right',
1303
+ 'valign':'bottom',
1304
+ 'tag': 'title.right'
1305
+ });
1306
+
1307
+
1308
+
1309
+ // Draw the main title for the whole chart
1310
+ RG.drawTitle(
1311
+ this,
1312
+ prop['chart.title'],
1313
+ this.gutterTop,
1314
+ null,
1315
+ prop['chart.title.size'] ? prop['chart.title.size'] : null
1316
+ );
1317
+ };
1318
+
1319
+
1320
+
1321
+
1322
+ /**
1323
+ * This function is used by MSIE only to manually draw the shadow
1324
+ *
1325
+ * @param array coords The coords for the bar
1326
+ */
1327
+ this.drawIEShadow =
1328
+ this.DrawIEShadow = function (coords)
1329
+ {
1330
+ var prevFillStyle = co.fillStyle;
1331
+ var offsetx = prop['chart.shadow.offsetx'];
1332
+ var offsety = prop['chart.shadow.offsety'];
1333
+
1334
+ co.lineWidth = prop['chart.linewidth'];
1335
+ co.fillStyle = prop['chart.shadow.color'];
1336
+ co.beginPath();
1337
+
1338
+ // Draw shadow here
1339
+ co.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2],coords[3]);
1340
+
1341
+ co.fill();
1342
+
1343
+ // Change the fillstyle back to what it was
1344
+ co.fillStyle = prevFillStyle;
1345
+ }
1346
+
1347
+
1348
+
1349
+
1350
+ /**
1351
+ * Returns the appropriate focussed bar coordinates
1352
+ *
1353
+ * @param e object The event object
1354
+ */
1355
+ this.getShape =
1356
+ this.getBar = function (e)
1357
+ {
1358
+ var canvas = this.canvas,
1359
+ context = this.context,
1360
+ mouseCoords = RG.getMouseXY(e)
1361
+
1362
+ /**
1363
+ * Loop through the bars determining if the mouse is over a bar
1364
+ */
1365
+ for (var i=0; i<this.coords.length; i++) {
1366
+
1367
+ var mouseX = mouseCoords[0],
1368
+ mouseY = mouseCoords[1],
1369
+ left = this.coords[i][0],
1370
+ top = this.coords[i][1],
1371
+ width = this.coords[i][2],
1372
+ height = this.coords[i][3]
1373
+
1374
+ //if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
1375
+ pa(co, ['b','r',left,top,width,height]);
1376
+
1377
+ if (co.isPointInPath(mouseX, mouseY)) {
1378
+
1379
+ var tooltip = RG.parseTooltipText(prop['chart.tooltips'], i);
1380
+
1381
+ return {
1382
+ 0: this,1: left,2: top,3: width,4: height,5: i,
1383
+ 'object': this, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip
1384
+ };
1385
+ }
1386
+ }
1387
+
1388
+ return null;
1389
+ };
1390
+
1391
+
1392
+
1393
+
1394
+ /**
1395
+ * Each object type has its own Highlight() function which highlights the appropriate shape
1396
+ *
1397
+ * @param object shape The shape to highlight
1398
+ */
1399
+ this.highlight =
1400
+ this.Highlight = function (shape)
1401
+ {
1402
+ // Add the new highlight
1403
+ RG.Highlight.Rect(this, shape);
1404
+ };
1405
+
1406
+
1407
+
1408
+
1409
+ /**
1410
+ * When you click on the canvas, this will return the relevant value (if any)
1411
+ *
1412
+ * REMEMBER This function will need updating if the Bipolar ever gets chart.ymin
1413
+ *
1414
+ * @param object e The event object
1415
+ */
1416
+ this.getValue = function (e)
1417
+ {
1418
+ var obj = e.target.__object__;
1419
+ var mouseXY = RG.getMouseXY(e);
1420
+ var mouseX = mouseXY[0];
1421
+
1422
+ /**
1423
+ * Left hand side
1424
+ */
1425
+ if (mouseX > this.gutterLeft && mouseX < ( (ca.width / 2) - (prop['chart.gutter.center'] / 2) )) {
1426
+ var value = (mouseX - prop['chart.gutter.left']) / this.axisWidth;
1427
+ value = this.max - (value * this.max);
1428
+ }
1429
+
1430
+ /**
1431
+ * Right hand side
1432
+ */
1433
+ if (mouseX < (ca.width - this.gutterRight) && mouseX > ( (ca.width / 2) + (prop['chart.gutter.center'] / 2) )) {
1434
+ var value = (mouseX - prop['chart.gutter.left'] - this.axisWidth - prop['chart.gutter.center']) / this.axisWidth;
1435
+ value = (value * this.max);
1436
+ }
1437
+
1438
+ return value;
1439
+ };
1440
+
1441
+
1442
+
1443
+
1444
+ /**
1445
+ * The getObjectByXY() worker method. Don't call this call:
1446
+ *
1447
+ * RGraph.ObjectRegistry.getObjectByXY(e)
1448
+ *
1449
+ * @param object e The event object
1450
+ */
1451
+ this.getObjectByXY = function (e)
1452
+ {
1453
+ var mouseXY = RG.getMouseXY(e);
1454
+
1455
+ // Adjust the mouse Y coordinate for when the bar chart is
1456
+ // a 3D variant
1457
+
1458
+ if (prop['chart.variant'] === '3d') {
1459
+ var adjustment = prop['chart.variant.threed.angle'] * mouseXY[0];
1460
+ mouseXY[1] -= adjustment;
1461
+ }
1462
+
1463
+
1464
+
1465
+ if (
1466
+ mouseXY[0] > prop['chart.gutter.left']
1467
+ && mouseXY[0] < (ca.width - prop['chart.gutter.right'])
1468
+ && mouseXY[1] > prop['chart.gutter.top']
1469
+ && mouseXY[1] < (ca.height - prop['chart.gutter.bottom'])
1470
+ ) {
1471
+
1472
+ return this;
1473
+ }
1474
+ };
1475
+
1476
+
1477
+
1478
+
1479
+ /**
1480
+ * This function positions a tooltip when it is displayed
1481
+ *
1482
+ * @param obj object The chart object
1483
+ * @param int x The X coordinate specified for the tooltip
1484
+ * @param int y The Y coordinate specified for the tooltip
1485
+ * @param objec tooltip The tooltips DIV element
1486
+ */
1487
+ this.positionTooltip = function (obj, x, y, tooltip, idx)
1488
+ {
1489
+ var coordX = obj.coords[tooltip.__index__][0],
1490
+ coordY = obj.coords[tooltip.__index__][1],
1491
+ coordW = obj.coords[tooltip.__index__][2],
1492
+ coordH = obj.coords[tooltip.__index__][3],
1493
+ canvasXY = RG.getCanvasXY(obj.canvas),
1494
+ mouseXY = RG.getMouseXY(window.event),
1495
+ gutterLeft = obj.Get('chart.gutter.left'),
1496
+ gutterTop = obj.Get('chart.gutter.top'),
1497
+ width = tooltip.offsetWidth,
1498
+ height = tooltip.offsetHeight
1499
+
1500
+ // If the chart is a 3D version the tooltip Y position needs this
1501
+ // adjustment
1502
+ if (prop['chart.variant'] === '3d' && mouseXY) {
1503
+ var adjustment = (prop['chart.variant.threed.angle'] * mouseXY[0]);
1504
+ }
1505
+
1506
+ // Set the top position
1507
+ tooltip.style.left = 0;
1508
+ tooltip.style.top = canvasXY[1] + coordY - height - 7 + (adjustment || 0) + 'px';
1509
+
1510
+
1511
+ // By default any overflow is hidden
1512
+ tooltip.style.overflow = '';
1513
+
1514
+ // The arrow
1515
+ var img = new Image();
1516
+ img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
1517
+ img.style.position = 'absolute';
1518
+ img.id = '__rgraph_tooltip_pointer__';
1519
+ img.style.top = (tooltip.offsetHeight - 2) + 'px';
1520
+ tooltip.appendChild(img);
1521
+
1522
+ // Reposition the tooltip if at the edges:
1523
+
1524
+ // LEFT edge
1525
+ if ((canvasXY[0] + coordX + (coordW / 2)- (width / 2)) < 0) {
1526
+ tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + (coordW / 2) + 'px';
1527
+ img.style.left = ((width * 0.1) - 8.5) + 'px';
1528
+
1529
+ // RIGHT edge
1530
+ } else if ((canvasXY[0] + coordX + width) > doc.body.offsetWidth) {
1531
+ tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + (coordW / 2) + 'px';
1532
+ img.style.left = ((width * 0.9) - 8.5) + 'px';
1533
+
1534
+ // Default positioning - CENTERED
1535
+ } else {
1536
+ tooltip.style.left = (canvasXY[0] + coordX + (coordW / 2) - (width * 0.5)) + 'px';
1537
+ img.style.left = ((width * 0.5) - 8.5) + 'px';
1538
+ }
1539
+ };
1540
+
1541
+
1542
+
1543
+
1544
+ /**
1545
+ * Returns the X coords for a value. Returns two coords because there are... two scales.
1546
+ *
1547
+ * @param number value The value to get the coord for
1548
+ */
1549
+ this.getXCoord = function (value)
1550
+ {
1551
+ if (value > this.max || value < 0) {
1552
+ return null;
1553
+ }
1554
+
1555
+ var ret = [];
1556
+
1557
+ // The offset into the graph area
1558
+ var offset = ((value / this.max) * this.axisWidth);
1559
+
1560
+ // Get the coords (one fo each side)
1561
+ ret[0] = (this.gutterLeft + this.axisWidth) - offset;
1562
+ ret[1] = (ca.width - this.gutterRight - this.axisWidth) + offset;
1563
+
1564
+ return ret;
1565
+ };
1566
+
1567
+
1568
+
1569
+
1570
+ /**
1571
+ * This allows for easy specification of gradients
1572
+ */
1573
+ this.parseColors = function ()
1574
+ {
1575
+ // Save the original colors so that they can be restored when the canvas is reset
1576
+ if (this.original_colors.length === 0) {
1577
+ this.original_colors['chart.colors'] = RG.array_clone(prop['chart.colors']);
1578
+ this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.fill']);
1579
+ this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
1580
+ this.original_colors['chart.axis.color'] = RG.array_clone(prop['chart.axis.color']);
1581
+ this.original_colors['chart.strokestyle'] = RG.array_clone(prop['chart.strokestyle']);
1582
+ }
1583
+
1584
+ var props = this.properties;
1585
+ var colors = props['chart.colors'];
1586
+
1587
+ for (var i=0; i<colors.length; ++i) {
1588
+ colors[i] = this.parseSingleColorForGradient(colors[i]);
1589
+ }
1590
+
1591
+ props['chart.highlight.stroke'] = this.parseSingleColorForGradient(props['chart.highlight.stroke']);
1592
+ props['chart.highlight.fill'] = this.parseSingleColorForGradient(props['chart.highlight.fill']);
1593
+ props['chart.axis.color'] = this.parseSingleColorForGradient(props['chart.axis.color']);
1594
+ props['chart.strokestyle'] = this.parseSingleColorForGradient(props['chart.strokestyle']);
1595
+ };
1596
+
1597
+
1598
+
1599
+
1600
+ /**
1601
+ * Use this function to reset the object to the post-constructor state. Eg reset colors if
1602
+ * need be etc
1603
+ */
1604
+ this.reset = function ()
1605
+ {
1606
+ };
1607
+
1608
+
1609
+
1610
+
1611
+ /**
1612
+ * This parses a single color value
1613
+ */
1614
+ this.parseSingleColorForGradient = function (color)
1615
+ {
1616
+ if (!color || typeof(color) != 'string') {
1617
+ return color;
1618
+ }
1619
+
1620
+ if (color.match(/^gradient\((.*)\)$/i)) {
1621
+
1622
+ var parts = RegExp.$1.split(':');
1623
+
1624
+ // Create the gradient
1625
+ var grad = co.createLinearGradient(prop['chart.gutter.left'],0,ca.width - prop['chart.gutter.right'],0);
1626
+
1627
+ var diff = 1 / (parts.length - 1);
1628
+
1629
+ grad.addColorStop(0, RG.trim(parts[0]));
1630
+
1631
+ for (var j=1; j<parts.length; ++j) {
1632
+ grad.addColorStop(j * diff, RG.trim(parts[j]));
1633
+ }
1634
+ }
1635
+
1636
+ return grad ? grad : color;
1637
+ };
1638
+
1639
+
1640
+
1641
+
1642
+ /**
1643
+ * Using a function to add events makes it easier to facilitate method chaining
1644
+ *
1645
+ * @param string type The type of even to add
1646
+ * @param function func
1647
+ */
1648
+ this.on = function (type, func)
1649
+ {
1650
+ if (type.substr(0,2) !== 'on') {
1651
+ type = 'on' + type;
1652
+ }
1653
+
1654
+ this[type] = func;
1655
+
1656
+ return this;
1657
+ };
1658
+
1659
+
1660
+
1661
+
1662
+ //
1663
+ // Draw the background grid
1664
+ //
1665
+ this.drawBackgroundGrid = function ()
1666
+ {
1667
+ if (prop['chart.background.grid']) {
1668
+
1669
+ var variant = prop['chart.variant'],
1670
+ color = prop['chart.background.grid.color'],
1671
+ numvlines = prop['chart.labels.count'],
1672
+ numhlines = this.left.length,
1673
+ vlines = prop['chart.background.grid.vlines'],
1674
+ hlines = prop['chart.background.grid.hlines'],
1675
+ linewidth = prop['chart.background.grid.linewidth'];
1676
+
1677
+ // Autofit
1678
+ if (typeof prop['chart.background.grid.autofit.numhlines'] === 'number') {
1679
+ numhlines = prop['chart.background.grid.autofit.numhlines'];
1680
+ }
1681
+
1682
+ if (typeof prop['chart.background.grid.autofit.numvlines'] === 'number') {
1683
+ numvlines = prop['chart.background.grid.autofit.numvlines'];
1684
+ }
1685
+
1686
+ co.lineWidth = linewidth;
1687
+
1688
+ // If it's a bar and 3D variant, translate
1689
+ if (variant == '3d') {
1690
+ co.save();
1691
+ co.translate(
1692
+ prop['chart.variant.threed.offsetx'],
1693
+ -1 * prop['chart.variant.threed.offsety']
1694
+ );
1695
+ }
1696
+
1697
+ // Draw vertical grid lines for the left side
1698
+ if (vlines) {
1699
+ for (var i=0; i<=numvlines; i+=1) {
1700
+ pa(co, [
1701
+ 'b',
1702
+ 'm', this.gutterLeft + (this.axisWidth / numvlines) * i, this.gutterTop,
1703
+ 'l', this.gutterLeft + (this.axisWidth / numvlines) * i, this.gutterTop + this.axisHeight,
1704
+ 's', color
1705
+ ]);
1706
+
1707
+ }
1708
+ }
1709
+
1710
+ // Draw horizontal grid lines for the left side
1711
+ if (hlines) {
1712
+ for (var i=0; i<=numhlines; i+=1) {
1713
+ pa(co, [
1714
+ 'b',
1715
+ 'm', this.gutterLeft, this.gutterTop + (this.axisHeight / numhlines) * i,
1716
+ 'l', this.gutterLeft + this.axisWidth, this.gutterTop + (this.axisHeight / numhlines) * i,
1717
+ 's', color
1718
+ ]);
1719
+ }
1720
+ }
1721
+
1722
+
1723
+ // Draw vertical grid lines for the right side
1724
+ if (vlines) {
1725
+ for (var i=0; i<=numvlines; i+=1) {
1726
+ pa(co, [
1727
+ 'b',
1728
+ 'm', this.gutterLeft + this.gutterCenter + this.axisWidth + (this.axisWidth / numvlines) * i, this.gutterTop,
1729
+ 'l', this.gutterLeft + this.gutterCenter + this.axisWidth + (this.axisWidth / numvlines) * i, this.gutterTop + this.axisHeight,
1730
+ 's', color
1731
+ ]);
1732
+ }
1733
+ }
1734
+
1735
+ // Draw horizontal grid lines for the right side
1736
+ if (hlines) {
1737
+ for (var i=0; i<=numhlines; i+=1) {
1738
+ pa(co, [
1739
+ 'b',
1740
+ 'm', this.gutterLeft + this.axisWidth + this.gutterCenter, this.gutterTop + (this.axisHeight / numhlines) * i,
1741
+ 'l', this.gutterLeft + this.axisWidth + this.gutterCenter + this.axisWidth, this.gutterTop + (this.axisHeight / numhlines) * i,
1742
+ 's', color
1743
+ ]);
1744
+ }
1745
+ }
1746
+
1747
+
1748
+ // If it's a bar and 3D variant, translate
1749
+ if (variant == '3d') {
1750
+ co.restore();
1751
+ }
1752
+ }
1753
+ };
1754
+
1755
+
1756
+
1757
+
1758
+ /**
1759
+ * This function runs once only
1760
+ * (put at the end of the file (before any effects))
1761
+ */
1762
+ this.firstDrawFunc = function ()
1763
+ {
1764
+ // Tooltips need reversing now because the bars
1765
+ // are drawn from the bottom up
1766
+ if (prop['chart.tooltips']) {
1767
+ prop['chart.tooltips'] = RG.arrayReverse(prop['chart.tooltips']);
1768
+ }
1769
+ };
1770
+
1771
+
1772
+
1773
+
1774
+
1775
+
1776
+
1777
+
1778
+ /**
1779
+ * Objects are now always registered so that when RGraph.Redraw()
1780
+ * is called this chart will be redrawn.
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 dsta - handle that.
1790
+ */
1791
+ if (parseConfObjectForOptions) {
1792
+ RG.parseObjectStyleConfig(this, conf.options);
1793
+ }
1794
+
1795
+
1796
+
1797
+
1798
+ /**
1799
+ * Grow
1800
+ *
1801
+ * The Bipolar chart Grow effect gradually increases the values of the bars
1802
+ *
1803
+ * @param object An object of options - eg: {frames: 30}
1804
+ * @param function A function to call when the effect is complete
1805
+ */
1806
+ this.grow = function ()
1807
+ {
1808
+ // Callback
1809
+ var opt = arguments[0] || {};
1810
+ var frames = opt.frames || 30;
1811
+ var frame = 0;
1812
+ var callback = arguments[1] || function () {};
1813
+ var obj = this;
1814
+
1815
+ // Save the data
1816
+ var originalLeft = RG.arrayClone(this.left);
1817
+ var originalRight = RG.arrayClone(this.right);
1818
+
1819
+
1820
+ // Stop the scale from changing by setting xmax (if it's not already set)
1821
+ if (RG.isNull(prop['chart.xmax'])) {
1822
+
1823
+ var xmax = 0;
1824
+
1825
+ // Go through the left and right data
1826
+ for (var i=0; i<this.left.length; i+=1) { xmax = ma.max(xmax, ma.abs(this.left[i])); }
1827
+ for (var i=0; i<this.right.length; i+=1) { xmax = ma.max(xmax, ma.abs(this.right[i])); }
1828
+
1829
+ var scale = RG.getScale2(obj, {'max':xmax});
1830
+ this.Set('chart.xmax', scale.max);
1831
+ }
1832
+
1833
+
1834
+
1835
+
1836
+
1837
+
1838
+
1839
+
1840
+
1841
+
1842
+
1843
+
1844
+ var iterator = function ()
1845
+ {
1846
+ var easingMultiplier = RG.Effects.getEasingMultiplier(frames, frame);
1847
+
1848
+ for (var i=0; i<obj.left.length; i+=1) { obj.left[i] = easingMultiplier * originalLeft[i]; }
1849
+ for (var i=0; i<obj.right.length; i+=1) { obj.right[i] = easingMultiplier * originalRight[i]; }
1850
+
1851
+ RG.redrawCanvas(obj.canvas);
1852
+
1853
+ // Repeat or call the end function if one is defined
1854
+ if (frame < frames) {
1855
+ frame += 1;
1856
+ RG.Effects.updateCanvas(iterator);
1857
+ } else {
1858
+ callback(obj);
1859
+ }
1860
+ };
1861
+
1862
+ iterator();
1863
+
1864
+ return this;
1865
+ };
1866
+
1867
+
1868
+
1869
+
1870
+ /**
1871
+ * Bipolar chart Wave effect.
1872
+ *
1873
+ * @param object OPTIONAL An object map of options. You specify 'frames' here to give the number of frames in the effect
1874
+ * @param function OPTIONAL A function that will be called when the effect is complete
1875
+ */
1876
+ this.wave = function ()
1877
+ {
1878
+ var obj = this,
1879
+ opt = arguments[0] || {};
1880
+ opt.frames = opt.frames || 60;
1881
+ opt.startFrames_left = [];
1882
+ opt.startFrames_right = [];
1883
+ opt.counters_left = [];
1884
+ opt.counters_right = [];
1885
+
1886
+ var framesperbar = opt.frames / 3,
1887
+ frame_left = -1,
1888
+ frame_right = -1,
1889
+ callback = arguments[1] || function () {},
1890
+ original_left = RG.arrayClone(obj.left),
1891
+ original_right = RG.arrayClone(obj.right);
1892
+
1893
+ for (var i=0,len=obj.left.length; i<len; i+=1) {
1894
+ opt.startFrames_left[i] = ((opt.frames / 2) / (obj.left.length - 1)) * i;
1895
+ opt.startFrames_right[i] = ((opt.frames / 2) / (obj.right.length - 1)) * i;
1896
+ opt.counters_left[i] = 0;
1897
+ opt.counters_right[i] = 0;
1898
+ }
1899
+
1900
+ // This stops the chart from jumping
1901
+ obj.draw();
1902
+ obj.set('xmax', obj.scale2.max);
1903
+ RG.clear(obj.canvas);
1904
+
1905
+
1906
+ // Zero all of the data
1907
+ for (var i=0,len=obj.left.length; i<len; i+=1) {
1908
+ if (typeof obj.left[i] === 'number') obj.left[i] = 0;
1909
+ if (typeof obj.right[i] === 'number') obj.right[i] = 0;
1910
+ }
1911
+
1912
+ //
1913
+ // Iterate over the left side
1914
+ //
1915
+ function iteratorLeft ()
1916
+ {
1917
+ ++frame_left;
1918
+
1919
+ for (var i=0,len=obj.left.length; i<len; i+=1) {
1920
+ if (frame_left > opt.startFrames_left[i]) {
1921
+
1922
+ var isNull = RG.isNull(obj.left[i]);
1923
+
1924
+ obj.left[i] = ma.min(
1925
+ ma.abs(original_left[i]),
1926
+ ma.abs(original_left[i] * ( (opt.counters_left[i]++) / framesperbar))
1927
+ );
1928
+
1929
+ // Make the number negative if the original was
1930
+ if (original_left[i] < 0) {
1931
+ obj.left[i] *= -1;
1932
+ }
1933
+
1934
+ if (isNull) {
1935
+ obj.left[i] = null;
1936
+ }
1937
+ } else {
1938
+ obj.left[i] = typeof obj.left[i] === 'object' && obj.left[i] ? RG.arrayPad([], obj.left[i].length, 0) : (RG.isNull(obj.left[i]) ? null : 0);
1939
+ }
1940
+
1941
+ }
1942
+
1943
+
1944
+ // No callback here - only called by the right function
1945
+ if (frame_left < opt.frames) {
1946
+ RG.redrawCanvas(obj.canvas);
1947
+ RG.Effects.updateCanvas(iteratorLeft);
1948
+ }
1949
+ }
1950
+
1951
+
1952
+
1953
+
1954
+ //
1955
+ // Iterate over the right side
1956
+ //
1957
+ function iteratorRight ()
1958
+ {
1959
+ ++frame_right;
1960
+
1961
+ for (var i=0,len=obj.right.length; i<len; i+=1) {
1962
+ if (frame_right > opt.startFrames_right[i]) {
1963
+
1964
+ var isNull = RG.isNull(obj.right[i]);
1965
+
1966
+ obj.right[i] = ma.min(
1967
+ ma.abs(original_right[i]),
1968
+ ma.abs(original_right[i] * ( (opt.counters_right[i]++) / framesperbar))
1969
+ );
1970
+
1971
+ // Make the number negative if the original was
1972
+ if (original_right[i] < 0) {
1973
+ obj.right[i] *= -1;
1974
+ }
1975
+
1976
+ if (isNull) {
1977
+ obj.right[i] = null;
1978
+ }
1979
+
1980
+ } else {
1981
+ obj.right[i] = typeof obj.right[i] === 'object' && obj.right[i] ? RG.arrayPad([], obj.right[i].length, 0) : (RG.isNull(obj.right[i]) ? null : 0);
1982
+ }
1983
+ }
1984
+
1985
+
1986
+ // No callback here - only called by the right function
1987
+ if (frame_right < opt.frames) {
1988
+ RG.redrawCanvas(obj.canvas);
1989
+ RG.Effects.updateCanvas(iteratorRight);
1990
+ } else {
1991
+ callback(this);
1992
+ }
1993
+ }
1994
+
1995
+
1996
+
1997
+
1998
+ iteratorLeft();
1999
+ iteratorRight();
2000
+
2001
+ return this;
2002
+ };
2003
+ };