Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
    /**
2
    * o-------------------------------------------------------------------------------o
3
    * | This file is part of the RGraph package. RGraph is Free software, licensed    |
4
    * | under the MIT license - so it's free to use for all purposes. Extended        |
5
    * | support is available if required and donations are always welcome! You can    |
6
    * | read more here:                                                               |
7
    * |                         http://www.rgraph.net/support                         |
8
    * o-------------------------------------------------------------------------------o
9
    */
10
 
11
    if (typeof(RGraph) == 'undefined') RGraph = {};
12
 
13
    /**
14
    * The bar chart constructor
15
    *
16
    * @param object canvas The canvas object
17
    * @param array  data   The chart data
18
    */
19
    RGraph.Bar = function (id, data)
20
    {
21
        // Get the canvas and context objects
22
        this.id                = id;
23
        this.canvas            = document.getElementById(typeof id === 'object' ? id.id : id);
24
        this.context           = this.canvas.getContext ? this.canvas.getContext("2d") : null;
25
        this.canvas.__object__ = this;
26
        this.type              = 'bar';
27
        this.max               = 0;
28
        this.stackedOrGrouped  = false;
29
        this.isRGraph          = true;
30
        this.uid               = RGraph.CreateUID();
31
        this.canvas.uid        = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
32
        this.colorsParsed      = false;
33
        this.original_colors   = [];
34
 
35
 
36
        /**
37
        * Compatibility with older browsers
38
        */
39
        RGraph.OldBrowserCompat(this.context);
40
 
41
 
42
        // Various config type stuff
43
        this.properties = {
44
            'chart.background.barcolor1':   'rgba(0,0,0,0)',
45
            'chart.background.barcolor2':   'rgba(0,0,0,0)',
46
            'chart.background.grid':        true,
47
            'chart.background.grid.color':  '#ddd',
48
            'chart.background.grid.width':  1,
49
            'chart.background.grid.hsize':  20,
50
            'chart.background.grid.vsize':  20,
51
            'chart.background.grid.vlines': true,
52
            'chart.background.grid.hlines': true,
53
            'chart.background.grid.border': true,
54
            'chart.background.grid.autofit':true,
55
            'chart.background.grid.autofit.numhlines': 5,
56
            'chart.background.grid.autofit.numvlines': 20,
57
            'chart.background.grid.dashed': false,
58
            'chart.background.grid.dotted': false,
59
            'chart.background.image.stretch': true,
60
            'chart.background.image.x':     null,
61
            'chart.background.image.y':     null,
62
            'chart.background.image.w':     null,
63
            'chart.background.image.h':     null,
64
            'chart.background.image.align': null,
65
            'chart.numyticks':              10,
66
            'chart.hmargin':                5,
67
            'chart.hmargin.grouped':        1,
68
            'chart.strokecolor':            'rgba(0,0,0,0)',
69
            'chart.axis.color':             'black',
70
            'chart.axis.linewidth':         1,
71
            'chart.gutter.top':             25,
72
            'chart.gutter.bottom':          25,
73
            'chart.gutter.left':            25,
74
            'chart.gutter.right':           25,
75
            'chart.labels':                 null,
76
            'chart.labels.ingraph':         null,
77
            'chart.labels.above':           false,
78
            'chart.labels.above.decimals':  0,
79
            'chart.labels.above.size':      null,
80
            'chart.labels.above.color':     null,
81
            'chart.labels.above.angle':     null,
82
            'chart.ylabels':                true,
83
            'chart.ylabels.count':          5,
84
            'chart.ylabels.inside':         false,
85
            'chart.xlabels.offset':         0,
86
            'chart.xaxispos':               'bottom',
87
            'chart.yaxispos':               'left',
88
            'chart.text.angle':             0,
89
            'chart.text.color':             'black', // Gradients aren't supported for this color
90
            'chart.text.size':              10,
91
            'chart.text.font':              'Arial',
92
            'chart.ymin':                   0,
93
            'chart.ymax':                   null,
94
            'chart.title':                  '',
95
            'chart.title.font':             null,
96
            'chart.title.background':       null, // Gradients aren't supported for this color
97
            'chart.title.hpos':             null,
98
            'chart.title.vpos':             null,
99
            'chart.title.bold':             true,
100
            'chart.title.xaxis':            '',
101
            'chart.title.xaxis.bold':       true,
102
            'chart.title.xaxis.size':       null,
103
            'chart.title.xaxis.font':       null,
104
            'chart.title.yaxis':            '',
105
            'chart.title.yaxis.bold':       true,
106
            'chart.title.yaxis.size':       null,
107
            'chart.title.yaxis.font':       null,
108
            'chart.title.yaxis.color':      null, // Gradients aren't supported for this color
109
            'chart.title.xaxis.pos':        null,
110
            'chart.title.yaxis.pos':        null,
111
            'chart.title.yaxis.x':          null,
112
            'chart.title.yaxis.y':          null,
113
            'chart.title.xaxis.x':          null,
114
            'chart.title.xaxis.y':          null,
115
            'chart.title.x':                null,
116
            'chart.title.y':                null,
117
            'chart.title.halign':           null,
118
            'chart.title.valign':           null,
119
            'chart.colors':                 ['#01B4FF', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'],
120
            'chart.colors.sequential':      false,
121
            'chart.colors.reverse':         false,
122
            'chart.grouping':               'grouped',
123
            'chart.variant':                'bar',
124
            'chart.variant.sketch.verticals': true,
125
            'chart.shadow':                 false,
126
            'chart.shadow.color':           '#999',  // Gradients aren't supported for this color
127
            'chart.shadow.offsetx':         3,
128
            'chart.shadow.offsety':         3,
129
            'chart.shadow.blur':            3,
130
            'chart.tooltips':               null,
131
            'chart.tooltips.effect':        'fade',
132
            'chart.tooltips.css.class':     'RGraph_tooltip',
133
            'chart.tooltips.event':         'onclick',
134
            'chart.tooltips.highlight':     true,
135
            'chart.highlight.stroke':       'rgba(0,0,0,0)',
136
            'chart.highlight.fill':         'rgba(255,255,255,0.7)',
137
            'chart.background.hbars':       null,
138
            'chart.key':                    null,
139
            'chart.key.background':         'white',
140
            'chart.key.position':           'graph',
141
            'chart.key.shadow':             false,
142
            'chart.key.shadow.color':       '#666',
143
            'chart.key.shadow.blur':        3,
144
            'chart.key.shadow.offsetx':     2,
145
            'chart.key.shadow.offsety':     2,
146
            'chart.key.position.gutter.boxed':false,
147
            'chart.key.position.x':         null,
148
            'chart.key.position.y':         null,
149
            'chart.key.interactive':        false,
150
            'chart.key.interactive.highlight.chart.stroke':'black',
151
            'chart.key.interactive.highlight.chart.fill':'rgba(255,255,255,0.7)',
152
            'chart.key.interactive.highlight.label':'rgba(255,0,0,0.2)',
153
            'chart.key.halign':             'right',
154
            'chart.key.color.shape':        'square',
155
            'chart.key.rounded':            true,
156
            'chart.key.text.size':          10,
157
            'chart.key.linewidth':          1,
158
            'chart.key.colors':             null,
159
            'chart.key.text.color':         'black',
160
            'chart.contextmenu':            null,
161
            'chart.units.pre':              '',
162
            'chart.units.post':             '',
163
            'chart.scale.decimals':         0,
164
            'chart.scale.point':            '.',
165
            'chart.scale.thousand':         ',',
166
            'chart.crosshairs':             false,
167
            'chart.crosshairs.color':       '#333',
168
            'chart.crosshairs.hline':       true,
169
            'chart.crosshairs.vline':       true,
170
            'chart.linewidth':              1,
171
            'chart.annotatable':            false,
172
            'chart.annotate.color':         'black',
173
            'chart.zoom.factor':            1.5,
174
            'chart.zoom.fade.in':           true,
175
            'chart.zoom.fade.out':          true,
176
            'chart.zoom.hdir':              'right',
177
            'chart.zoom.vdir':              'down',
178
            'chart.zoom.frames':            25,
179
            'chart.zoom.delay':             16.666,
180
            'chart.zoom.shadow':            true,
181
            'chart.zoom.background':        true,
182
            'chart.resizable':              false,
183
            'chart.resize.handle.background': null,
184
            'chart.adjustable':             false,
185
            'chart.noaxes':                 false,
186
            'chart.noxaxis':                false,
187
            'chart.noyaxis':                false,
188
            'chart.events.click':           null,
189
            'chart.events.mousemove':       null,
190
            'chart.numxticks':              null,
191
            'chart.bevel':                  false
192
        }
193
 
194
        // Check for support
195
        if (!this.canvas) {
196
            alert('[BAR] No canvas support');
197
            return;
198
        }
199
 
200
        /**
201
        * Determine whether the chart will contain stacked or grouped bars
202
        */
203
        for (var i=0; i<data.length; ++i) {
204
            if (typeof(data[i]) == 'object' && !RGraph.is_null(data[i])) {
205
                this.stackedOrGrouped = true;
206
            }
207
        }
208
 
209
 
210
        /**
211
        * Create the dollar objects so that functions can be added to them
212
        */
213
        var linear_data = RGraph.array_linearize(data);
214
        for (var i=0; i<linear_data.length; ++i) {
215
            this['$' + i] = {};
216
        }
217
 
218
 
219
        // Store the data
220
        this.data = data;
221
 
222
        // Used to store the coords of the bars
223
        this.coords     = [];
224
        this.coords2    = [];
225
        this.coordsText = [];
226
 
227
 
228
 
229
        /**
230
        * This linearises the data. Doing so can make it easier to pull
231
        * out the appropriate data from tooltips
232
        */
233
        this.data_arr = RGraph.array_linearize(this.data);
234
 
235
 
236
        /**
237
        * Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
238
        * done already
239
        */
240
        if (!this.canvas.__rgraph_aa_translated__) {
241
            this.context.translate(0.5,0.5);
242
 
243
            this.canvas.__rgraph_aa_translated__ = true;
244
        }
245
 
246
 
247
 
248
 
249
        ///////////////////////////////// SHORT PROPERTIES /////////////////////////////////
250
 
251
 
252
 
253
 
254
        var RG   = RGraph;
255
        var ca   = this.canvas;
256
        var co   = ca.getContext('2d');
257
        var prop = this.properties;
258
        //var $jq  = jQuery;
259
 
260
 
261
 
262
 
263
        //////////////////////////////////// METHODS ///////////////////////////////////////
264
 
265
 
266
 
267
 
268
        /**
269
        * A setter
270
        *
271
        * @param name  string The name of the property to set
272
        * @param value mixed  The value of the property
273
        */
274
        this.Set = function (name, value)
275
        {
276
            name = name.toLowerCase();
277
 
278
            /**
279
            * This should be done first - prepend the propertyy name with "chart." if necessary
280
            */
281
            if (name.substr(0,6) != 'chart.') {
282
                name = 'chart.' + name;
283
            }
284
 
285
            if (name == 'chart.labels.abovebar') {
286
                name = 'chart.labels.above';
287
            }
288
 
289
            if (name == 'chart.strokestyle') {
290
                name = 'chart.strokecolor';
291
            }
292
 
293
            /**
294
            * Check for xaxispos
295
            */
296
            if (name == 'chart.xaxispos' ) {
297
                if (value != 'bottom' && value != 'center' && value != 'top') {
298
                    alert('[BAR] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
299
                    value = 'center';
300
                }
301
 
302
                if (value == 'top') {
303
                    for (var i=0; i<this.data.length; ++i) {
304
                        if (typeof(this.data[i]) == 'number' && this.data[i] > 0) {
305
                            alert('[BAR] The data element with index ' + i + ' should be negative');
306
                        }
307
                    }
308
                }
309
            }
310
 
311
            /**
312
            * lineWidth doesn't appear to like a zero setting
313
            */
314
            if (name.toLowerCase() == 'chart.linewidth' && value == 0) {
315
                value = 0.0001;
316
            }
317
 
318
            prop[name] = value;
319
 
320
            return this;
321
        }
322
 
323
 
324
 
325
 
326
        /**
327
        * A getter
328
        *
329
        * @param name  string The name of the property to get
330
        */
331
        this.Get = function (name)
332
        {
333
            /**
334
            * This should be done first - prepend the property name with "chart." if necessary
335
            */
336
            if (name.substr(0,6) != 'chart.') {
337
                name = 'chart.' + name;
338
            }
339
 
340
            return prop[name];
341
        }
342
 
343
 
344
 
345
 
346
        /**
347
        * The function you call to draw the bar chart
348
        */
349
        this.Draw = function ()
350
        {
351
            // MUST be the first thing done!
352
            if (typeof(prop['chart.background.image']) == 'string') {
353
                RG.DrawBackgroundImage(this);
354
            }
355
 
356
            /**
357
            * Fire the onbeforedraw event
358
            */
359
            RG.FireCustomEvent(this, 'onbeforedraw');
360
 
361
 
362
 
363
            /**
364
            * Parse the colors. This allows for simple gradient syntax
365
            */
366
            if (!this.colorsParsed) {
367
                this.parseColors();
368
 
369
                // Don't want to do this again
370
                this.colorsParsed = true;
371
            }
372
 
373
 
374
 
375
            /**
376
            * This is new in May 2011 and facilitates indiviual gutter settings,
377
            * eg chart.gutter.left
378
            */
379
            this.gutterLeft   = prop['chart.gutter.left'];
380
            this.gutterRight  = prop['chart.gutter.right'];
381
            this.gutterTop    = prop['chart.gutter.top'];
382
            this.gutterBottom = prop['chart.gutter.bottom'];
383
 
384
            // Cache this in a class variable as it's used rather a lot
385
 
386
            /**
387
            * Check for tooltips and alert the user that they're not supported with pyramid charts
388
            */
389
            if (   (prop['chart.variant'] == 'pyramid' || prop['chart.variant'] == 'dot')
390
                && typeof(prop['chart.tooltips']) == 'object'
391
                && prop['chart.tooltips']
392
                && prop['chart.tooltips'].length > 0) {
393
 
394
                alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts');
395
            }
396
 
397
            /**
398
            * Stop the coords arrays from growing uncontrollably
399
            */
400
            this.coords     = [];
401
            this.coords2    = [];
402
            this.coordsText = [];
403
 
404
            /**
405
            * Work out a few things. They need to be here because they depend on things you can change before you
406
            * call Draw() but after you instantiate the object
407
            */
408
            this.max            = 0;
409
            this.grapharea      = ca.height - this.gutterTop - this.gutterBottom;
410
            this.halfgrapharea  = this.grapharea / 2;
411
            this.halfTextHeight = prop['chart.text.size'] / 2;
412
 
413
 
414
            // Progressively Draw the chart
415
            RG.background.Draw(this);
416
 
417
 
418
 
419
 
420
            //If it's a sketch chart variant, draw the axes first
421
            if (prop['chart.variant'] == 'sketch') {
422
                this.DrawAxes();
423
                this.Drawbars();
424
            } else {
425
                this.Drawbars();
426
                this.DrawAxes();
427
            }
428
 
429
            this.DrawLabels();
430
 
431
 
432
            /**
433
            * Draw the bevel if required
434
            */
435
            if (prop['chart.bevel'] || prop['chart.bevelled']) {
436
                this.DrawBevel();
437
            }
438
 
439
 
440
            // Draw the key if necessary
441
            if (prop['chart.key'] && prop['chart.key'].length) {
442
                RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
443
            }
444
 
445
 
446
            /**
447
            * Setup the context menu if required
448
            */
449
            if (prop['chart.contextmenu']) {
450
                RG.ShowContext(this);
451
            }
452
 
453
 
454
 
455
 
456
            /**
457
            * Draw "in graph" labels
458
            */
459
            if (prop['chart.labels.ingraph']) {
460
                RG.DrawInGraphLabels(this);
461
            }
462
 
463
 
464
            /**
465
            * This function enables resizing
466
            */
467
            if (prop['chart.resizable']) {
468
                RG.AllowResizing(this);
469
            }
470
 
471
 
472
            /**
473
            * This installs the event listeners
474
            */
475
            RG.InstallEventListeners(this);
476
 
477
 
478
            /**
479
            * Fire the RGraph ondraw event
480
            */
481
            RG.FireCustomEvent(this, 'ondraw');
482
 
483
            return this;
484
        }
485
 
486
 
487
 
488
 
489
        /**
490
        * Draws the charts axes
491
        */
492
        this.DrawAxes = function ()
493
        {
494
            if (prop['chart.noaxes']) {
495
                return;
496
            }
497
 
498
            var xaxispos = prop['chart.xaxispos'];
499
            var yaxispos = prop['chart.yaxispos'];
500
            var isSketch = prop['chart.variant'] == 'sketch';
501
 
502
            co.beginPath();
503
            co.strokeStyle = prop['chart.axis.color'];
504
            co.lineWidth   = prop['chart.axis.linewidth'] + 0.001;
505
 
506
 
507
            if (ISSAFARI == -1) {
508
                co.lineCap = 'square';
509
            }
510
 
511
 
512
            // Draw the Y axis
513
            if (prop['chart.noyaxis'] == false) {
514
                if (yaxispos == 'right') {
515
                    co.moveTo(ca.width - this.gutterRight + (isSketch ? 3 : 0), this.gutterTop - (isSketch ? 3 : 0));
516
                    co.lineTo(ca.width - this.gutterRight - (isSketch ? 2 : 0), ca.height - this.gutterBottom + (isSketch ? 5 : 0));
517
                } else {
518
                    co.moveTo(this.gutterLeft - (isSketch ? 2 : 0), this.gutterTop - (isSketch ? 5 : 0));
519
                    co.lineTo(this.gutterLeft - (isSketch ? 1 : 0), ca.height - this.gutterBottom + (isSketch ? 5 : 0));
520
                }
521
            }
522
 
523
            // Draw the X axis
524
            if (prop['chart.noxaxis'] == false) {
525
                if (xaxispos == 'center') {
526
                    co.moveTo(this.gutterLeft - (isSketch ? 5 : 0), Math.round(((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + (isSketch ? 2 : 0)));
527
                    co.lineTo(ca.width - this.gutterRight + (isSketch ? 5 : 0), Math.round(((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - (isSketch ? 2 : 0)));
528
                } else if (xaxispos == 'top') {
529
                    co.moveTo(this.gutterLeft - (isSketch ? 3 : 0), this.gutterTop - (isSketch ? 3 : 0));
530
                    co.lineTo(ca.width - this.gutterRight + (isSketch ? 5 : 0), this.gutterTop + (isSketch ? 2 : 0));
531
                } else {
532
                    co.moveTo(this.gutterLeft - (isSketch ? 5 : 0), ca.height - this.gutterBottom - (isSketch ? 2 : 0));
533
                    co.lineTo(ca.width - this.gutterRight + (isSketch ? 8 : 0), ca.height - this.gutterBottom + (isSketch ? 2 : 0));
534
                }
535
            }
536
 
537
            var numYTicks = prop['chart.numyticks'];
538
 
539
            // Draw the Y tickmarks
540
            if (prop['chart.noyaxis'] == false && !isSketch) {
541
                var yTickGap = (ca.height - this.gutterTop - this.gutterBottom) / numYTicks;
542
                var xpos     = yaxispos == 'left' ? this.gutterLeft : ca.width - this.gutterRight;
543
 
544
                if (this.properties['chart.numyticks'] > 0) {
545
                    for (y=this.gutterTop;
546
                         xaxispos == 'center' ? y <= (ca.height - this.gutterBottom) : y < (ca.height - this.gutterBottom + (xaxispos == 'top' ? 1 : 0));
547
                         y += yTickGap) {
548
 
549
                        if (xaxispos == 'center' && y == (this.gutterTop + (this.grapharea / 2))) continue;
550
 
551
                        // X axis at the top
552
                        if (xaxispos == 'top' && y == this.gutterTop) continue;
553
 
554
                        co.moveTo(xpos + (yaxispos == 'left' ? 0 : 0), Math.round(y));
555
                        co.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(y));
556
                    }
557
                }
558
 
559
                /**
560
                * If the X axis is not being shown, draw an extra tick
561
                */
562
                if (prop['chart.noxaxis']) {
563
                    if (xaxispos == 'center') {
564
                        co.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(ca.height / 2));
565
                        co.lineTo(xpos, Math.round(ca.height / 2));
566
                    } else if (xaxispos == 'top') {
567
                        co.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(this.gutterTop));
568
                        co.lineTo(xpos, Math.round(this.gutterTop));
569
                    } else {
570
                        co.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), Math.round(ca.height - this.gutterBottom));
571
                        co.lineTo(xpos, Math.round(ca.height - this.gutterBottom));
572
                    }
573
                }
574
            }
575
 
576
 
577
            // Draw the X tickmarks
578
            if (prop['chart.noxaxis'] == false && !isSketch) {
579
 
580
                if (typeof(prop['chart.numxticks']) == 'number') {
581
                    var xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
582
                } else {
583
                    var xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / this.data.length;
584
                }
585
 
586
                if (xaxispos == 'bottom') {
587
                    yStart   = ca.height - this.gutterBottom;
588
                    yEnd     = (ca.height - this.gutterBottom) + 3;
589
                } else if (xaxispos == 'top') {
590
                    yStart = this.gutterTop - 3;
591
                    yEnd   = this.gutterTop;
592
                } else if (xaxispos == 'center') {
593
                    yStart = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + 3;
594
                    yEnd   = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - 3;
595
                }
596
 
597
                yStart = yStart;
598
                yEnd = yEnd;
599
 
600
                //////////////// X TICKS ////////////////
601
                var noEndXTick = prop['chart.noendxtick'];
602
 
603
                for (x=this.gutterLeft + (yaxispos == 'left' ? xTickGap : 0),len=(ca.width - this.gutterRight + (yaxispos == 'left' ? 5 : 0)); x<len; x+=xTickGap) {
604
 
605
                    if (yaxispos == 'left' && !noEndXTick && x > this.gutterLeft) {
606
                        co.moveTo(Math.round(x), yStart);
607
                        co.lineTo(Math.round(x), yEnd);
608
 
609
                    } else if (yaxispos == 'left' && noEndXTick && x > this.gutterLeft && x < (ca.width - this.gutterRight) ) {
610
                        co.moveTo(Math.round(x), yStart);
611
                        co.lineTo(Math.round(x), yEnd);
612
 
613
                    } else if (yaxispos == 'right' && x < (ca.width - this.gutterRight) && !noEndXTick) {
614
                        co.moveTo(Math.round(x), yStart);
615
                        co.lineTo(Math.round(x), yEnd);
616
 
617
                    } else if (yaxispos == 'right' && x < (ca.width - this.gutterRight) && x > (this.gutterLeft) && noEndXTick) {
618
                        co.moveTo(Math.round(x), yStart);
619
                        co.lineTo(Math.round(x), yEnd);
620
                    }
621
                }
622
 
623
                if (prop['chart.noyaxis'] || prop['chart.numxticks'] == null) {
624
                    if (typeof(prop['chart.numxticks']) == 'number' && prop['chart.numxticks'] > 0) {
625
                        co.moveTo(Math.round(this.gutterLeft), yStart);
626
                        co.lineTo(Math.round(this.gutterLeft), yEnd);
627
                    }
628
                }
629
 
630
                //////////////// X TICKS ////////////////
631
            }
632
 
633
            /**
634
            * If the Y axis is not being shown, draw an extra tick
635
            */
636
            if (prop['chart.noyaxis'] && prop['chart.noxaxis'] == false && prop['chart.numxticks'] == null) {
637
                if (xaxispos == 'center') {
638
                    co.moveTo(Math.round(this.gutterLeft), (ca.height / 2) - 3);
639
                    co.lineTo(Math.round(this.gutterLeft), (ca.height / 2) + 3);
640
                } else {
641
                    co.moveTo(Math.round(this.gutterLeft), ca.height - this.gutterBottom);
642
                    co.lineTo(Math.round(this.gutterLeft), ca.height - this.gutterBottom + 3);
643
                }
644
            }
645
 
646
            co.stroke();
647
        }
648
 
649
 
650
 
651
        /**
652
        * Draws the bars
653
        */
654
        this.Drawbars = function ()
655
        {
656
            // Variable "caching" so the context can be accessed as a local variable
657
            var ca   = this.canvas;
658
            var co   = this.context;
659
            var prop = this.properties;
660
 
661
            co.lineWidth   = prop['chart.linewidth'];
662
            co.strokeStyle = prop['chart.strokecolor'];
663
            co.fillStyle   = prop['chart.colors'][0];
664
            var prevX      = 0;
665
            var prevY      = 0;
666
            var decimals   = prop['chart.scale.decimals'];
667
 
668
            /**
669
            * Work out the max value
670
            */
671
            if (prop['chart.ymax']) {
672
 
673
                this.scale2 = RGraph.getScale2(this, {
674
                                                    'max':prop['chart.ymax'],
675
                                                    'strict': true,
676
                                                    'min':prop['chart.ymin'],
677
                                                    'scale.thousand':prop['chart.scale.thousand'],
678
                                                    'scale.point':prop['chart.scale.point'],
679
                                                    'scale.decimals':prop['chart.scale.decimals'],
680
                                                    'ylabels.count':prop['chart.ylabels.count'],
681
                                                    'scale.round':prop['chart.scale.round'],
682
                                                    'units.pre': prop['chart.units.pre'],
683
                                                    'units.post': prop['chart.units.post']
684
                                                   });
685
 
686
            } else {
687
 
688
                for (i=0; i<this.data.length; ++i) {
689
                    if (typeof(this.data[i]) == 'object') {
690
                        var value = prop['chart.grouping'] == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;
691
 
692
                    } else {
693
                        var value = Number(this.data[i]);
694
                    }
695
 
696
                    this.max = Math.max(Math.abs(this.max), Math.abs(value));
697
                }
698
 
699
                this.scale2 = RGraph.getScale2(this, {
700
                                                    'max':this.max,
701
                                                    'min':prop['chart.ymin'],
702
                                                    'scale.thousand':prop['chart.scale.thousand'],
703
                                                    'scale.point':prop['chart.scale.point'],
704
                                                    'scale.decimals':prop['chart.scale.decimals'],
705
                                                    'ylabels.count':prop['chart.ylabels.count'],
706
                                                    'scale.round':prop['chart.scale.round'],
707
                                                    'units.pre': prop['chart.units.pre'],
708
                                                    'units.post': prop['chart.units.post']
709
                                                   });
710
 
711
                this.max = this.scale2.max;
712
            }
713
            /**
714
            * if the chart is adjustable fix the scale so that it doesn't change.
715
            */
716
            if (prop['chart.adjustable'] && !prop['chart.ymax']) {
717
                this.Set('chart.ymax', this.scale2.max);
718
            }
719
 
720
            /**
721
            * Draw horizontal bars here
722
            */
723
            if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length > 0) {
724
                RGraph.DrawBars(this);
725
            }
726
 
727
            var variant = prop['chart.variant'];
728
 
729
            /**
730
            * Draw the 3D axes is necessary
731
            */
732
            if (variant == '3d') {
733
                RG.Draw3DAxes(this);
734
            }
735
 
736
            /**
737
            * Get the variant once, and draw the bars, be they regular, stacked or grouped
738
            */
739
 
740
            // Get these variables outside of the loop
741
            var xaxispos      = prop['chart.xaxispos'];
742
            var width         = (ca.width - this.gutterLeft - this.gutterRight ) / this.data.length;
743
            var orig_height   = height;
744
            var hmargin       = prop['chart.hmargin'];
745
            var shadow        = prop['chart.shadow'];
746
            var shadowColor   = prop['chart.shadow.color'];
747
            var shadowBlur    = prop['chart.shadow.blur'];
748
            var shadowOffsetX = prop['chart.shadow.offsetx'];
749
            var shadowOffsetY = prop['chart.shadow.offsety'];
750
            var strokeStyle   = prop['chart.strokecolor'];
751
            var colors        = prop['chart.colors'];
752
            var sequentialColorIndex = 0;
753
 
754
            for (i=0,len=this.data.length; i<len; i+=1) {
755
 
756
                // Work out the height
757
                //The width is up outside the loop
758
                var height = ((RGraph.array_sum(this.data[i]) < 0 ? RGraph.array_sum(this.data[i]) + this.scale2.min : RGraph.array_sum(this.data[i]) - this.scale2.min) / (this.scale2.max - this.scale2.min) ) * (ca.height - this.gutterTop - this.gutterBottom);
759
 
760
                // Half the height if the Y axis is at the center
761
                if (xaxispos == 'center') {
762
                    height /= 2;
763
                }
764
 
765
                var x = (i * width) + this.gutterLeft;
766
                var y = xaxispos == 'center' ? ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - height
767
                                             : ca.height - height - this.gutterBottom;
768
 
769
                // xaxispos is top
770
                if (xaxispos == 'top') {
771
                    y = this.gutterTop + Math.abs(height);
772
                }
773
 
774
 
775
                // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
776
                if (height < 0) {
777
                    y += height;
778
                    height = Math.abs(height);
779
                }
780
 
781
                /**
782
                * Turn on the shadow if need be
783
                */
784
                if (shadow) {
785
                    co.shadowColor   = shadowColor;
786
                    co.shadowBlur    = shadowBlur;
787
                    co.shadowOffsetX = shadowOffsetX;
788
                    co.shadowOffsetY = shadowOffsetY;
789
                }
790
 
791
                /**
792
                * Draw the bar
793
                */
794
                co.beginPath();
795
                    if (typeof(this.data[i]) == 'number') {
796
 
797
                        var barWidth = width - (2 * hmargin);
798
 
799
                        /**
800
                        * Check for a negative bar width
801
                        */
802
                        if (barWidth < 0) {
803
                            alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');
804
                        }
805
 
806
                        // Set the fill color
807
                        co.strokeStyle = strokeStyle;
808
                        co.fillStyle = colors[0];
809
 
810
                        /**
811
                        * Sequential colors
812
                        */
813
                        if (prop['chart.colors.sequential']) {
814
                            co.fillStyle = colors[i];
815
                        }
816
 
817
                        if (variant == 'sketch') {
818
 
819
                            co.lineCap = 'round';
820
 
821
                            var sketchOffset = 3;
822
 
823
                            co.beginPath();
824
 
825
                            co.strokeStyle = colors[0];
826
 
827
                            /**
828
                            * Sequential colors
829
                            */
830
                            if (prop['chart.colors.sequential']) {
831
                                co.strokeStyle = colors[i];
832
                            }
833
 
834
                            // Left side
835
                            co.moveTo(x + hmargin + 2, y + height - 2);
836
                            co.lineTo(x + hmargin -    1, y - 4);
837
 
838
                            // The top
839
                            co.moveTo(x + hmargin - 3, y + -2 + (this.data[i] < 0 ? height : 0));
840
                            co.bezierCurveTo(
841
                                             x + ((hmargin + width) * 0.33),
842
                                             y + 15 + (this.data[i] < 0 ? height - 10: 0),
843
                                             x + ((hmargin + width) * 0.66),
844
                                             y + 5 + (this.data[i] < 0 ? height - 10 : 0),x + hmargin + width + -1, y + 0 + (this.data[i] < 0 ? height : 0)
845
                                            );
846
 
847
 
848
                            // The right side
849
                            co.moveTo(x + hmargin + width - 5, y  - 5);
850
                            co.lineTo(x + hmargin + width - 3, y + height - 3);
851
 
852
                            if (prop['chart.variant.sketch.verticals']) {
853
                                for (var r=0.2; r<=0.8; r+=0.2) {
854
                                    co.moveTo(x + hmargin + width + (r > 0.4 ? -1 : 3) - (r * width),y - 1);
855
                                    co.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2));
856
                                }
857
                            }
858
 
859
                            co.stroke();
860
 
861
                        // Regular bar
862
                        } else if (variant == 'bar' || variant == '3d' || variant == 'glass' || variant == 'bevel') {
863
 
864
                            if (RGraph.isOld() && shadow) {
865
                                this.DrawIEShadow([x + hmargin, y, barWidth, height]);
866
                            }
867
 
868
                            if (variant == 'glass') {
869
                                RGraph.filledCurvyRect(co, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
870
                                RGraph.strokedCurvyRect(co, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
871
                            } else {
872
                                // On 9th April 2013 these two were swapped around so that the stroke happens SECOND so that any
873
                                // shadow that is cast by the fill does not overwrite the stroke
874
 
875
                                co.beginPath();
876
                                co.rect(x + hmargin, y, barWidth, height);
877
                                co.fill();
878
 
879
                                // Turn the shadow off so that the stroke doesn't cast any "extra" shadow
880
                                // that would show inside the bar
881
                                RG.NoShadow(this);
882
 
883
                                co.beginPath();
884
                                co.rect(x + hmargin, y, barWidth, height);
885
                                co.stroke();
886
                            }
887
 
888
                            // 3D effect
889
                            if (variant == '3d') {
890
 
891
                                var prevStrokeStyle = co.strokeStyle;
892
                                var prevFillStyle   = co.fillStyle;
893
 
894
                                // Draw the top
895
                                co.beginPath();
896
                                    co.moveTo(x + hmargin, y);
897
                                    co.lineTo(x + hmargin + 10, y - 5);
898
                                    co.lineTo(x + hmargin + 10 + barWidth, y - 5);
899
                                    co.lineTo(x + hmargin + barWidth, y);
900
                                co.closePath();
901
 
902
                                co.stroke();
903
                                co.fill();
904
 
905
                                // Draw the right hand side
906
                                co.beginPath();
907
                                    co.moveTo(x + hmargin + barWidth, y);
908
                                    co.lineTo(x + hmargin + barWidth + 10, y - 5);
909
                                    co.lineTo(x + hmargin + barWidth + 10, y + height - 5);
910
                                    co.lineTo(x + hmargin + barWidth, y + height);
911
                                co.closePath();
912
 
913
                                co.stroke();
914
                                co.fill();
915
 
916
                                // Draw the darker top section
917
                                co.beginPath();
918
                                    co.fillStyle = 'rgba(255,255,255,0.3)';
919
                                    co.moveTo(x + hmargin, y);
920
                                    co.lineTo(x + hmargin + 10, y - 5);
921
                                    co.lineTo(x + hmargin + 10 + barWidth, y - 5);
922
                                    co.lineTo(x + hmargin + barWidth, y);
923
                                    co.lineTo(x + hmargin, y);
924
                                co.closePath();
925
 
926
                                co.stroke();
927
                                co.fill();
928
 
929
                                // Draw the darker right side section
930
                                co.beginPath();
931
                                    co.fillStyle = 'rgba(0,0,0,0.4)';
932
                                    co.moveTo(x + hmargin + barWidth, y);
933
                                    co.lineTo(x + hmargin + barWidth + 10, y - 5);
934
                                    co.lineTo(x + hmargin + barWidth + 10, y - 5 + height);
935
                                    co.lineTo(x + hmargin + barWidth, y + height);
936
                                    co.lineTo(x + hmargin + barWidth, y);
937
                                co.closePath();
938
 
939
                                co.stroke();
940
                                co.fill();
941
 
942
                                co.strokeStyle = prevStrokeStyle;
943
                                co.fillStyle   = prevFillStyle;
944
 
945
                            // Glass variant
946
                            } else if (variant == 'glass') {
947
 
948
                                var grad = co.createLinearGradient(x + hmargin,y,x + hmargin + (barWidth / 2),y);
949
                                grad.addColorStop(0, 'rgba(255,255,255,0.9)');
950
                                grad.addColorStop(1, 'rgba(255,255,255,0.5)');
951
 
952
                                co.beginPath();
953
                                co.fillStyle = grad;
954
                                co.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2);
955
                                co.fill();
956
                            }
957
 
958
                            // This bit draws the text labels that appear above the bars if requested
959
                            if (prop['chart.labels.above']) {
960
 
961
                                // Turn off any shadow
962
                                if (shadow) {
963
                                    RGraph.NoShadow(this);
964
                                }
965
 
966
                                var yPos = y - 3;
967
                                var xPos = x + hmargin + (barWidth / 2);
968
 
969
                                // Account for negative bars
970
                                if (this.data[i] < 0) {
971
                                    yPos += height + 6 + (prop['chart.text.size']);
972
                                }
973
 
974
                                // Account for chart.xaxispos=top
975
                                if (prop['chart.xaxispos'] == 'top') {
976
                                    yPos = this.gutterTop + height + 6 + (typeof(prop['chart.labels.above.size']) == 'number' ? prop['chart.labels.above.size'] : prop['chart.text.size'] - 4);
977
                                }
978
 
979
                                // Account for chart.variant=3d
980
                                if (prop['chart.variant'] == '3d') {
981
                                    yPos -= 3;
982
                                    xPos += 5;
983
                                }
984
 
985
                                // Angled above labels
986
                                if (this.properties['chart.labels.above.angle']) {
987
                                    var angle = -45;
988
                                    var halign = 'left';
989
                                    var valign = 'center';
990
                                } else {
991
                                    var angle = 0;
992
                                    var halign = 'center';
993
                                    var valign = 'bottom';
994
                                }
995
 
996
                                // Above labels color
997
                                if (typeof this.properties['chart.labels.above.color'] == 'string') {
998
                                    co.fillStyle = prop['chart.labels.above.color'];
999
                                } else {
1000
                                    co.fillStyle = prop['chart.text.color'];
1001
                                }
1002
 
1003
                                RGraph.Text2(this, {'font': prop['chart.text.font'],
1004
                                                    'size': typeof(prop['chart.labels.above.size']) == 'number' ? prop['chart.labels.above.size'] : prop['chart.text.size'] - 3,
1005
                                                       'x': xPos,
1006
                                                       'y': yPos,
1007
                                                    'text': RGraph.number_format(this, Number(this.data[i]).toFixed(prop['chart.labels.above.decimals']),prop['chart.units.pre'],prop['chart.units.post']),
1008
                                                  'halign': halign,
1009
                                                  'marker': false,
1010
                                                  'valign': valign,
1011
                                                  'angle': angle,
1012
                                                  'tag': 'labels.above'
1013
                                                 });
1014
                            }
1015
 
1016
                        // Dot chart
1017
                        } else if (variant == 'dot') {
1018
 
1019
                            co.beginPath();
1020
                            co.moveTo(x + (width / 2), y);
1021
                            co.lineTo(x + (width / 2), y + height);
1022
                            co.stroke();
1023
 
1024
                            co.beginPath();
1025
                            co.fillStyle = this.properties['chart.colors'][i];
1026
                            co.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0);
1027
 
1028
                            // Set the colour for the dots
1029
                            co.fillStyle = prop['chart.colors'][0];
1030
 
1031
                            /**
1032
                            * Sequential colors
1033
                            */
1034
                            if (prop['chart.colors.sequential']) {
1035
                                co.fillStyle = colors[i];
1036
                            }
1037
 
1038
                            co.stroke();
1039
                            co.fill();
1040
 
1041
 
1042
 
1043
                        // Unknown variant type
1044
                        } else {
1045
                            alert('[BAR] Warning! Unknown chart.variant: ' + variant);
1046
                        }
1047
 
1048
                        this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
1049
 
1050
                            if (typeof this.coords2[i] == 'undefined') {
1051
                                this.coords2[i] = [];
1052
                            }
1053
                            this.coords2[i].push([x + hmargin, y, width - (2 * hmargin), height]);
1054
 
1055
 
1056
                    /**
1057
                    * Stacked bar
1058
                    */
1059
                    } else if (this.data[i] && typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'stacked') {
1060
 
1061
                        if (this.scale2.min) {
1062
                            alert("[ERROR] Stacked Bar charts with a Y min are not supported");
1063
                        }
1064
 
1065
                        var barWidth     = width - (2 * hmargin);
1066
                        var redrawCoords = [];// Necessary to draw if the shadow is enabled
1067
                        var startY       = 0;
1068
                        var dataset      = this.data[i];
1069
 
1070
                        /**
1071
                        * Check for a negative bar width
1072
                        */
1073
                        if (barWidth < 0) {
1074
                            alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');
1075
                        }
1076
 
1077
                        for (j=0; j<dataset.length; ++j) {
1078
 
1079
                            // Stacked bar chart and X axis pos in the middle - poitless since negative values are not permitted
1080
                            if (xaxispos == 'center') {
1081
                                alert("[BAR] It's pointless having the X axis position at the center on a stacked bar chart.");
1082
                                return;
1083
                            }
1084
 
1085
                            // Negative values not permitted for the stacked chart
1086
                            if (this.data[i][j] < 0) {
1087
                                alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');
1088
                                return;
1089
                            }
1090
 
1091
                            /**
1092
                            * Set the fill and stroke colors
1093
                            */
1094
                            co.strokeStyle = strokeStyle
1095
                            co.fillStyle = colors[j];
1096
 
1097
                            if (prop['chart.colors.reverse']) {
1098
                                co.fillStyle = colors[this.data[i].length - j - 1];
1099
                            }
1100
 
1101
                            if (prop['chart.colors.sequential'] && colors[sequentialColorIndex]) {
1102
                                co.fillStyle = colors[sequentialColorIndex++];
1103
                            } else if (prop['chart.colors.sequential']) {
1104
                                co.fillStyle = colors[sequentialColorIndex - 1];
1105
                            }
1106
 
1107
                            var height = (dataset[j] / this.scale2.max) * (ca.height - this.gutterTop - this.gutterBottom );
1108
 
1109
                            // If the X axis pos is in the center, we need to half the  height
1110
                            if (xaxispos == 'center') {
1111
                                height /= 2;
1112
                            }
1113
 
1114
                            var totalHeight = (RGraph.array_sum(dataset) / this.scale2.max) * (ca.height - hmargin - this.gutterTop - this.gutterBottom);
1115
 
1116
                            /**
1117
                            * Store the coords for tooltips
1118
                            */
1119
                            this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
1120
                            if (typeof this.coords2[i] == 'undefined') {
1121
                                this.coords2[i] = [];
1122
                            }
1123
                            this.coords2[i].push([x + hmargin, y, width - (2 * hmargin), height]);
1124
 
1125
                            // MSIE shadow
1126
                            if (RGraph.isOld() && shadow) {
1127
                                this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]);
1128
                            }
1129
 
1130
                            if (height > 0) {
1131
                                co.strokeRect(x + hmargin, y, width - (2 * hmargin), height);
1132
                                co.fillRect(x + hmargin, y, width - (2 * hmargin), height);
1133
                            }
1134
 
1135
 
1136
                            if (j == 0) {
1137
                                var startY = y;
1138
                                var startX = x;
1139
                            }
1140
 
1141
                            /**
1142
                            * Store the redraw coords if the shadow is enabled
1143
                            */
1144
                            if (shadow) {
1145
                                redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, co.fillStyle]);
