source: Dev/branches/jQueryUI/client/RGraph/libraries/RGraph.funnel.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: 21.5 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.Funnel = 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              = 'funnel';
31        this.coords            = [];
32        this.isRGraph          = true;
33
34
35        /**
36        * Compatibility with older browsers
37        */
38        RGraph.OldBrowserCompat(this.context);
39
40
41        // Check for support
42        if (!this.canvas) {
43            alert('[FUNNEL] No canvas support');
44            return;
45        }
46
47        /**
48        * The funnel charts properties
49        */
50        this.properties = {
51            'chart.strokestyle':           'black',
52            'chart.gutter.left':           25,
53            'chart.gutter.right':          25,
54            'chart.gutter.top':            25,
55            'chart.gutter.bottom':         25,
56            'chart.labels':                null,
57            'chart.title':                 '',
58            'chart.title.background':       null,
59            'chart.title.hpos':             null,
60            'chart.title.vpos':            null,
61            'chart.colors':                ['red', 'green', 'gray', 'blue', 'black', 'gray'],
62            'chart.text.size':             10,
63            'chart.text.boxed':            true,
64            'chart.text.halign':           'left',
65            'chart.text.color':            'black',
66            'chart.text.font':             'Verdana',
67            'chart.contextmenu':           null,
68            'chart.shadow':                false,
69            'chart.shadow.color':          '#666',
70            'chart.shadow.blur':           3,
71            'chart.shadow.offsetx':        3,
72            'chart.shadow.offsety':        3,
73            'chart.key':                    [],
74            'chart.key.background':         'white',
75            'chart.key.position':           'graph',
76            'chart.key.halign':             'right',
77            'chart.key.shadow':             false,
78            'chart.key.shadow.color':       '#666',
79            'chart.key.shadow.blur':        3,
80            'chart.key.shadow.offsetx':     2,
81            'chart.key.shadow.offsety':     2,
82            'chart.key.position.gutter.boxed': true,
83            'chart.key.position.x':         null,
84            'chart.key.position.y':         null,
85            'chart.key.color.shape':        'square',
86            'chart.key.rounded':            true,
87            'chart.key.linewidth':          1,
88            'chart.tooltips':               null,
89            'chart.tooltips.effect':        'fade',
90            'chart.tooltips.css.class':     'RGraph_tooltip',
91            'chart.highlight.stroke':       'black',
92            'chart.highlight.fill':         'rgba(255,255,255,0.5)',
93            'chart.tooltips.highlight':     true,
94            'chart.annotatable':           false,
95            'chart.annotate.color':        'black',
96            'chart.zoom.factor':           1.5,
97            'chart.zoom.fade.in':          true,
98            'chart.zoom.fade.out':         true,
99            'chart.zoom.factor':           1.5,
100            'chart.zoom.fade.in':          true,
101            'chart.zoom.fade.out':         true,
102            'chart.zoom.hdir':             'right',
103            'chart.zoom.vdir':             'down',
104            'chart.zoom.frames':           10,
105            'chart.zoom.delay':            50,
106            'chart.zoom.shadow':           true,
107            'chart.zoom.mode':             'canvas',
108            'chart.zoom.thumbnail.width':  75,
109            'chart.zoom.thumbnail.height': 75,
110            'chart.zoom.background':        true,
111            'chart.zoom.action':            'zoom',
112            'chart.resizable':              false,
113            'chart.resize.handle.adjust':   [0,0],
114            'chart.resize.handle.background': null
115        }
116
117        // Store the data
118        this.data = data;
119
120        /**
121        * Set the .getShape commonly named method
122        */
123        this.getShape = this.getSegment;
124    }
125
126
127    /**
128    * A setter
129    *
130    * @param name  string The name of the property to set
131    * @param value mixed  The value of the property
132    */
133    RGraph.Funnel.prototype.Set = function (name, value)
134    {
135        this.properties[name.toLowerCase()] = value;
136    }
137
138
139    /**
140    * A getter
141    *
142    * @param name  string The name of the property to get
143    */
144    RGraph.Funnel.prototype.Get = function (name)
145    {
146        return this.properties[name.toLowerCase()];
147    }
148
149
150    /**
151    * The function you call to draw the bar chart
152    */
153    RGraph.Funnel.prototype.Draw = function ()
154    {
155        /**
156        * Fire the onbeforedraw event
157        */
158        RGraph.FireCustomEvent(this, 'onbeforedraw');
159
160        /**
161        * Clear all of this canvases event handlers (the ones installed by RGraph)
162        */
163        RGraph.ClearEventListeners(this.id);
164       
165        /**
166        * This is new in May 2011 and facilitates indiviual gutter settings,
167        * eg chart.gutter.left
168        */
169        this.gutterLeft   = this.Get('chart.gutter.left');
170        this.gutterRight  = this.Get('chart.gutter.right');
171        this.gutterTop    = this.Get('chart.gutter.top');
172        this.gutterBottom = this.Get('chart.gutter.bottom');
173
174        // This stops the coords array from growing
175        this.coords = [];
176
177        RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.text.size') + 2);
178        this.DrawFunnel();
179       
180       
181        /**
182        * Setup the context menu if required
183        */
184        if (this.Get('chart.contextmenu')) {
185            RGraph.ShowContext(this);
186        }
187       
188        /**
189        * The tooltip handler
190        */
191        if (this.Get('chart.tooltips')) {
192       
193            RGraph.Register(this);
194
195            var canvas_onclick_func = function (e)
196            {
197                RGraph.Redraw();
198
199                var e       = RGraph.FixEventObject(e);
200                var canvas  = e.target;
201                var context = canvas.getContext('2d');
202                var obj     = canvas.__object__;
203
204                var mouseCoords = RGraph.getMouseXY(e);
205                var coords      = obj.coords;
206                var x           = mouseCoords[0];
207                var y           = mouseCoords[1];
208                var segment     = obj.getSegment(e);
209               
210                if (segment) {
211               
212                    var idx = segment[2];
213
214                    // Is there a tooltip defined?
215                    if (!obj.Get('chart.tooltips')[idx] && typeof(obj.Get('chart.tooltips')) != 'function') {
216                        return;
217                    }
218
219                    context.beginPath();
220
221                    RGraph.NoShadow(obj);
222
223                    context.strokeStyle = obj.Get('chart.highlight.stroke');
224                    context.fillStyle   = obj.Get('chart.highlight.fill');
225
226                    context.moveTo(coords[idx][0], coords[idx][1]);
227                    context.lineTo(coords[idx][2], coords[idx][3]);
228                    context.lineTo(coords[idx][4], coords[idx][5]);
229                    context.lineTo(coords[idx][6], coords[idx][7]);
230                    context.closePath();
231
232                    context.stroke();
233                    context.fill();
234               
235                    /**
236                    * Draw the key again for in-graph keys so they don't appear "under" the highlight
237                    */
238                    if (obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') {
239                        RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
240                    }
241                   
242                    /**
243                    * Redraw the labels if necessary
244                    */
245                    if (obj.Get('chart.labels')) {
246                        obj.DrawLabels();
247                    }
248
249                    /**
250                    * Get the tooltip text
251                    */
252                    if (typeof(obj.Get('chart.tooltips')) == 'function') {
253                        var text = obj.Get('chart.tooltips')(idx);
254                   
255                    } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') {
256                        var text = obj.Get('chart.tooltips')[idx](idx);
257                   
258                    } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
259                        var text = obj.Get('chart.tooltips')[idx];
260
261                    } else {
262                        var text = '';
263                    }
264
265                    // Show the tooltip
266                    RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx);
267   
268                    // Stop the event propagating
269                    e.stopPropagation();
270                }
271            }
272            this.canvas.addEventListener('click', canvas_onclick_func, false);
273            RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
274           
275            /**
276            * Onmousemove event handler
277            */
278            var canvas_onmousemove_func = function (e)
279            {
280                var e = RGraph.FixEventObject(e);
281
282                var canvas = e.target;
283                var context = canvas.getContext('2d');
284                var obj     = canvas.__object__;
285                var overFunnel = false;
286                var coords = obj.coords;
287
288                var mouseCoords = RGraph.getMouseXY(e);
289                var x           = mouseCoords[0];
290                var y           = mouseCoords[1];
291                var segment     = obj.getSegment(e);
292
293                if (segment) {
294               
295                    var idx = segment[2];
296                   
297                    // Is there a tooltip defined?
298                    if (obj.Get('chart.tooltips')[idx] || typeof(obj.Get('chart.tooltips')) == 'function') {
299
300                        overFunnel = true;
301                        canvas.style.cursor = 'pointer';
302       
303                        // Stop the event propagating
304                        e.stopPropagation();
305                       
306                    }
307                }
308               
309                if (!overFunnel) {
310                    canvas.style.cursor = 'default';
311                    canvas.style.cursor = 'default';
312                }
313            }
314            this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
315            RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
316        }
317
318
319        /**
320        * Draw the labels on the chart
321        */
322        this.DrawLabels();
323       
324        /**
325        * If the canvas is annotatable, do install the event handlers
326        */
327        if (this.Get('chart.annotatable')) {
328            RGraph.Annotate(this);
329        }
330       
331        /**
332        * This bit shows the mini zoom window if requested
333        */
334        if (this.Get('chart.zoom.mode') == 'thumbnail'|| this.Get('chart.zoom.mode') == 'area') {
335            RGraph.ShowZoomWindow(this);
336        }
337
338       
339        /**
340        * This function enables resizing
341        */
342        if (this.Get('chart.resizable')) {
343            RGraph.AllowResizing(this);
344        }
345       
346        /**
347        * Fire the RGraph ondraw event
348        */
349        RGraph.FireCustomEvent(this, 'ondraw');
350    }
351
352
353    /**
354    * This function actually draws the chart
355    */
356    RGraph.Funnel.prototype.DrawFunnel = function ()
357    {
358        var context   = this.context;
359        var canvas    = this.canvas;
360        var width     = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight;
361        var height    = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
362        var total     = RGraph.array_max(this.data);
363        var accheight = this.gutterTop;
364
365
366        /**
367        * Loop through each segment to draw
368        */
369       
370        // Set a shadow if it's been requested
371        if (this.Get('chart.shadow')) {
372            context.shadowColor   = this.Get('chart.shadow.color');
373            context.shadowBlur    = this.Get('chart.shadow.blur');
374            context.shadowOffsetX = this.Get('chart.shadow.offsetx');
375            context.shadowOffsetY = this.Get('chart.shadow.offsety');
376        }
377
378        for (i=0; i<this.data.length; ++i) {
379
380            i = Number(i);
381
382            var firstvalue = this.data[0];
383            var firstwidth = (firstvalue / total) * width;
384            var curvalue   = this.data[i];
385            var curwidth   = (curvalue / total) * width;
386            var curheight  = height / this.data.length;
387            var halfCurWidth = (curwidth / 2);
388            var nextvalue  = this.data[i + 1] ?  this.data[i + 1] : 0;
389            var nextwidth  = this.data[i + 1] ? (nextvalue / total) * width : 0;
390            var halfNextWidth = (nextwidth / 2);
391            var center     = this.gutterLeft + (firstwidth / 2);
392
393            /**
394            * First segment
395            */
396            if (i == 0) {
397                var x1 = center - halfCurWidth;
398                var y1 = this.gutterTop;
399                var x2 = center + halfCurWidth;
400                var y2 = this.gutterTop;
401                var x3 = center + halfNextWidth;
402                var y3 = accheight + curheight;
403                var x4 = center - halfNextWidth;
404                var y4 = accheight + curheight;
405
406            /**
407            * Subsequent segments
408            */
409            } else {
410                var x1 = center - halfCurWidth;
411                var y1 = accheight;
412                var x2 = center + halfCurWidth;
413                var y2 = accheight;
414                var x3 = center + halfNextWidth;
415                var y3 = accheight + curheight;
416                var x4 = center - halfNextWidth;
417                var y4 = accheight + curheight;
418            }
419
420            /**
421            * Set the fill colour. If i is over 0 then don't use an offset
422            */
423            if (document.all && this.Get('chart.shadow')) {
424                this.DrawIEShadow([x1, y1, x2, y2, x3, y3, x4, y4], i > 0 && this.Get('chart.shadow.offsety') < 0);
425            }
426
427            context.strokeStyle = this.Get('chart.strokestyle');
428            context.fillStyle   = this.Get('chart.colors')[i];
429
430            context.beginPath();
431                context.moveTo(x1, y1);
432                context.lineTo(x2, y2);
433                context.lineTo(x3, y3);
434                context.lineTo(x4, y4);
435            context.closePath();
436
437            /**
438            * Store the coordinates
439            */
440            this.coords.push([x1, y1, x2, y2, x3, y3, x4, y4]);
441
442            // The redrawing if the shadow is on will do the stroke
443            if (!this.Get('chart.shadow')) {
444                context.stroke();
445            }
446
447            context.fill();
448
449            accheight += curheight;
450        }
451
452        /**
453        * If the shadow is enabled, redraw every segment, in order to allow for shadows going upwards
454        */
455        if (this.Get('chart.shadow')) {
456       
457            RGraph.NoShadow(this);
458       
459            for (i=0; i<this.coords.length; ++i) {
460           
461                context.strokeStyle = this.Get('chart.strokestyle');
462                context.fillStyle = this.Get('chart.colors')[i];
463       
464                context.beginPath();
465                    context.moveTo(this.coords[i][0], this.coords[i][1]);
466                    context.lineTo(this.coords[i][2], this.coords[i][3]);
467                    context.lineTo(this.coords[i][4], this.coords[i][5]);
468                    context.lineTo(this.coords[i][6], this.coords[i][7]);
469                context.closePath();
470               
471                context.stroke();
472                context.fill();
473            }
474        }
475       
476        /**
477        * Lastly, draw the key if necessary
478        */
479        if (this.Get('chart.key') && this.Get('chart.key').length) {
480            RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
481        }
482    }
483   
484    /**
485    * Draws the labels
486    */
487    RGraph.Funnel.prototype.DrawLabels = function ()
488    {
489        /**
490        * Draws the labels
491        */
492        if (this.Get('chart.labels') && this.Get('chart.labels').length > 0) {
493
494            var context = this.context;
495
496            for (var j=0; j<this.coords.length; ++j) {  // MUST be "j"
497                context.beginPath();
498               
499                // Set the color back to black
500                context.strokeStyle = 'black';
501                context.fillStyle = this.Get('chart.text.color');
502               
503                // Turn off any shadow
504                RGraph.NoShadow(this);
505               
506                var label = this.Get('chart.labels')[j];
507
508                RGraph.Text(context,
509                            this.Get('chart.text.font'),
510                            this.Get('chart.text.size'),
511                            this.Get('chart.text.halign') == 'left' ? (this.gutterLeft - 15) : ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft,
512                            this.coords[j][1],
513                            label,
514                            'center',
515                            this.Get('chart.text.halign') == 'left' ? 'left' : 'center',
516                            true,
517                            null,
518                            this.Get('chart.text.boxed') ? 'white' : null);
519            }
520        }
521    }
522
523
524    /**
525    * This function is used by MSIE only to manually draw the shadow
526    *
527    * @param array coords The coords for the bar
528    */
529    RGraph.Funnel.prototype.DrawIEShadow = function (coords, noOffset)
530    {
531        var prevFillStyle = this.context.fillStyle;
532        var offsetx = this.Get('chart.shadow.offsetx');
533        var offsety = this.Get('chart.shadow.offsety');
534        var context = this.context;
535
536        context.lineWidth = 1;
537        context.fillStyle = this.Get('chart.shadow.color');
538       
539        // Draw the shadow
540        context.beginPath();
541            context.moveTo(coords[0] + (noOffset ? 0 : offsetx), coords[1] + (noOffset ? 0 : offsety));
542            context.lineTo(coords[2] + (noOffset ? 0 : offsetx), coords[3] + (noOffset ? 0 : offsety));
543            context.lineTo(coords[4] + (noOffset ? 0 : offsetx), coords[5] + (noOffset ? 0 : offsety));
544            context.lineTo(coords[6] + (noOffset ? 0 : offsetx), coords[7] + (noOffset ? 0 : offsety));
545        context.closePath();
546       
547        context.fill();
548       
549
550       
551        // Change the fillstyle back to what it was
552        this.context.fillStyle = prevFillStyle;
553    }
554
555
556    /**
557    * Gets the appropriate segment that has been highlighted
558    */
559    RGraph.Funnel.prototype.getSegment = function (e)
560    {
561        var canvas      = e.target;
562        var obj         = canvas.__object__;
563        var mouseCoords = RGraph.getMouseXY(e);
564        var coords = obj.coords;
565        var x = mouseCoords[0];
566        var y = mouseCoords[1];
567       
568       
569
570        for (i=0; i<coords.length; ++i) {
571            if (
572                   x > coords[i][0]
573                && x < coords[i][2]
574                && y > coords[i][1]
575                && y < coords[i][5]
576               ) {
577               
578                /**
579                * Handle the right corner
580                */
581                if (x > coords[i][4]) {
582                    var w1 = coords[i][2] - coords[i][4];
583                    var h1 = coords[i][5] - coords[i][3];;
584                    var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
585
586                    var w2 = coords[i][2] - mouseCoords[0];
587                    var h2 = mouseCoords[1] - coords[i][1];
588                    var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
589
590                    if (a2 > a1) {
591                        continue;
592                    }
593               
594                /**
595                * Handle the left corner
596                */
597                } else if (x < coords[i][6]) {
598                    var w1 = coords[i][6] - coords[i][0];
599                    var h1 = coords[i][7] - coords[i][1];;
600                    var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
601
602                    var w2 = mouseCoords[0] - coords[i][0];
603                    var h2 = mouseCoords[1] - coords[i][1];
604                    var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
605               
606                    if (a2 > a1) {
607                        continue;
608                    }
609                }
610               
611                return [obj, coords[i], i];
612            }
613        }
614       
615        return null;
616    }
Note: See TracBrowser for help on using the repository browser.