source: Dev/branches/rest-dojo-ui/jQueryUI/client/RGraph/libraries/RGraph.waterfall.js @ 312

Last change on this file since 312 was 312, checked in by jkraaijeveld, 13 years ago
File size: 25.4 KB
Line 
1/**
2    * o------------------------------------------------------------------------------o
3    * | This file is part of the RGraph package - you can learn more at:             |
4    * |                                                                              |
5    * |                          http://www.rgraph.net                               |
6    * |                                                                              |
7    * | This package is licensed under the RGraph license. For all kinds of business |
8    * | purposes there is a small one-time licensing fee to pay and for non          |
9    * | commercial  purposes it is free to use. You can read the full license here:  |
10    * |                                                                              |
11    * |                      http://www.rgraph.net/LICENSE.txt                       |
12    * o------------------------------------------------------------------------------o
13    */
14   
15    if (typeof(RGraph) == 'undefined') RGraph = {};
16
17    /**
18    * The bar chart constructor
19    *
20    * @param object canvas The canvas object
21    * @param array  data   The chart data
22    */
23    RGraph.Waterfall = function (id, data)
24    {
25        // Get the canvas and context objects
26        this.id                = id;
27        this.canvas            = document.getElementById(id);
28        this.context           = this.canvas.getContext ? this.canvas.getContext("2d") : null;
29        this.canvas.__object__ = this;
30        this.type              = 'waterfall';
31        this.max               = 0;
32        this.isRGraph          = true;
33        this.coords            = [];
34
35        /**
36        * Compatibility with older browsers
37        */
38        RGraph.OldBrowserCompat(this.context);
39
40
41        // Various config
42        this.properties = {
43            'chart.background.barcolor1':   'rgba(0,0,0,0)',
44            'chart.background.barcolor2':   'rgba(0,0,0,0)',
45            'chart.background.grid':        true,
46            'chart.background.grid.color':  '#ddd',
47            'chart.background.grid.width':  1,
48            'chart.background.grid.hsize':  20,
49            'chart.background.grid.vsize':  20,
50            'chart.background.grid.vlines': true,
51            'chart.background.grid.hlines': true,
52            'chart.background.grid.border': true,
53            'chart.background.grid.autofit':false,
54            'chart.background.grid.autofit.numhlines': 7,
55            'chart.background.grid.autofit.numvlines': 20,
56            'chart.background.grid.autofit.align': false,
57            'chart.background.image':       null,
58            'chart.background.hbars':       null, // ???
59
60            'chart.numyticks':              10,
61            'chart.hmargin':                5,
62            'chart.strokestyle':            '#666',
63            'chart.axis.color':             'black',
64            'chart.gutter.left':            25,
65            'chart.gutter.right':           25,
66            'chart.gutter.top':             25,
67            'chart.gutter.bottom':          25,
68            'chart.labels':                 [],
69            'chart.ylabels':                true,
70            'chart.text.color':             'black',
71            'chart.text.size':              10,
72            'chart.text.angle':             0,
73            'chart.text.font':              'Verdana',
74            'chart.ymax':                   null,
75
76            'chart.title':                  '',
77            'chart.title.color':            'black',
78            'chart.title.background':       null,
79            'chart.title.hpos':             null,
80            'chart.title.vpos':             null,
81            'chart.title.xaxis':            '',
82            'chart.title.yaxis':            '',
83            'chart.title.xaxis.pos':        0.25,
84            'chart.title.yaxis.pos':        0.25,
85            'chart.title.yaxis.align':      'left',
86
87            'chart.colors':                 ['green', 'red', 'blue'],
88
89            'chart.shadow':                 false,
90            'chart.shadow.color':           '#666',
91            'chart.shadow.offsetx':         3,
92            'chart.shadow.offsety':         3,
93            'chart.shadow.blur':            3,
94
95            'chart.tooltips':               null,
96            'chart.tooltips.effect':        'fade',
97            'chart.tooltips.css.class':     'RGraph_tooltip',
98        //'chart.tooltips.event':         'onclick',
99            'chart.tooltips.coords.adjust': [0,0],
100            'chart.tooltips.highlight':     true,
101            'chart.tooltips.override':     null,
102
103            'chart.highlight.stroke':       'black',
104            'chart.highlight.fill':         'rgba(255,255,255,0.5)',
105
106            'chart.contextmenu':            null,
107
108            'chart.units.pre':              '',
109            'chart.units.post':             '',
110       
111        //'chart.scale.decimals':         0,
112        //'chart.scale.point':            '.',
113        //'chart.scale.thousand':         ',',
114        //'chart.scale.formatter':        null,
115
116            'chart.crosshairs':             false,
117            'chart.crosshairs.color':       '#333',
118
119            'chart.annotatable':            false,
120            'chart.annotate.color':         'black',
121           
122            'chart.zoom.factor':            1.5,
123            'chart.zoom.fade.in':           true,
124            'chart.zoom.fade.out':          true,
125            'chart.zoom.hdir':              'right',
126            'chart.zoom.vdir':              'down',
127            'chart.zoom.frames':            10,
128            'chart.zoom.delay':             50,
129            'chart.zoom.shadow':            true,
130            'chart.zoom.mode':              'canvas',
131            'chart.zoom.thumbnail.width':   75,
132            'chart.zoom.thumbnail.height':  75,
133            'chart.zoom.background':        true,
134
135            'chart.resizable':              false,
136            'chart.resize.handle.adjust':   [0,0],
137            'chart.resize.handle.background': null,
138           
139            'chart.noaxes':                 false,
140            'chart.noxaxis':                false,
141            'chart.noyaxis':                false,
142
143            'chart.axis.color':             'black',
144            'chart.total':                  true
145        }
146
147        // Check for support
148        if (!this.canvas) {
149            alert('[WATERFALL] No canvas support');
150            return;
151        }
152
153        // Store the data
154        this.data = data;
155    }
156
157
158    /**
159    * A setter
160    *
161    * @param name  string The name of the property to set
162    * @param value mixed  The value of the property
163    */
164    RGraph.Waterfall.prototype.Set = function (name, value)
165    {
166        this.properties[name.toLowerCase()] = value;
167    }
168
169
170    /**
171    * A getter
172    *
173    * @param name  string The name of the property to get
174    */
175    RGraph.Waterfall.prototype.Get = function (name)
176    {       
177        return this.properties[name.toLowerCase()];
178    }
179
180
181    /**
182    * The function you call to draw the bar chart
183    */
184    RGraph.Waterfall.prototype.Draw = function ()
185    {
186        // MUST be the first thing done!
187        if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
188            RGraph.DrawBackgroundImage(this);
189            return;
190        }
191
192
193        /**
194        * Fire the onbeforedraw event
195        */
196        RGraph.FireCustomEvent(this, 'onbeforedraw');
197
198        /**
199        * Clear all of this canvases event handlers (the ones installed by RGraph)
200        */
201        RGraph.ClearEventListeners(this.id);
202       
203        /**
204        * This is new in May 2011 and facilitates indiviual gutter settings,
205        * eg chart.gutter.left
206        */
207        this.gutterLeft   = this.Get('chart.gutter.left');
208        this.gutterRight  = this.Get('chart.gutter.right');
209        this.gutterTop    = this.Get('chart.gutter.top');
210        this.gutterBottom = this.Get('chart.gutter.bottom');
211
212        /**
213        * Stop the coords array from growing uncontrollably
214        */
215        this.coords = [];
216
217        /**
218        * Work out a few things. They need to be here because they depend on things you can change after you instantiate the object
219        */
220        this.max            = 0;
221        this.grapharea      = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
222        this.graphwidth     = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight;
223        this.halfTextHeight = this.Get('chart.text.size') / 2;
224
225        // Work out the resulting total
226        if (typeof(this.Get('chart.total')) == 'boolean' && this.Get('chart.total')) {
227            var total = RGraph.array_sum(this.data);
228            this.data.push(total);
229            this.Set('chart.total', total);
230        }
231
232        /**
233        * Work out the maximum value
234        */
235        var runningTotal = 0;
236        for (var i=0; i<this.data.length - 1; ++i) {
237            runningTotal += this.data[i];
238            this.max = Math.max(this.max, runningTotal);
239        }
240
241        this.scale   = RGraph.getScale(typeof(this.Get('chart.ymax')) == 'number' ? this.Get('chart.ymax') : this.max, this);
242        this.max     = this.scale[4];
243        var decimals = this.Get('chart.scale.decimals');
244
245        this.scale = [
246                      (this.max * (1/5)).toFixed(decimals),
247                      (this.max * (2/5)).toFixed(decimals),
248                      (this.max * (3/5)).toFixed(decimals),
249                      (this.max * (4/5)).toFixed(decimals),
250                      this.max.toFixed(decimals)
251                     ];
252
253
254        // Progressively Draw the chart
255        RGraph.background.Draw(this);
256
257        this.Drawbars();
258        this.DrawAxes();
259        this.DrawLabels();
260
261        /**
262        * Setup the context menu if required
263        */
264        if (this.Get('chart.contextmenu')) {
265            RGraph.ShowContext(this);
266        }
267
268       
269        /**
270        * Draw crosschairs
271        */
272        if (this.Get('chart.crosshairs')) {
273            RGraph.DrawCrosshairs(this);
274        }
275       
276        /**
277        * If the canvas is annotatable, do install the event handlers
278        */
279        if (this.Get('chart.annotatable')) {
280            RGraph.Annotate(this);
281        }
282       
283        /**
284        * This bit shows the mini zoom window if requested
285        */
286        if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
287            RGraph.ShowZoomWindow(this);
288        }
289
290       
291        /**
292        * This function enables resizing
293        */
294        if (this.Get('chart.resizable')) {
295            RGraph.AllowResizing(this);
296        }
297       
298        /**
299        * Tooltips
300        */
301        if (this.Get('chart.tooltips')) {
302       
303            RGraph.Register(this);
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318            /**
319            * Install the onclick event handler for the tooltips
320            */
321            var canvas_onclick_func = function (e)
322            {
323                e = RGraph.FixEventObject(e);
324
325                var canvas  = document.getElementById(this.id);
326                var context = canvas.getContext('2d');
327                var obj     = canvas.__object__;
328
329                /**
330                * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
331                * This "deselects" any already selected bar
332                */
333                RGraph.Clear(canvas);
334                obj.Draw();
335   
336                /**
337                * Get the mouse X/Y coordinates
338                */
339                var mouseCoords = RGraph.getMouseXY(e);
340                var mouseX      = mouseCoords[0];
341                var mouseY      = mouseCoords[1];
342                var adjust      = obj.Get('chart.tooltips.coords.adjust');
343
344                /**
345                * Loop through the bars determining if the mouse is over a bar
346                */
347                for (var i=0; i<obj.coords.length; i++) {
348               
349                    var coordsX = obj.coords[i][0];
350                    var coordsY = obj.coords[i][1];
351                    var coordsW = obj.coords[i][2];
352                    var coordsH = obj.coords[i][3];
353
354                    if (
355                        mouseX >= (coordsX + adjust[0]) &&
356                        mouseX <= (coordsX + coordsW + adjust[0]) &&
357                        mouseY >= (coordsY + adjust[1]) &&
358                        mouseY <= (coordsY + coordsH + adjust[1])
359                       ) {
360                       
361                       if (!obj.Get('chart.tooltips')[i]) {
362                        return;
363                       }
364                       
365                        // Draw the highlight (if necessary)
366                        if (obj.Get('chart.tooltips.highlight')) {
367                           context.beginPath();
368                               context.fillStyle = obj.Get('chart.highlight.fill');
369                               context.strokeStyle = obj.Get('chart.highlight.stroke');
370                               context.strokeRect(coordsX, coordsY, coordsW, coordsH);
371                               context.fillRect(coordsX, coordsY, coordsW, coordsH);
372                           context.stroke();
373                           context.fill();
374                        }
375
376                        /**
377                        * Get the tooltip text
378                        */
379                        if (typeof(obj.Get('chart.tooltips')) == 'function') {
380                            var text = String(obj.Get('chart.tooltips')(i));
381                       
382                        } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'function') {
383                            var text = String(obj.Get('chart.tooltips')[barCoords[5]](i));
384                       
385                        } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[i]) == 'string' || typeof(obj.Get('chart.tooltips')[i]) == 'number')) {
386                            var text = String(obj.Get('chart.tooltips')[i]);
387                   
388                        } else {
389                            var text = null;
390                        }
391   
392                        if (text) {
393                            canvas.style.cursor = 'pointer';
394                        } else {
395                            canvas.style.cursor = 'default';
396                        }
397
398                        RGraph.Tooltip(canvas, obj.Get('chart.tooltips')[i], e.pageX, e.pageY, text, i);
399                    }
400                }
401
402                /**
403                * Stop the event bubbling
404                */
405                e.stopPropagation();
406               
407                return false;
408            }
409            this.canvas.addEventListener('click', canvas_onclick_func, false);
410            RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
411
412            /**
413            * Install the window onclick handler
414            */
415            var window_onclick_func = function (){RGraph.Redraw();};
416            window.addEventListener('click', window_onclick_func, false);
417            RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func);
418
419
420
421
422
423
424
425
426
427
428
429
430
431            /**
432            * Install the onmousemove event handler for the tooltips
433            */
434            var canvas_onmousemove_func = function (e)
435            {
436                e = RGraph.FixEventObject(e);
437
438                var canvas  = document.getElementById(this.id);
439                var context = canvas.getContext('2d');
440                var obj     = canvas.__object__;
441   
442                /**
443                * Get the mouse X/Y coordinates
444                */
445                var mouseCoords = RGraph.getMouseXY(e);
446                var mouseX      = mouseCoords[0];
447                var mouseY      = mouseCoords[1];
448                var adjust      = obj.Get('chart.tooltips.coords.adjust');
449
450                /**
451                * Loop through the bars determining if the mouse is over a bar
452                */
453                for (var i=0; i<obj.coords.length; i++) {
454               
455                    var coordsX = obj.coords[i][0];
456                    var coordsY = obj.coords[i][1];
457                    var coordsW = obj.coords[i][2];
458                    var coordsH = obj.coords[i][3];
459
460                    if (
461                        mouseX >= (coordsX + adjust[0]) &&
462                        mouseX <= (coordsX + coordsW + adjust[0]) &&
463                        mouseY >= (coordsY + adjust[1]) &&
464                        mouseY <= (coordsY + coordsH + adjust[1])
465                       ) {
466                       if (obj.Get('chart.tooltips')[i]) {                       
467                            canvas.style.cursor = 'pointer';
468                            e.stopPropagation();
469                        }
470
471                       return;
472                    }
473                }
474               
475                canvas.style.cursor = 'default';
476
477                /**
478                * Stop the event bubbling
479                */
480                e.stopPropagation();
481               
482                return false;
483            }
484            this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
485            RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
486
487
488
489
490
491
492
493
494
495
496
497
498
499        }
500
501       
502        /**
503        * Fire the RGraph ondraw event
504        */
505        RGraph.FireCustomEvent(this, 'ondraw');
506    }
507
508   
509    /**
510    * Draws the charts axes
511    */
512    RGraph.Waterfall.prototype.DrawAxes = function ()
513    {
514        if (this.Get('chart.noaxes')) {
515            return;
516        }
517
518        this.context.beginPath();
519        this.context.strokeStyle = this.Get('chart.axis.color');
520        this.context.lineWidth = 1;
521
522        // Draw the Y axis
523        if (this.Get('chart.noyaxis') == false) {
524            this.context.moveTo(this.gutterLeft, this.gutterTop);
525            this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
526        }
527
528        // Draw the X axis
529        if (this.Get('chart.noxaxis') == false) {
530            this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
531            this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
532        }
533
534        var numYTicks = this.Get('chart.numyticks');
535
536        // Draw the Y tickmarks
537        if (this.Get('chart.noyaxis') == false) {
538
539            var yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / numYTicks;
540   
541            for (y=this.gutterTop; y < (RGraph.GetHeight(this) - this.gutterBottom); y += yTickGap) {
542                this.context.moveTo(this.gutterLeft, y);
543                this.context.lineTo(this.gutterLeft - 3, y);
544            }
545           
546            /**
547            * If the X axis is not being shown, draw an extra tick
548            */
549            if (this.Get('chart.noxaxis')) {
550                this.context.moveTo(this.gutterLeft - 3, RGraph.GetHeight(this) - this.gutterBottom);
551                this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
552            }
553        }
554
555
556        // Draw the X tickmarks
557        if (this.Get('chart.noxaxis') == false) {
558
559            xTickGap = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight ) / this.data.length;
560           
561            yStart   = RGraph.GetHeight(this) - this.gutterBottom;
562            yEnd     = (RGraph.GetHeight(this) - this.gutterBottom) + 3;
563   
564            for (x=this.gutterLeft + xTickGap; x<=RGraph.GetWidth(this) - this.gutterRight; x+=xTickGap) {
565                this.context.moveTo(x, yStart);
566                this.context.lineTo(x, yEnd);
567            }
568           
569            if (this.Get('chart.noyaxis')) {
570                this.context.moveTo(this.gutterLeft, yStart);
571                this.context.lineTo(this.gutterLeft, yEnd);
572            }
573        }
574
575        /**
576        * If the Y axis is not being shown, draw an extra tick
577        */
578        if (this.Get('chart.noyaxis') && this.Get('chart.noxaxis') == false) {
579            this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
580            this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom + 3);
581        }
582
583        this.context.stroke();
584    }
585
586
587    /**
588    * Draws the labels for the graph
589    */
590    RGraph.Waterfall.prototype.DrawLabels = function ()
591    {
592        var context    = this.context;
593        var numYLabels = 5;
594        var interval   = this.grapharea / numYLabels;
595        var font       = this.Get('chart.text.font');
596        var size       = this.Get('chart.text.size');
597        var color      = this.Get('chart.text.color');
598        var units_pre  = this.Get('chart.units.pre');
599        var units_post = this.Get('chart.units.post');
600       
601        this.context.beginPath();
602        this.context.fillStyle = color;
603
604        /**
605        * First, draw the Y labels
606        */
607        if (this.Get('chart.ylabels')) {
608            for (var i=1; i<=numYLabels; ++i) {
609
610                RGraph.Text(context,
611                            font,
612                            size,
613                            this.gutterLeft - 5,
614                            this.canvas.height - this.gutterBottom - (i * interval),
615                            RGraph.number_format(this, this.scale[i - 1], units_pre, units_post),
616                            'center',
617                            'right');
618
619            }
620        }
621
622
623
624        /**
625        * Now, draw the X labels
626        */
627        if (this.Get('chart.labels').length > 0) {
628       
629            // Recalculate the interval for the X labels
630            interval = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / this.Get('chart.labels').length;
631           
632            var halign = 'center';
633            var angle  = this.Get('chart.text.angle');
634           
635            if (angle) {
636                halign = 'right';
637                angle *= -1;
638            }
639
640            for (var i=0; i<this.Get('chart.labels').length; ++i) {
641               
642                var labels = this.Get('chart.labels');
643
644                RGraph.Text(context,
645                            font,
646                            size,
647                            this.gutterLeft - 5 + (i * interval) + (interval / 2),// X
648                            RGraph.GetHeight(this) - this.gutterBottom + 5 + this.halfTextHeight, // Y
649                            labels[i],
650                            'center',
651                            halign,
652                            null,
653                            angle);
654
655            }
656        }
657       
658        this.context.stroke();
659        this.context.fill();
660    }
661
662
663    /**
664    * Draws the bars on to the chart
665    */
666    RGraph.Waterfall.prototype.Drawbars = function ()
667    {
668        var context      = this.context;
669        var canvas       = this.canvas;
670        var hmargin      = this.Get('chart.hmargin');
671        var runningTotal = 0;
672
673       
674            for (var i=0; i<this.data.length; ++i) {
675                context.beginPath();
676                context.strokeStyle = this.Get('chart.strokestyle');
677
678                    var isLast = (i == (this.data.length - 1));
679                    var x      = this.gutterLeft + hmargin + ((this.graphwidth / this.data.length) * i);
680                    var y      = this.gutterTop + this.grapharea - (i == 0 ? ((this.data[0] / this.max) * this.grapharea) : (this.data[i] > 0 ? ((runningTotal + this.data[i]) / this.max) * this.grapharea : (runningTotal / this.max) * this.grapharea));
681                    var w      = ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / this.data.length) - (2 * this.Get('chart.hmargin'));
682                    var h      = (Math.abs(this.data[i]) / this.max) * this.grapharea;
683   
684                    // Color
685                    context.fillStyle = this.data[i] >= 0 ? this.Get('chart.colors')[0] : this.Get('chart.colors')[1];
686   
687                    // Last item? Change to the third color
688                    if (isLast && this.Get('chart.total')) {
689                        context.fillStyle = this.Get('chart.colors')[2];
690                        y = this.gutterTop + this.grapharea - h;
691                    }
692                   
693                    if (this.Get('chart.shadow')) {
694                        RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
695                    } else {
696                        RGraph.NoShadow(this);
697                    }
698   
699                    context.strokeRect(x, y, w, h);
700                    context.fillRect(x, y, w, h);
701                   
702                    this.coords.push([x, y, w, h]);
703                   
704                    runningTotal += this.data[i];
705
706                context.stroke();
707                context.fill();
708            }
709           
710            RGraph.NoShadow(this);
711
712            for (var i=1; i<this.coords.length; ++i) {
713                context.strokeStyle = 'gray';
714                context.beginPath();
715                    if (this.data[i - 1] > 0) {
716                        context.moveTo(this.coords[i - 1][0] + this.coords[i - 1][2], this.coords[i - 1][1]);
717                        context.lineTo(this.coords[i - 1][0] + this.coords[i - 1][2] + (2 * hmargin), this.coords[i - 1][1]);
718                    } else {
719                        context.moveTo(this.coords[i - 1][0] + this.coords[i - 1][2], this.coords[i - 1][1] + this.coords[i - 1][3]);
720                        context.lineTo(this.coords[i - 1][0] + this.coords[i - 1][2] + (2 * hmargin), this.coords[i - 1][1] + this.coords[i - 1][3]);
721                    }
722                context.stroke();
723            }
724    }
725
726
Note: See TracBrowser for help on using the repository browser.