1146
                            }
1147
 
1148
                            /**
1149
                            * Stacked 3D effect
1150
                            */
1151
                            if (variant == '3d') {
1152
 
1153
                                var prevFillStyle = co.fillStyle;
1154
                                var prevStrokeStyle = co.strokeStyle;
1155
 
1156
 
1157
                                // Draw the top side
1158
                                if (j == 0) {
1159
                                    co.beginPath();
1160
                                        co.moveTo(startX + hmargin, y);
1161
                                        co.lineTo(startX + 10 + hmargin, y - 5);
1162
                                        co.lineTo(startX + 10 + barWidth + hmargin, y - 5);
1163
                                        co.lineTo(startX + barWidth + hmargin, y);
1164
                                    co.closePath();
1165
 
1166
                                    co.fill();
1167
                                    co.stroke();
1168
                                }
1169
 
1170
                                // Draw the side section
1171
                                co.beginPath();
1172
                                    co.moveTo(startX + barWidth + hmargin, y);
1173
                                    co.lineTo(startX + barWidth + hmargin + 10, y - 5);
1174
                                    co.lineTo(startX + barWidth + hmargin + 10, y - 5 + height);
1175
                                    co.lineTo(startX + barWidth + hmargin , y + height);
1176
                                co.closePath();
1177
 
1178
                                co.fill();
1179
                                co.stroke();
1180
 
1181
                                // Draw the darker top side
1182
                                if (j == 0) {
1183
                                    co.fillStyle = 'rgba(255,255,255,0.3)';
1184
                                    co.beginPath();
1185
                                        co.moveTo(startX + hmargin, y);
1186
                                        co.lineTo(startX + 10 + hmargin, y - 5);
1187
                                        co.lineTo(startX + 10 + barWidth + hmargin, y - 5);
1188
                                        co.lineTo(startX + barWidth + hmargin, y);
1189
                                    co.closePath();
1190
 
1191
                                    co.fill();
1192
                                    co.stroke();
1193
                                }
1194
 
1195
                                // Draw the darker side section
1196
                                co.fillStyle = 'rgba(0,0,0,0.4)';
1197
                                co.beginPath();
1198
                                    co.moveTo(startX + barWidth + hmargin, y);
1199
                                    co.lineTo(startX + barWidth + hmargin + 10, y - 5);
1200
                                    co.lineTo(startX + barWidth + hmargin + 10, y - 5 + height);
1201
                                    co.lineTo(startX + barWidth + hmargin , y + height);
1202
                                co.closePath();
1203
 
1204
                                co.fill();
1205
                                co.stroke();
1206
 
1207
                                co.strokeStyle = prevStrokeStyle;
1208
                                co.fillStyle = prevFillStyle;
1209
                            }
