source: Dev/branches/jQueryUI/client/RGraph/libraries/RGraph.waterfall.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

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.