1210
 
1211
                            y += height;
1212
                        }
1213
 
1214
                        // This bit draws the text labels that appear above the bars if requested
1215
                        if (prop['chart.labels.above']) {
1216
 
1217
                            // Turn off any shadow
1218
                            RG.NoShadow(this);
1219
 
1220
                            // Above labels color
1221
                            if (typeof this.properties['chart.labels.above.color'] == 'string') {
1222
                                co.fillStyle = prop['chart.labels.above.color'];
1223
                            } else {
1224
                                co.fillStyle = prop['chart.text.color'];
1225
                            }
1226
 
1227
                            // Angled above labels
1228
                            if (prop['chart.labels.above.angle']) {
1229
                                var angle = -45;
1230
                                var halign = 'left';
1231
                                var valign = 'center';
1232
                            } else {
1233
                                var angle = 0;
1234
                                var halign = 'center';
1235
                                var valign = 'bottom';
1236
                            }
1237
 
1238
                            RGraph.Text2(this,{'font': prop['chart.text.font'],
1239
                                               'size': typeof(prop['chart.labels.above.size']) == 'number' ? prop['chart.labels.above.size'] : prop['chart.text.size'] - 3,
1240
                                               'x': startX + (barWidth / 2) + prop['chart.hmargin'],
1241
                                               'y': startY - (prop['chart.shadow'] && prop['chart.shadow.offsety'] < 0 ? 7 : 4) - (prop['chart.variant'] == '3d' ? 5 : 0),
1242
                                               'text': String(prop['chart.units.pre'] + RGraph.array_sum(this.data[i]).toFixed(prop['chart.labels.above.decimals']) + prop['chart.units.post']),
1243
                                               'angle': angle,
1244
                                               'valign': valign,
1245
                                               'halign': halign,
1246
                                                'tag': 'labels.above'
1247
                                              });
1248
 
1249
 
1250
                            // Turn any shadow back on
1251
                            if (shadow) {
1252
                                co.shadowColor   = shadowColor;
1253
                                co.shadowBlur    = shadowBlur;
1254
                                co.shadowOffsetX = shadowOffsetX;
1255
                                co.shadowOffsetY = shadowOffsetY;
1256
                            }
1257
                        }
1258
 
1259
 
1260
                        /**
1261
                        * Redraw the bars if the shadow is enabled due to hem being drawn from the bottom up, and the
1262
                        * shadow spilling over to higher up bars
1263
                        */
1264
                        if (shadow) {
1265
 
1266
                            RGraph.NoShadow(this);
1267
 
1268
                            for (k=0; k<redrawCoords.length; ++k) {
1269
                                co.strokeStyle = strokeStyle;
1270
                                co.fillStyle = redrawCoords[k][4];
1271
                                co.strokeRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
1272
                                co.fillRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
1273
 
1274
                                co.stroke();
1275
                                co.fill();
1276
                            }
1277
 
1278
                            // Reset the redraw coords to be empty
1279
                            redrawCoords = [];
1280
                        }
1281
                    /**
1282
                    * Grouped bar
1283
                    */
1284
                    } else if (this.data[i] && typeof(this.data[i]) == 'object' && prop['chart.grouping'] == 'grouped') {
1285
 
1286
                        var redrawCoords = [];
1287
                        co.lineWidth = prop['chart.linewidth'];
1288
 
1289
                        for (j=0; j<this.data[i].length; ++j) {
1290
 
1291
                            // Set the fill and stroke colors
1292
                            co.strokeStyle = strokeStyle;
1293
                            co.fillStyle   = colors[j];
1294
 
1295
                            /**
1296
                            * Sequential colors
1297
                            */
1298
                            if (prop['chart.colors.sequential'] && colors[sequentialColorIndex]) {
1299
                                co.fillStyle = colors[sequentialColorIndex++];
1300
                            } else if (prop['chart.colors.sequential']) {
1301
                                co.fillStyle = colors[sequentialColorIndex - 1];
1302
                            }
1303
 
1304
                            var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length;
1305
                            var height = ((this.data[i][j] + (this.data[i][j] < 0 ? this.scale2.min : (-1 * this.scale2.min) )) / (this.scale2.max - this.scale2.min) ) * (ca.height - this.gutterTop - this.gutterBottom );
1306
                            var groupedMargin = prop['chart.hmargin.grouped'];
1307
                            var startX = x + hmargin + (j * individualBarWidth);
1308
 
1309
                            /**
1310
                            * Check for a negative bar width
1311
                            */
1312
                            if (individualBarWidth < 0) {
1313
                                alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.');
1314
                            }
1315
 
1316
                            // If the X axis pos is in the center, we need to half the  height
1317
                            if (xaxispos == 'center') {
1318
                                height /= 2;
1319
                            }
1320
 
1321
                            /**
1322
                            * Determine the start positioning for the bar
1323
                            */
1324
                            if (xaxispos == 'top') {
1325
                                var startY = this.gutterTop;
1326
                                var height = Math.abs(height);
1327
 
1328
                            } else if (xaxispos == 'center') {
1329
                                var startY = this.gutterTop + (this.grapharea / 2) - height;
1330
 
1331
                            } else {
1332
                                var startY = ca.height - this.gutterBottom - height;
1333
                                var height = Math.abs(height);
1334
                            }
1335
 
1336
                            /**
1337
                            * Draw MSIE shadow
1338
                            */
1339
                            if (RGraph.isOld() && shadow) {
1340
                                this.DrawIEShadow([startX, startY, individualBarWidth, height]);
1341
                            }
1342
 
1343
                            co.strokeRect(startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height);
1344
                            co.fillRect(startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height);
1345
                            y += height;
1346
 
1347
 
1348
 
1349
                            /**
1350
                            * Grouped 3D effect
1351
                            */
1352
                            if (variant == '3d') {
1353
                                var prevFillStyle = co.fillStyle;
1354
                                var prevStrokeStyle = co.strokeStyle;
1355
 
1356
                                // Draw the top side
1357
                                co.beginPath();
1358
                                    co.moveTo(startX, startY);
1359
                                    co.lineTo(startX + 10, startY - 5);
1360
                                    co.lineTo(startX + 10 + individualBarWidth, startY - 5);
1361
                                    co.lineTo(startX + individualBarWidth, startY);
1362
                                co.closePath();
1363
 
1364
                                co.fill();
1365
                                co.stroke();
1366
 
1367
                                // Draw the side section
1368
                                co.beginPath();
1369
                                    co.moveTo(startX + individualBarWidth, startY);
1370
                                    co.lineTo(startX + individualBarWidth + 10, startY - 5);
1371
                                    co.lineTo(startX + individualBarWidth + 10, startY - 5 + height);
1372
                                    co.lineTo(startX + individualBarWidth , startY + height);
1373
                                co.closePath();
1374
 
1375
                                co.fill();
1376
                                co.stroke();
1377
 
1378
 
1379
                                // Draw the darker top side
1380
                                co.fillStyle = 'rgba(255,255,255,0.3)';
1381
                                co.beginPath();
1382
                                    co.moveTo(startX, startY);
1383
                                    co.lineTo(startX + 10, startY - 5);
1384
                                    co.lineTo(startX + 10 + individualBarWidth, startY - 5);
1385
                                    co.lineTo(startX + individualBarWidth, startY);
1386
                                co.closePath();
1387
 
1388
                                co.fill();
1389
                                co.stroke();
1390
 
1391
                                // Draw the darker side section
1392
                                co.fillStyle = 'rgba(0,0,0,0.4)';
1393
                                co.beginPath();
1394
                                    co.moveTo(startX + individualBarWidth, startY);
1395
                                    co.lineTo(startX + individualBarWidth + 10, startY - 5);
1396
                                    co.lineTo(startX + individualBarWidth + 10, startY - 5 + height);
1397
                                    co.lineTo(startX + individualBarWidth , startY + height);
1398
                                co.closePath();
1399
 
1400
                                co.fill();
1401
                                co.stroke();
1402
 
1403
                                co.strokeStyle = prevStrokeStyle;
1404
                                co.fillStyle   = prevFillStyle;
1405
                            }
1406
 
1407
                            if (height < 0) {
1408
                                height = Math.abs(height);
1409
                                startY = startY - height;
1410
                            }
1411
 
1412
                            this.coords.push([startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height]);
1413
                            if (typeof this.coords2[i] == 'undefined') {
1414
                                this.coords2[i] = [];
1415
                            }
1416
 
1417
                            this.coords2[i].push([startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height]);
1418
 
1419
                            // Facilitate shadows going to the left
1420
                            if (prop['chart.shadow']) {
1421
                                redrawCoords.push([startX + groupedMargin, startY, individualBarWidth - (2 * groupedMargin), height, co.fillStyle]);
1422
                            }
1423
 
1424
 
1425
                            // This bit draws the text labels that appear above the bars if requested
1426
                            if (prop['chart.labels.above']) {
1427
 
1428
                                co.strokeStyle = 'rgba(0,0,0,0)';
1429
 
1430
                                // Turn off any shadow
1431
                                if (shadow) {
1432
                                    RGraph.NoShadow(this);
1433
                                }
1434
 
1435
                                var yPos = y - 3;
1436
 
1437
                                // Angled above labels
1438
                                if (prop['chart.labels.above.angle']) {
1439
                                    var angle = -45;
1440
                                    var halign = 'left';
1441
                                    var valign = 'center';
1442
                                } else {
1443
 
1444
                                    var angle = 0;
1445
                                    var halign = 'center';
1446
                                    var valign = 'bottom';
1447
 
1448
                                    // Account for negative bars
1449
                                    if (this.data[i][j] < 0 || prop['chart.xaxispos'] == 'top') {
1450
                                        yPos = startY + height + 6;
1451
                                        var valign = 'top';
1452
                                    } else {
1453
                                        yPos = startY;
1454
                                    }
1455
                                }
1456
 
1457
                                // Above labels color
1458
                                if (typeof this.properties['chart.labels.above.color'] == 'string') {
1459
                                    co.fillStyle = prop['chart.labels.above.color'];
1460
                                } else {
1461
                                    co.fillStyle = prop['chart.text.color'];
1462
                                }
1463
 
1464
                                RGraph.Text2(this, {'font': prop['chart.text.font'],
1465
                                                    'size': typeof(prop['chart.labels.above.size']) == 'number' ? prop['chart.labels.above.size'] : prop['chart.text.size'] - 3,
1466
                                                    'x': startX + (individualBarWidth / 2),
1467
                                                    'y': yPos - 3,
1468
                                                    'text': RGraph.number_format(this, this.data[i][j].toFixed(prop['chart.labels.above.decimals'])),
1469
                                                    'halign': halign,
1470
                                                    'valign': valign,
1471
                                                    'angle':angle,
1472
                                                    'tag': 'labels.above'
1473
                                                   });
1474
 
1475
                                // Turn any shadow back on
1476
                                if (shadow) {
1477
                                    co.shadowColor   = shadowColor;
1478
                                    co.shadowBlur    = shadowBlur;
1479
                                    co.shadowOffsetX = shadowOffsetX;
1480
                                    co.shadowOffsetY = shadowOffsetY;
1481
                                }
1482
                            }
1483
                        }
1484
 
1485
                        /**
1486
                        * Redraw the bar if shadows are going to the left
1487
                        */
1488
                        if (redrawCoords.length) {
1489
 
1490
                            RGraph.NoShadow(this);
1491
 
1492
                            co.lineWidth = prop['chart.linewidth'];
1493
 
1494
                            co.beginPath();
1495
                                for (var j=0; j<redrawCoords.length; ++j) {
1496
 
1497
                                    co.fillStyle   = redrawCoords[j][4];
1498
                                    co.strokeStyle = prop['chart.strokecolor'];
1499
 
1500
                                    co.fillRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
1501
                                    co.strokeRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
1502
                                }
1503
                            co.fill();
1504
                            co.stroke();
1505
 
1506
                            redrawCoords = [];
1507
                        }
1508
                    } else {
1509
                        this.coords.push([]);
1510
                    }
1511
 
1512
                co.closePath();
1513
            }
1514
 
1515
            /**
1516
            * Turn off any shadow
1517
            */
1518
            RGraph.NoShadow(this);
1519
        }
1520
 
1521
 
1522
 
1523
        /**
1524
        * Draws the labels for the graph
1525
        */
1526
        this.DrawLabels = function ()
1527
        {
1528
            // Variable "caching" so the context can be accessed as a local variable
1529
            var ca      = this.canvas;
1530
            var co      = this.context;
1531
            var prop    = this.properties;
1532
            var context = co;
1533
 
1534
            var text_angle = prop['chart.text.angle'];
1535
            var text_size  = prop['chart.text.size'];
1536
            var labels     = prop['chart.labels'];
1537
 
1538
 
1539
            // Draw the Y axis labels:
1540
            if (prop['chart.ylabels']) {
1541
                if (prop['chart.xaxispos'] == 'top')    this.Drawlabels_top();
1542
                if (prop['chart.xaxispos'] == 'center') this.Drawlabels_center();
1543
                if (prop['chart.xaxispos'] == 'bottom') this.Drawlabels_bottom();
1544
            }
1545
 
1546
            /**
1547
            * The X axis labels
1548
            */
1549
            if (typeof(labels) == 'object' && labels) {
1550
 
1551
                var yOffset = Number(prop['chart.xlabels.offset']);
1552
 
1553
                /**
1554
                * Text angle
1555
                */
1556
                if (prop['chart.text.angle'] != 0) {
1557
                    var valign =  'center';
1558
                    var halign =  'right';
1559
                    var angle  = 0 - prop['chart.text.angle'];
1560
                } else {
1561
                    var valign =  'top';
1562
                    var halign =  'center';
1563
                    var angle  = 0;
1564
                }
1565
 
1566
                // Draw the X axis labels
1567
                co.fillStyle = prop['chart.text.color'];
1568
 
1569
                // How wide is each bar
1570
                var barWidth = (ca.width - this.gutterRight - this.gutterLeft) / labels.length;
1571
 
1572
                // Reset the xTickGap
1573
                xTickGap = (ca.width - this.gutterRight - this.gutterLeft) / labels.length
1574
 
1575
                // Draw the X tickmarks
1576
                var i=0;
1577
                var font = prop['chart.text.font'];
1578
 
1579
                for (x=this.gutterLeft + (xTickGap / 2); x<=ca.width - this.gutterRight; x+=xTickGap) {
1580
 
1581
                    RGraph.Text2(this, {'font': font,
1582
                                        'size': text_size,
1583
                                           'x': x,
1584
                                           'y': prop['chart.xaxispos'] == 'top' ? this.gutterTop - yOffset - 5: (ca.height - this.gutterBottom) + yOffset + 3,
1585
                                        'text': String(labels[i++]),
1586
                                      'valign': prop['chart.xaxispos'] == 'top' ? 'bottom' : valign,
1587
                                      'halign': halign,
1588
                                        'tag':'label',
1589
                                        'marker':false,
1590
                                        'angle':angle,
1591
                                        'tag': 'labels'
1592
                                       });
1593
                }
1594
            }
1595
 
1596
            /**
1597
            * If chart.labels.above.specific is specified, draw them
1598
            */
1599
            if (prop['chart.labels.above.specific']) {
1600
 
1601
                var labels = prop['chart.labels.above.specific'];
1602
 
1603
                for (var i=0; i<this.coords.length; ++i) {
1604
 
1605
                    var xaxispos = prop['chart.xaxispos'];
1606
                    var coords = this.coords[i];
1607
                    var value  = this.data_arr[i];
1608
                    var valign =  (value >=0 && xaxispos != 'top') ? 'bottom' : 'top';
1609
                    var halign = 'center';
1610
                    var text   = labels[i];
1611
 
1612
 
1613
                    if (text && text.toString().length > 0) {
1614
                        RGraph.Text2(this, {'font': prop['chart.text.font'],
1615
                                            'size': prop['chart.labels.above.size'] ? prop['chart.labels.above.size'] : prop['chart.text.size'],
1616
                                            'x': coords[0] + (coords[2] / 2),
1617
                                            'y': (value >=0 && xaxispos != 'top') ? coords[1] - 5 : coords[1] + coords[3] + 3,
1618
                                            'text': String(labels[i]),
1619
                                            'valign': valign,
1620
                                            'halign': halign,
1621
                                            'tag': 'labels.above'
1622
                                           });
1623
                    }
1624
                }
1625
            }
1626
        }
1627
 
1628
 
1629
 
1630
        /**
1631
        * Draws the X axis at the top
1632
        */
1633
        this.Drawlabels_top = function ()
1634
        {
1635
            var ca   = this.canvas;
1636
            var co   = this.context;
1637
            var prop = this.properties;
1638
 
1639
            co.beginPath();
1640
            co.fillStyle   = prop['chart.text.color'];
1641
            co.strokeStyle = 'black';
1642
 
1643
            if (prop['chart.xaxispos'] == 'top') {
1644
 
1645
                var context    = co;
1646
                var text_size  = prop['chart.text.size'];
1647
                var units_pre  = prop['chart.units.pre'];
1648
                var units_post = prop['chart.units.post'];
1649
                var align      = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1650
                var font       = prop['chart.text.font'];
1651
                var numYLabels = prop['chart.ylabels.count'];
1652
                var ymin       = prop['chart.ymin'];
1653
 
1654
                if (prop['chart.ylabels.inside'] == true) {
1655
                    var xpos  = prop['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : ca.width - this.gutterRight - 5;
1656
                    var align = prop['chart.yaxispos'] == 'left' ? 'left' : 'right';
1657
                    var boxed = true;
1658
                } else {
1659
                    var xpos  = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1660
                    var boxed = false;
1661
                }
1662
 
1663
                /**
1664
                * Draw specific Y labels here so that the local variables can be reused
1665
                */
1666
                if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific']) {
1667
 
1668
                    var labels = RGraph.array_reverse(prop['chart.ylabels.specific']);
1669
                    var grapharea = ca.height - this.gutterTop - this.gutterBottom;
1670
 
1671
                    for (var i=0; i<labels.length; ++i) {
1672
 
1673
                        var y = this.gutterTop + (grapharea * (i / labels.length)) + (grapharea / labels.length);
1674
 
1675
                        RGraph.Text2(this, {'font': font,
1676
                                            'size': text_size,
1677
                                            'x': xpos,
1678
                                            'y': y,
1679
                                            'text': String(labels[i]),
1680
                                            'valign': 'center',
1681
                                            'halign': align,
1682
                                            'bordered':boxed,
1683
                                            'tag': 'scale'
1684
                                           });
1685
                    }
1686
 
1687
                    return;
1688
                }
1689
 
1690
 
1691
 
1692
 
1693
 
1694
 
1695
 
1696
                /**
1697
                * Draw the scale
1698
                */
1699
                var labels = this.scale2.labels;
1700
                for (var i=0; i<labels.length; ++i) {
1701
                    RGraph.Text2(this, {'font': font,
1702
                                        'size':text_size,
1703
                                        'x':xpos,
1704
                                        'y':this.gutterTop + ((this.grapharea / labels.length) * (i + 1)),
1705
                                        'text': '-' + labels[i],
1706
                                        'valign': 'center',
1707
                                        'halign': align,
1708
                                        'bordered': boxed,
1709
                                        'tag':'scale'});
1710
                }
1711
 
1712
 
1713
 
1714
 
1715
 
1716
 
1717
 
1718
 
1719
                /**
1720
                * Show the minimum value if its not zero
1721
                */
1722
                if (prop['chart.ymin'] != 0 || prop['chart.noxaxis'] || prop['chart.scale.zerostart']) {
1723
 
1724
                    RGraph.Text2(this, {'font': font,
1725
                                        'size': text_size,
1726
                                           'x': xpos,
1727
                                           'y': this.gutterTop,
1728
                                        'text': (this.scale2.min != 0 ? '-' : '') + RGraph.number_format(this,(this.scale2.min.toFixed((prop['chart.scale.decimals']))), units_pre, units_post),
1729
                                      'valign': 'center',
1730
                                      'halign': align,
1731
                                    'bordered': boxed,
1732
                                        'tag': 'scale'});
1733
                }
1734
 
1735
            }
1736
 
1737
            co.fill();
1738
        }
1739
 
1740
 
1741
 
1742
        /**
1743
        * Draws the X axis in the middle
1744
        */
1745
        this.Drawlabels_center = function ()
1746
        {
1747
            var ca   = this.canvas;
1748
            var co   = this.context;
1749
            var prop = this.properties;
1750
 
1751
            var font       = prop['chart.text.font'];
1752
            var numYLabels = prop['chart.ylabels.count'];
1753
 
1754
            co.fillStyle = prop['chart.text.color'];
1755
 
1756
            if (prop['chart.xaxispos'] == 'center') {
1757
 
1758
                /**
1759
                * Draw the top labels
1760
                */
1761
                var text_size  = prop['chart.text.size'];
1762
                var units_pre  = prop['chart.units.pre'];
1763
                var units_post = prop['chart.units.post'];
1764
                var context = co;
1765
                var align   = '';
1766
                var xpos    = 0;
1767
                var boxed   = false;
1768
                var ymin    = prop['chart.ymin'];
1769
 
1770
                co.fillStyle   = prop['chart.text.color'];
1771
                co.strokeStyle = 'black';
1772
 
1773
                if (prop['chart.ylabels.inside'] == true) {
1774
                    var xpos  = prop['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : ca.width - this.gutterRight - 5;
1775
                    var align = prop['chart.yaxispos'] == 'left' ? 'left' : 'right';
1776
                    var boxed = true;
1777
                } else {
1778
                    var xpos  = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1779
                    var align = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1780
                    var boxed = false;
1781
                }
1782
 
1783
 
1784
 
1785
 
1786
 
1787
 
1788
 
1789
 
1790
 
1791
 
1792
 
1793
 
1794
                /**
1795
                * Draw specific Y labels here so that the local variables can be reused
1796
                */
1797
                if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific']) {
1798
 
1799
                    var labels    = prop['chart.ylabels.specific'];
1800
                    var grapharea = ca.height - this.gutterTop - this.gutterBottom;
1801
 
1802
                    // Draw the top halves labels
1803
                    for (var i=0; i<labels.length; ++i) {
1804
 
1805
                        var y = this.gutterTop + ((grapharea / 2) / (labels.length - 1)) * i;
1806
 
1807
                        RGraph.Text2(this, {'font':font,
1808
                                            'size':text_size,
1809
                                            'x':xpos,
1810
                                            'y':y,
1811
                                            'text':String(labels[i]),
1812
                                            'valign':'center',
1813
                                            'halign':align,
1814
                                            'bordered':boxed,
1815
                                            'tag': 'scale'
1816
                                           });
1817
                    }
1818
 
1819
                    // Draw the bottom halves labels
1820
                    for (var i=labels.length-1; i>=1; --i) {
1821
 
1822
                        var y = this.gutterTop  + (grapharea * (i / ((labels.length - 1) * 2) )) + (grapharea / 2);
1823
 
1824
                        RG.Text2(this, {'font':font,
1825
                                            'size':text_size,
1826
                                            'x':xpos,
1827
                                            'y':y,
1828
                                            'text':String(labels[labels.length - i - 1]),
1829
                                            'valign':'center',
1830
                                            'halign':align,
1831
                                            'bordered':boxed,
1832
                                            'tag': 'scale'
1833
                                           });
1834
                    }
1835
 
1836
                    return;
1837
                }
1838
 
1839
 
1840
 
1841
 
1842
 
1843
 
1844
 
1845
 
1846
 
1847
 
1848
                /**
1849
                * Draw the top halfs labels
1850
                */
1851
                for (var i=0; i<this.scale2.labels.length; ++i) {
1852
                    var y    = this.gutterTop + this.halfgrapharea - ((this.halfgrapharea / numYLabels) * (i + 1));
1853
                    var text = this.scale2.labels[i];
1854
                    RG.Text2(this, {'font':font, 'size':text_size, 'x':xpos, 'y':y, 'text': text, 'valign':'center', 'halign': align, 'bordered': boxed, 'tag':'scale'});
1855
                }
1856
 
1857
                /**
1858
                * Draw the bottom halfs labels
1859
                */
1860
                for (var i=(this.scale2.labels.length - 1); i>=0; --i) {
1861
                    var y = this.gutterTop + ((this.halfgrapharea / numYLabels) * (i + 1)) + this.halfgrapharea;
1862
                    var text = this.scale2.labels[i];
1863
                    RG.Text2(this, {'font':font, 'size':text_size,'x':xpos,'y':y,'text': '-' + text,'valign':'center','halign': align,'bordered': boxed,'tag':'scale'});
1864
                }
1865
 
1866
 
1867
 
1868
 
1869
 
1870
                /**
1871
                * Show the minimum value if its not zero
1872
                */
1873
                if (this.scale2.min != 0 || prop['chart.scale.zerostart']) {
1874
                    RG.Text2(this, {'font':font,'size':text_size, 'x':xpos, 'y':this.gutterTop + this.halfgrapharea,'text': RGraph.number_format(this,(this.scale2.min.toFixed((prop['chart.scale.decimals']))), units_pre, units_post),'valign':'center', 'valign':'center','halign': align, 'bordered': boxed, 'tag':'scale'});
1875
                }
1876
            }
1877
        }
1878
 
1879
 
1880
 
1881
 
1882
        /**
1883
        * Draws the X axdis at the bottom (the default)
1884
        */
1885
        this.Drawlabels_bottom = function ()
1886
        {
1887
            var co   = this.context;
1888
            var ca   = this.canvas;
1889
            var prop = this.properties;
1890
 
1891
            var text_size  = prop['chart.text.size'];
1892
            var units_pre  = prop['chart.units.pre'];
1893
            var units_post = prop['chart.units.post'];
1894
            var context    = this.context;
1895
            var align      = prop['chart.yaxispos'] == 'left' ? 'right' : 'left';
1896
            var font       = prop['chart.text.font'];
1897
            var numYLabels = prop['chart.ylabels.count'];
1898
            var ymin       = prop['chart.ymin'];
1899
 
1900
            co.beginPath();
1901
            co.fillStyle = prop['chart.text.color'];
1902
            co.strokeStyle = 'black';
1903
 
1904
            if (prop['chart.ylabels.inside'] == true) {
1905
                var xpos  = prop['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : ca.width - this.gutterRight - 5;
1906
                var align = prop['chart.yaxispos'] == 'left' ? 'left' : 'right';
1907
                var boxed = true;
1908
            } else {
1909
                var xpos  = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
1910
                var boxed = false;
1911
            }
1912
 
1913
            /**
1914
            * Draw specific Y labels here so that the local variables can be reused
1915
            */
1916
            if (prop['chart.ylabels.specific'] && typeof(prop['chart.ylabels.specific']) == 'object') {
1917
 
1918
                var labels = prop['chart.ylabels.specific'];
1919
                var grapharea = ca.height - this.gutterTop - this.gutterBottom;
1920
 
1921
                for (var i=0; i<labels.length; ++i) {
1922
                    var y = this.gutterTop + (grapharea * (i / (labels.length - 1)));
1923
 
1924
                    RGraph.Text2(this, {'font':font,
1925
                                        'size':text_size,
1926
                                        'x':xpos,
1927
                                        'y':y,
1928
                                        'text': labels[i],
1929
                                        'valign':'center',
1930
                                        'halign': align,
1931
                                        'bordered': boxed,
1932
                                        'tag':'scale'
1933
                                       });
1934
                }
1935
 
1936
                return;
1937
            }
1938
 
1939
            var gutterTop      = this.gutterTop;
1940
            var halfTextHeight = this.halfTextHeight;
1941
            var scale          = this.scale;
1942
 
1943
 
1944
            for (var i=0; i<numYLabels; ++i) {
1945
                var text = this.scale2.labels[i];
1946
                RGraph.Text2(this, {'font':font,
1947
                                    'size':text_size,
1948
                                    'x':xpos,
1949
                                    'y':this.gutterTop + this.grapharea - ((this.grapharea / numYLabels) * (i+1)),
1950
                                    'text': text,
1951
                                    'valign':'center',
1952
                                    'halign': align,
1953
                                    'bordered': boxed,
1954
                                    'tag':'scale'});
1955
            }
1956
 
1957
 
1958
            /**
1959
            * Show the minimum value if its not zero
1960
            */
1961
            if (prop['chart.ymin'] != 0 || prop['chart.noxaxis'] || prop['chart.scale.zerostart']) {
1962
                RG.Text2(this, {'font':font,
1963
                                'size':text_size,
1964
                                'x':xpos,
1965
                                'y':ca.height - this.gutterBottom,
1966
                                'text': RG.number_format(this,(this.scale2.min.toFixed((prop['chart.scale.decimals']))), units_pre, units_post),
1967
                                'valign':'center',
1968
                                'halign': align,
1969
                                'bordered': boxed,
1970
                                'tag':'scale'});
1971
            }
1972
 
1973
            co.fill();
1974
        }
1975
 
1976
 
1977
        /**
1978
        * This function is used by MSIE only to manually draw the shadow
1979
        *
1980
        * @param array coords The coords for the bar
1981
        */
1982
        this.DrawIEShadow = function (coords)
1983
        {
1984
            var co   = this.context;
1985
            var ca   = this.canvas;
1986
            var prop = this.properties;
1987
 
1988
            var prevFillStyle = co.fillStyle;
1989
            var offsetx       = prop['chart.shadow.offsetx'];
1990
            var offsety       = prop['chart.shadow.offsety'];
1991
 
1992
            co.lineWidth = prop['chart.linewidth'];
1993
            co.fillStyle = prop['chart.shadow.color'];
1994
            co.beginPath();
1995
 
1996
            // Draw shadow here
1997
            co.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]);
1998
 
1999
            co.fill();
2000
 
2001
            // Change the fillstyle back to what it was
2002
            co.fillStyle = prevFillStyle;
2003
        }
2004
 
2005
 
2006
        /**
2007
        * Not used by the class during creating the graph, but is used by event handlers
2008
        * to get the coordinates (if any) of the selected bar
2009
        *
2010
        * @param object e The event object
2011
        * @param object   OPTIONAL You can pass in the bar object instead of the
2012
        *                          function using "this"
2013
        */
2014
        this.getShape =
2015
        this.getBar = function (e)
2016
        {
2017
            // This facilitates you being able to pass in the bar object as a parameter instead of
2018
            // the function getting it from itself
2019
            var obj = arguments[1] ? arguments[1] : this;
2020
 
2021
            var mouseXY = RGraph.getMouseXY(e);
2022
            var mouseX  = mouseXY[0];
2023
            var mouseY  = mouseXY[1];
2024
            var canvas  = obj.canvas;
2025
            var context = obj.context;
2026
            var coords  = obj.coords
2027
 
2028
            for (var i=0,len=coords.length; i<len; i+=1) {
2029
 
2030
                if (obj.coords[i].length == 0) {
2031
                    continue;
2032
                }
2033
 
2034
                var left   = coords[i][0];
2035
                var top    = coords[i][1];
2036
                var width  = coords[i][2];
2037
                var height = coords[i][3];
2038
                var prop   = obj.properties;
2039
 
2040
                if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height)) {
2041
 
2042
 
2043
                    if (prop['chart.tooltips']) {
2044
                        var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(prop['chart.tooltips'], i) : prop['chart.tooltips'][i];
2045
                    }
2046
 
2047
                    // Work out the dataset
2048
                    var dataset = 0;
2049
                    var idx = i;
2050
 
2051
                    while (idx >=  (typeof(obj.data[dataset]) == 'object' && obj.data[dataset] ? obj.data[dataset].length : 1)) {
2052
 
2053
                        if (typeof(obj.data[dataset]) == 'number') {
2054
                            idx -= 1;
2055
                        } else if (obj.data[dataset]) { // Accounts for null being an object
2056
                            idx -= obj.data[dataset].length;
2057
                        } else {
2058
                            idx -= 1;
2059
                        }
2060
 
2061
                        dataset++;
2062
                    }
2063
 
2064
                    if (typeof(obj.data[dataset]) == 'number') {
2065
                        idx = null;
2066
                    }
2067
 
2068
 
2069
                    return {
2070
                            0: obj, 1: left, 2: top, 3: width, 4: height, 5: i,
2071
                            'object': obj, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip, 'index_adjusted': idx, 'dataset': dataset
2072
                           };
2073
                }
2074
            }
2075
 
2076
            return null;
2077
        }
2078
 
2079
 
2080
 
2081
 
2082
        /**
2083
        * This retrives the bar based on the X coordinate only.
2084
        *
2085
        * @param object e The event object
2086
        * @param object   OPTIONAL You can pass in the bar object instead of the
2087
        *                          function using "this"
2088
        */
2089
        this.getShapeByX = function (e)
2090
        {
2091
            var canvas      = e.target;
2092
            var mouseCoords = RGraph.getMouseXY(e);
2093
 
2094
 
2095
            // This facilitates you being able to pass in the bar object as a parameter instead of
2096
            // the function getting it from itself
2097
            var obj = arguments[1] ? arguments[1] : this;
2098
 
2099
 
2100
            /**
2101
            * Loop through the bars determining if the mouse is over a bar
2102
            */
2103
            for (var i=0,len=obj.coords.length; i<len; i++) {
2104
 
2105
                if (obj.coords[i].length == 0) {
2106
                    continue;
2107
                }
2108
 
2109
                var mouseX = mouseCoords[0];
2110
                var mouseY = mouseCoords[1];
2111
                var left   = obj.coords[i][0];
2112
                var top    = obj.coords[i][1];
2113
                var width  = obj.coords[i][2];
2114
                var height = obj.coords[i][3];
2115
                var prop   = obj.properties;
2116
 
2117
                if (mouseX >= left && mouseX <= (left + width)) {
2118
 
2119
                    if (prop['chart.tooltips']) {
2120
                        var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(prop['chart.tooltips'], i) : prop['chart.tooltips'][i];
2121
                    }
2122
 
2123
 
2124
 
2125
                    return {
2126
                            0: obj, 1: left, 2: top, 3: width, 4: height, 5: i,
2127
                            'object': obj, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip
2128
                           };
2129
                }
2130
            }
2131
 
2132
            return null;
2133
        }
2134
 
2135
 
2136
        /**
2137
        * When you click on the chart, this method can return the Y value at that point. It works for any point on the
2138
        * chart (that is inside the gutters) - not just points within the Bars.
2139
        *
2140
        * EITHER:
2141
        *
2142
        * @param object arg The event object
2143
        *
2144
        * OR:
2145
        *
2146
        * @param object arg A two element array containing the X and Y coordinates
2147
        */
2148
        this.getValue = function (arg)
2149
        {
2150
            var co   = this.context;
2151
            var ca   = this.canvas;
2152
            var prop = this.properties;
2153
 
2154
            if (arg.length == 2) {
2155
                var mouseX = arg[0];
2156
                var mouseY = arg[1];
2157
            } else {
2158
                var mouseCoords = RGraph.getMouseXY(arg);
2159
                var mouseX      = mouseCoords[0];
2160
                var mouseY      = mouseCoords[1];
2161
            }
2162
 
2163
            if (   mouseY < prop['chart.gutter.top']
2164
                || mouseY > (ca.height - prop['chart.gutter.bottom'])
2165
                || mouseX < prop['chart.gutter.left']
2166
                || mouseX > (ca.width - prop['chart.gutter.right'])
2167
               ) {
2168
                return null;
2169
            }
2170
 
2171
            if (prop['chart.xaxispos'] == 'center') {
2172
                var value = (((this.grapharea / 2) - (mouseY - prop['chart.gutter.top'])) / this.grapharea) * (this.scale2.max - this.scale2.min)
2173
                value *= 2;
2174
 
2175
                if (value >= 0) {
2176
                    value += this.scale2.min;
2177
                } else {
2178
                    value -= this.scale2.min;
2179
                }
2180
 
2181
            } else if (prop['chart.xaxispos'] == 'top') {
2182
                var value = ((this.grapharea - (mouseY - prop['chart.gutter.top'])) / this.grapharea) * (this.scale2.max - this.scale2.min)
2183
                value = this.scale2.max - value;
2184
                value = Math.abs(value) * -1;
2185
            } else {
2186
                var value = ((this.grapharea - (mouseY - prop['chart.gutter.top'])) / this.grapharea) * (this.scale2.max - this.scale2.min)
2187
                value += this.scale2.min;
2188
            }
2189
 
2190
            return value;
2191
        }
2192
 
2193
 
2194
        /**
2195
        * This function can be used when the canvas is clicked on (or similar - depending on the event)
2196
        * to retrieve the relevant Y coordinate for a particular value.
2197
        *
2198
        * @param int value The value to get the Y coordinate for
2199
        */
2200
        this.getYCoord = function (value)
2201
        {
2202
            if (value > this.scale2.max) {
2203
                return null;
2204
            }
2205
 
2206
            var co   = this.context;
2207
            var ca   = this.canvas;
2208
            var prop = this.properties;
2209
 
2210
            var y;
2211
            var xaxispos = prop['chart.xaxispos'];
2212
 
2213
            if (xaxispos == 'top') {
2214
 
2215
                // Account for negative numbers
2216
                if (value < 0) {
2217
                    value = Math.abs(value);
2218
                }
2219
 
2220
                y = ((value - this.scale2.min) / (this.scale2.max - this.scale2.min)) * this.grapharea;
2221
                y = y + this.gutterTop
2222
 
2223
            } else if (xaxispos == 'center') {
2224
 
2225
                y = ((value - this.scale2.min) / (this.scale2.max - this.scale2.min)) * (this.grapharea / 2);
2226
                y = (this.grapharea / 2) - y;
2227
                y += this.gutterTop;
2228
 
2229
            } else {
2230
 
2231
                if (value < this.scale2.min) {
2232
                    value = this.scale2.min;
2233
                }
2234
 
2235
                y = ((value - this.scale2.min) / (this.scale2.max - this.scale2.min)) * this.grapharea;
2236
 
2237
                y = ca.height - this.gutterBottom - y;
2238
            }
2239
 
2240
            return y;
2241
        }
2242
 
2243
 
2244
 
2245
        /**
2246
        * Each object type has its own Highlight() function which highlights the appropriate shape
2247
        *
2248
        * @param object shape The shape to highlight
2249
        */
2250
        this.Highlight = function (shape)
2251
        {
2252
            // Add the new highlight
2253
            RGraph.Highlight.Rect(this, shape);
2254
        }
2255
 
2256
 
2257
 
2258
        /**
2259
        * The getObjectByXY() worker method
2260
        */
2261
        this.getObjectByXY = function (e)
2262
        {
2263
            var ca   = this.canvas;
2264
            var prop = this.properties;
2265
 
2266
            var mouseXY = RGraph.getMouseXY(e);
2267
 
2268
            if (
2269
                   mouseXY[0] >= prop['chart.gutter.left']
2270
                && mouseXY[0] <= (ca.width - prop['chart.gutter.right'])
2271
                && mouseXY[1] >= prop['chart.gutter.top']
2272
                && mouseXY[1] <= (ca.height - prop['chart.gutter.bottom'])
2273
                ) {
2274
 
2275
                return this;
2276
            }
2277
        }
2278
 
2279
 
2280
 
2281
 
2282
        /**
2283
        * This method handles the adjusting calculation for when the mouse is moved
2284
        *
2285
        * @param object e The event object
2286
        */
2287
        this.Adjusting_mousemove = function (e)
2288
        {
2289
            /**
2290
            * Handle adjusting for the Bar
2291
            */
2292
            if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) {
2293
 
2294
                // Rounding the value to the given number of decimals make the chart step
2295
                var value   = Number(this.getValue(e));
2296
                var shape   = this.getShapeByX(e);
2297
 
2298
                if (shape) {
2299
 
2300
                    RG.Registry.Set('chart.adjusting.shape', shape);
2301
 
2302
                    if (this.stackedOrGrouped && prop['chart.grouping'] == 'grouped') {
2303
 
2304
                        var indexes = RG.sequentialIndexToGrouped(shape['index'], this.data);
2305
 
2306
                        if (typeof this.data[indexes[0]] == 'number') {
2307
                            this.data[indexes[0]] = Number(value);
2308
                        } else if (!RG.is_null(this.data[indexes[0]])) {
2309
                            this.data[indexes[0]][indexes[1]] = Number(value);
2310
                        }
2311
                    } else if (typeof this.data[shape['index']] == 'number') {
2312
 
2313
                        this.data[shape['index']] = Number(value);
2314
                    }
2315
 
2316
                    RG.RedrawCanvas(e.target);
2317
 
2318
                    RG.FireCustomEvent(this, 'onadjust');
2319
                }
2320
            }
2321
        }
2322
 
2323
 
2324
 
2325
 
2326
        /**
2327
        * This function positions a tooltip when it is displayed
2328
        *
2329
        * @param obj object    The chart object
2330
        * @param int x         The X coordinate specified for the tooltip
2331
        * @param int y         The Y coordinate specified for the tooltip
2332
        * @param objec tooltip The tooltips DIV element
2333
        */
2334
        this.positionTooltip = function (obj, x, y, tooltip, idx)
2335
        {
2336
            var prop       = obj.properties;
2337
            var coordX     = obj.coords[tooltip.__index__][0];
2338
            var coordY     = obj.coords[tooltip.__index__][1];
2339
            var coordW     = obj.coords[tooltip.__index__][2];
2340
            var coordH     = obj.coords[tooltip.__index__][3];
2341
            var canvasXY   = RGraph.getCanvasXY(obj.canvas);
2342
            var gutterLeft = prop['chart.gutter.left'];
2343
            var gutterTop  = prop['chart.gutter.top'];
2344
            var width      = tooltip.offsetWidth;
2345
            var height     = tooltip.offsetHeight;
2346
            var value      = obj.data_arr[tooltip.__index__];
2347
 
2348
 
2349
            // Set the top position
2350
            tooltip.style.left = 0;
2351
            tooltip.style.top  = canvasXY[1] + coordY - height - 7 + 'px';
2352
 
2353
            /**
2354
            * If the tooltip is for a negative value - position it underneath the bar
2355
            */
2356
            if (value < 0) {
2357
                tooltip.style.top =  canvasXY[1] + coordY + coordH + 7 + 'px';
2358
            }
2359
 
2360
 
2361
            // By default any overflow is hidden
2362
            tooltip.style.overflow = '';
2363
 
2364
            // Inverted arrow
2365
            // data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAFCAMAAACkeOZkAAAAK3RFWHRDcmVhdGlvbiBUaW1lAFNhdCA2IE9jdCAyMDEyIDEyOjQ5OjMyIC0wMDAw2S1RlgAAAAd0SU1FB9wKBgszM4Ed2k4AAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAACVBMVEX/AAC9vb3//+92Pom0AAAAAXRSTlMAQObYZgAAAB1JREFUeNpjYAABRgY4YGRiRDCZYBwQE8qBMEEcAANCACqByy1sAAAAAElFTkSuQmCC
2366
 
2367
            // The arrow
2368
            var img = new Image();
2369
                img.style.position = 'absolute';
2370
                img.id = '__rgraph_tooltip_pointer__';
2371
                if (value >= 0) {
2372
                    img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAFCAYAAACjKgd3AAAARUlEQVQYV2NkQAN79+797+RkhC4M5+/bd47B2dmZEVkBCgcmgcsgbAaA9GA1BCSBbhAuA/AagmwQPgMIGgIzCD0M0AMMAEFVIAa6UQgcAAAAAElFTkSuQmCC';
2373
                    img.style.top = (tooltip.offsetHeight - 2) + 'px';
2374
                } else {
2375
                    img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAFCAMAAACkeOZkAAAAK3RFWHRDcmVhdGlvbiBUaW1lAFNhdCA2IE9jdCAyMDEyIDEyOjQ5OjMyIC0wMDAw2S1RlgAAAAd0SU1FB9wKBgszM4Ed2k4AAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAACVBMVEX/AAC9vb3//+92Pom0AAAAAXRSTlMAQObYZgAAAB1JREFUeNpjYAABRgY4YGRiRDCZYBwQE8qBMEEcAANCACqByy1sAAAAAElFTkSuQmCC';
2376
                    img.style.top = '-5px';
2377
                }
2378
 
2379
            tooltip.appendChild(img);
2380
 
2381
            // Reposition the tooltip if at the edges:
2382
 
2383
            // LEFT edge
2384
            if ((canvasXY[0] + coordX + (coordW / 2) - (width / 2)) < 10) {
2385
                tooltip.style.left = (canvasXY[0] + coordX - (width * 0.1)) + (coordW / 2) + 'px';
2386
                img.style.left = ((width * 0.1) - 8.5) + 'px';
2387
 
2388
            // RIGHT edge
2389
            } else if ((canvasXY[0] + coordX + (width / 2)) > document.body.offsetWidth) {
2390
                tooltip.style.left = canvasXY[0] + coordX - (width * 0.9) + (coordW / 2) + 'px';
2391
                img.style.left = ((width * 0.9) - 8.5) + 'px';
2392
 
2393
            // Default positioning - CENTERED
2394
            } else {
2395
                tooltip.style.left = (canvasXY[0] + coordX + (coordW / 2) - (width * 0.5)) + 'px';
2396
                img.style.left = ((width * 0.5) - 8.5) + 'px';
2397
            }
2398
        }
2399
 
2400
 
2401
 
2402
 
2403
        /**
2404
        * This allows for easy specification of gradients
2405
        */
2406
        this.parseColors = function ()
2407
        {
2408
            // Save the original colors so that they can be restored when the canvas is reset
2409
            if (this.original_colors.length === 0) {
2410
                this.original_colors['chart.colors']                = RGraph.array_clone(prop['chart.colors']);
2411
                this.original_colors['chart.key.colors']            = RGraph.array_clone(prop['chart.key.colors']);
2412
                this.original_colors['chart.crosshairs.color']      = prop['chart.crosshairs.color'];
2413
                this.original_colors['chart.highlight.stroke']      = prop['chart.highlight.stroke'];
2414
                this.original_colors['chart.highlight.fill']        = prop['chart.highlight.fill'];
2415
                this.original_colors['chart.text.color']            = prop['chart.text.color'];
2416
                this.original_colors['chart.background.barcolor1']  = prop['chart.background.barcolor1'];
2417
                this.original_colors['chart.background.barcolor2']  = prop['chart.background.barcolor2'];
2418
                this.original_colors['chart.background.grid.color'] = prop['chart.background.grid.color'];
2419
                this.original_colors['chart.strokecolor']           = prop['chart.strokecolor'];
2420
                this.original_colors['chart.axis.color']            = prop['chart.axis.color'];
2421
            }
2422
 
2423
 
2424
            // chart.colors
2425
            var colors = prop['chart.colors'];
2426
            if (colors) {
2427
                for (var i=0; i<colors.length; ++i) {
2428
                    colors[i] = this.parseSingleColorForGradient(colors[i]);
2429
                }
2430
            }
2431
 
2432
            // chart.key.colors
2433
            var colors = prop['chart.key.colors'];
2434
            if (colors) {
2435
                for (var i=0; i<colors.length; ++i) {
2436
                    colors[i] = this.parseSingleColorForGradient(colors[i]);
2437
                }
2438
            }
2439
 
2440
             prop['chart.crosshairs.color']      = this.parseSingleColorForGradient(prop['chart.crosshairs.color']);
2441
             prop['chart.highlight.stroke']      = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
2442
             prop['chart.highlight.fill']        = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
2443
             prop['chart.text.color']            = this.parseSingleColorForGradient(prop['chart.text.color']);
2444
             prop['chart.background.barcolor1']  = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
2445
             prop['chart.background.barcolor2']  = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
2446
             prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
2447
             prop['chart.strokecolor']           = this.parseSingleColorForGradient(prop['chart.strokecolor']);
2448
             prop['chart.axis.color']            = this.parseSingleColorForGradient(prop['chart.axis.color']);
2449
        }
2450
 
2451
 
2452
 
2453
        /**
2454
        * This parses a single color value
2455
        */
2456
        this.parseSingleColorForGradient = function (color)
2457
        {
2458
            if (!color || typeof(color) != 'string') {
2459
                return color;
2460
            }
2461
 
2462
            if (color.match(/^gradient\((.*)\)$/i)) {
2463
 
2464
                var parts = RegExp.$1.split(':');
2465
 
2466
                // Create the gradient
2467
                var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'], 0, prop['chart.gutter.top']);
2468
 
2469
                var diff = 1 / (parts.length - 1);
2470
 
2471
                grad.addColorStop(0, RG.trim(parts[0]));
2472
 
2473
                for (var j=1,len=parts.length; j<len; ++j) {
2474
                    grad.addColorStop(j * diff, RGraph.trim(parts[j]));
2475
                }
2476
            }
2477
 
2478
            return grad ? grad : color;
2479
        }
2480
 
2481
 
2482
 
2483
        this.DrawBevel = function ()
2484
        {
2485
           var coords  = this.coords;
2486
           var coords2 = this.coords2;
2487
 
2488
           var prop    = this.properties;
2489
           var co      = this.context;
2490
           var ca      = this.canvas;
2491
 
2492
            if (prop['chart.grouping'] == 'stacked') {
2493
                for (var i=0; i<coords2.length; ++i) {
2494
                    if (coords2[i] && coords2[i][0] && coords2[i][0][0]) {
2495
 
2496
                        var x = coords2[i][0][0];
2497
                        var y = coords2[i][0][1];
2498
                        var w = coords2[i][0][2];
2499
 
2500
                        var arr = [];
2501
                        for (var j=0; j<coords2[i].length; ++j) {
2502
                            arr.push(coords2[i][j][3]);
2503
                        }
2504
                        var h = RGraph.array_sum(arr);
2505
 
2506
 
2507
                        co.save();
2508
 
2509
                            co.strokeStyle = 'black';
2510
 
2511
                            // Clip to the rect
2512
                            co.beginPath();
2513
                            co.rect(x, y, w, h);
2514
                            co.clip();
2515
 
2516
                            // Add the shadow
2517
                            co.shadowColor = 'black';
2518
                            co.shadowOffsetX = 0;
2519
                            co.shadowOffsetY = 0;
2520
                            co.shadowBlur = 20;
2521
 
2522
                            co.beginPath();
2523
                            co.rect(x - 3, y - 3, w + 6, h + 100);
2524
                            co.lineWidth = 5;
2525
                            co.stroke();
2526
                        co.restore();
2527
                    }
2528
                }
2529
            } else {
2530
 
2531
                for (var i=0; i<coords.length; ++i) {
2532
                    if (coords[i]) {
2533
 
2534
                        var x = coords[i][0];
2535
                        var y = coords[i][1];
2536
                        var w = coords[i][2];
2537
                        var h = coords[i][3];
2538
 
2539
                        var xaxispos = prop['chart.xaxispos'];
2540
                        var xaxis_ycoord = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
2541
 
2542
 
2543
                        co.save();
2544
 
2545
                            co.strokeStyle = 'black';
2546
 
2547
                            // Clip to the rect
2548
                            co.beginPath();
2549
                            co.rect(x, y, w, h);
2550
 
2551
                            co.clip();
2552
 
2553
                            // Add the shadow
2554
                            co.shadowColor = 'black';
2555
                            co.shadowOffsetX = 0;
2556
                            co.shadowOffsetY = 0;
2557
                            co.shadowBlur =  20;
2558
 
2559
                            if (xaxispos == 'top' || (xaxispos == 'center' && (y + h) > xaxis_ycoord)) {
2560
                                y = y - 100;
2561
                                h = h + 100;
2562
                            } else {
2563
                                y = y;
2564
                                h = h + 100;
2565
                            }
2566
 
2567
                            co.beginPath();
2568
                                co.rect(x - 3, y - 3, w + 6, h + 6);
2569
                                co.lineWidth = 5;
2570
                            co.stroke();
2571
                        co.restore();
2572
                    }
2573
                }
2574
            }
2575
        }
2576
 
2577
 
2578
 
2579
 
2580
        /**
2581
        * This function handles highlighting an entire data-series for the interactive
2582
        * key
2583
        *
2584
        * @param int index The index of the data series to be highlighted
2585
        */
2586
        this.interactiveKeyHighlight = function (index)
2587
        {
2588
            this.coords2.forEach(function (value, idx, arr)
2589
            {
2590
                if (typeof value[index] == 'object' && value[index]) {
2591
 
2592
                    var x = value[index][0]
2593
                    var y = value[index][1]
2594
                    var w = value[index][2]
2595
                    var h = value[index][3]
2596
 
2597
                    co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
2598
                    co.strokeStyle = prop['chart.key.interactive.highlight.chart.stroke'];
2599
                    co.lineWidth   = 2;
2600
                    co.fillRect(x, y, w, h);
2601
                    co.strokeRect(x, y, w, h);
2602
                }
2603
            });
2604
        }
2605
 
2606
 
2607
 
2608
 
2609
        /**
2610
        * Register the object
2611
        */
2612
        RGraph.Register(this);
2613
    }
2614
 
2615
 
2616
 
2617
 
2618
 
2619
    /*********************************************************************************************************
2620
    * This is the combined bar and Line class which makes creating bar/line combo charts a little bit easier *
2621
    /*********************************************************************************************************/
2622
 
2623
 
2624
 
2625
 
2626
 
2627
 
2628
 
2629
    RGraph.CombinedChart = function ()
2630
    {
2631
        /**
2632
        * Create a default empty array for the objects
2633
        */
2634
        this.objects = [];
2635
 
2636
        var objects = arguments;
2637
 
2638
        if (RGraph.is_array(arguments[0])) {
2639
            objects = arguments[0];
2640
        }
2641
 
2642
        for (var i=0; i<objects.length; ++i) {
2643
 
2644
            this.objects[i] = objects[i];
2645
 
2646
            /**
2647
            * Set the Line chart gutters to match the Bar chart gutters
2648
            */
2649
            this.objects[i].Set('chart.gutter.left',  this.objects[0].Get('chart.gutter.left'));
2650
            this.objects[i].Set('chart.gutter.right',  this.objects[0].Get('chart.gutter.right'));
2651
            this.objects[i].Set('chart.gutter.top',    this.objects[0].Get('chart.gutter.top'));
2652
            this.objects[i].Set('chart.gutter.bottom', this.objects[0].Get('chart.gutter.bottom'));
2653
 
2654
            if (this.objects[i].type == 'line') {
2655
 
2656
                /**
2657
                * Set the line chart hmargin
2658
                */
2659
                this.objects[i].Set('chart.hmargin', ((this.objects[0].canvas.width - this.objects[0].Get('chart.gutter.right') - this.objects[0].Get('chart.gutter.left')) / this.objects[0].data.length) / 2 );
2660
 
2661
 
2662
                /**
2663
                * No labels, axes or grid on the Line chart
2664
                */
2665
                this.objects[i].Set('chart.noaxes', true);
2666
                this.objects[i].Set('chart.background.grid', false);
2667
                this.objects[i].Set('chart.ylabels', false);
2668
            }
2669
 
2670
            /**
2671
            * Resizing
2672
            */
2673
            if (this.objects[i].Get('chart.resizable')) {
2674
                var resizable_object = this.objects[i];
2675
            }
2676
        }
2677
 
2678
        /**
2679
        * Resizing
2680
        */
2681
        if (resizable_object) {
2682
            /**
2683
            * This recalculates the Line chart hmargin when the chart is resized
2684
            */
2685
            function myOnresizebeforedraw (obj)
2686
            {
2687
                var gutterLeft = obj.Get('chart.gutter.left');
2688
                var gutterRight = obj.Get('chart.gutter.right');
2689
 
2690
                obj.Set('chart.hmargin', (obj.canvas.width - gutterLeft - gutterRight) / (obj.original_data[0].length * 2));
2691
            }
2692
 
2693
            RGraph.AddCustomEventListener(resizable_object,
2694
                                          'onresizebeforedraw',
2695
                                          myOnresizebeforedraw);
2696
        }
2697
    }
2698
 
2699
 
2700
    /**
2701
    * The Add method can be used to add methods to the CombinedChart object.
2702
    */
2703
    RGraph.CombinedChart.prototype.Add = function (obj)
2704
    {
2705
        this.objects.push(obj);
2706
    }
2707
 
2708
 
2709
    /**
2710
    * The Draw method goes through all of the objects drawing them (sequentially)
2711
    */
2712
    RGraph.CombinedChart.prototype.Draw = function ()
2713
    {
2714
        for (var i=0; i<this.objects.length; ++i) {
2715
            if (typeof(arguments[i]) == 'function') {
2716
                arguments[i](this.objects[i]);
2717
            } else {
2718
                this.objects[i].Draw();
2719
            }
2720
        }
2721
    }