2 * o------------------------------------------------------------------------------o
3 * | This file is part of the RGraph package - you can learn more at: |
5 * | http://www.rgraph.net |
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: |
11 * | http://www.rgraph.net/LICENSE.txt |
12 * o------------------------------------------------------------------------------o
15 if (typeof(RGraph) == 'undefined') RGraph = {};
18 * The bar chart constructor
20 * @param object canvas The canvas object
21 * @param array data The chart data
23 RGraph.Funnel = function (id, data)
25 // Get the canvas and context objects
27 this.canvas = document.getElementById(id);
28 this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
29 this.canvas.__object__ = this;
36 * Compatibility with older browsers
38 RGraph.OldBrowserCompat(this.context);
43 alert('[FUNNEL] No canvas support');
47 // Check the common library has been included
48 if (typeof(RGraph) == 'undefined') {
49 alert('[FUNNEL] Fatal error: The common library does not appear to have been included');
53 * The funnel charts properties
56 'chart.strokestyle': 'black',
60 'chart.title.background': null,
61 'chart.title.hpos': null,
62 'chart.title.vpos': null,
63 'chart.colors': ['red', 'green', 'gray', 'blue', 'black', 'gray'],
64 'chart.text.size': 10,
65 'chart.text.boxed': true,
66 'chart.text.halign': 'left',
67 'chart.text.color': 'black',
68 'chart.text.font': 'Verdana',
69 'chart.contextmenu': null,
70 'chart.shadow': false,
71 'chart.shadow.color': '#666',
72 'chart.shadow.blur': 3,
73 'chart.shadow.offsetx': 3,
74 'chart.shadow.offsety': 3,
78 'chart.key.background': 'white',
79 'chart.key.position': 'graph',
80 'chart.key.shadow': false,
81 'chart.key.shadow.color': '#666',
82 'chart.key.shadow.blur': 3,
83 'chart.key.shadow.offsetx': 2,
84 'chart.key.shadow.offsety': 2,
85 'chart.key.position.gutter.boxed': true,
86 'chart.key.position.x': null,
87 'chart.key.position.y': null,
88 'chart.key.color.shape': 'square',
89 'chart.key.rounded': true,
91 'chart.tooltips': null,
92 'chart.tooltips.effect': 'fade',
93 'chart.tooltips.css.class': 'RGraph_tooltip',
94 'chart.tooltips.highlight': true,
95 'chart.annotatable': false,
96 'chart.annotate.color': 'black',
97 'chart.zoom.factor': 1.5,
98 'chart.zoom.fade.in': true,
99 'chart.zoom.fade.out': true,
100 'chart.zoom.factor': 1.5,
101 'chart.zoom.fade.in': true,
102 'chart.zoom.fade.out': true,
103 'chart.zoom.hdir': 'right',
104 'chart.zoom.vdir': 'down',
105 'chart.zoom.frames': 10,
106 'chart.zoom.delay': 50,
107 'chart.zoom.shadow': true,
108 'chart.zoom.mode': 'canvas',
109 'chart.zoom.thumbnail.width': 75,
110 'chart.zoom.thumbnail.height': 75,
111 'chart.zoom.background': true,
112 'chart.zoom.action': 'zoom',
113 'chart.resizable': false
124 * @param name string The name of the property to set
125 * @param value mixed The value of the property
127 RGraph.Funnel.prototype.Set = function (name, value)
129 this.properties[name.toLowerCase()] = value;
136 * @param name string The name of the property to get
138 RGraph.Funnel.prototype.Get = function (name)
140 return this.properties[name.toLowerCase()];
145 * The function you call to draw the bar chart
147 RGraph.Funnel.prototype.Draw = function ()
150 * Fire the onbeforedraw event
152 RGraph.FireCustomEvent(this, 'onbeforedraw');
155 * Clear all of this canvases event handlers (the ones installed by RGraph)
157 RGraph.ClearEventListeners(this.id);
159 // This stops the coords array from growing
162 RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.Get('chart.gutter'), null, this.Get('chart.text.size') + 2);
167 * Setup the context menu if required
169 if (this.Get('chart.contextmenu')) {
170 RGraph.ShowContext(this);
174 * The tooltip handler
176 if (this.Get('chart.tooltips')) {
178 RGraph.Register(this);
180 var canvas_onclick_func = function (e)
184 var e = RGraph.FixEventObject(e);
185 var canvas = e.target;
186 var context = canvas.getContext('2d');
187 var obj = canvas.__object__;
189 var mouseCoords = RGraph.getMouseXY(e);
190 var coords = obj.coords;
191 var x = mouseCoords[0];
192 var y = mouseCoords[1];
194 for (i=0; i<coords.length; ++i) {
203 * Handle the right corner
205 if (x > coords[i][4]) {
206 var w1 = coords[i][2] - coords[i][4];
207 var h1 = coords[i][5] - coords[i][3];;
208 var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
210 var w2 = coords[i][2] - mouseCoords[0];
211 var h2 = mouseCoords[1] - coords[i][1];
212 var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
219 * Handle the left corner
221 } else if (x < coords[i][6]) {
222 var w1 = coords[i][6] - coords[i][0];
223 var h1 = coords[i][7] - coords[i][1];;
224 var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
226 var w2 = mouseCoords[0] - coords[i][0];
227 var h2 = mouseCoords[1] - coords[i][1];
228 var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
235 // Is there a tooltip defined?
236 if (!obj.Get('chart.tooltips')[i] && typeof(obj.Get('chart.tooltips')) != 'function') {
242 RGraph.NoShadow(obj);
244 context.fillStyle = 'rgba(255,255,255,0.5)';
245 context.moveTo(coords[i][0], coords[i][1]);
246 context.lineTo(coords[i][2], coords[i][3]);
247 context.lineTo(coords[i][4], coords[i][5]);
248 context.lineTo(coords[i][6], coords[i][7]);
255 * Draw the key again for in-graph keys so they don't appear "under" the highlight
257 if (obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') {
258 RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
262 * Redraw the labels if necessary
264 if (obj.Get('chart.labels')) {
269 * Get the tooltip text
271 if (typeof(obj.Get('chart.tooltips')) == 'function') {
272 var text = obj.Get('chart.tooltips')(i);
274 } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'function') {
275 var text = obj.Get('chart.tooltips')[i](i);
277 } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
278 var text = obj.Get('chart.tooltips')[i];
285 RGraph.Tooltip(canvas, text, e.pageX, e.pageY, i);
287 // Stop the event propagating
294 this.canvas.addEventListener('click', canvas_onclick_func, false);
295 RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
298 * Onmousemove event handler
300 var canvas_onmousemove_func = function (e)
302 var e = RGraph.FixEventObject(e);
304 var canvas = e.target;
305 var context = canvas.getContext('2d');
306 var obj = canvas.__object__;
307 var overFunnel = false;
308 var coords = obj.coords;
310 var mouseCoords = RGraph.getMouseXY(e);
311 var x = mouseCoords[0];
312 var y = mouseCoords[1];
314 for (i=0; i<coords.length; ++i) {
323 * Handle the right corner
325 if (x > coords[i][4]) {
326 var w1 = coords[i][2] - coords[i][4];
327 var h1 = coords[i][5] - coords[i][3];;
328 var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
330 var w2 = coords[i][2] - mouseCoords[0];
331 var h2 = mouseCoords[1] - coords[i][1];
332 var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
339 * Handle the left corner
341 } else if (x < coords[i][6]) {
342 var w1 = coords[i][6] - coords[i][0];
343 var h1 = coords[i][7] - coords[i][1];;
344 var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
346 var w2 = mouseCoords[0] - coords[i][0];
347 var h2 = mouseCoords[1] - coords[i][1];
348 var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
355 // Is there a tooltip defined?
356 if (!obj.Get('chart.tooltips')[i] && typeof(obj.Get('chart.tooltips')) != 'function') {
361 canvas.style.cursor = 'pointer';
363 // Stop the event propagating
371 canvas.style.cursor = 'default';
372 canvas.style.cursor = 'default';
375 this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
376 RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
381 * Draw the labels on the chart
386 * If the canvas is annotatable, do install the event handlers
388 if (this.Get('chart.annotatable')) {
389 RGraph.Annotate(this);
393 * This bit shows the mini zoom window if requested
395 if (this.Get('chart.zoom.mode') == 'thumbnail'|| this.Get('chart.zoom.mode') == 'area') {
396 RGraph.ShowZoomWindow(this);
401 * This function enables resizing
403 if (this.Get('chart.resizable')) {
404 RGraph.AllowResizing(this);
408 * Fire the RGraph ondraw event
410 RGraph.FireCustomEvent(this, 'ondraw');
415 * This function actually draws the chart
417 RGraph.Funnel.prototype.DrawFunnel = function ()
419 var context = this.context;
420 var canvas = this.canvas;
421 var width = this.canvas.width - (2 * this.Get('chart.gutter'));
422 var height = this.canvas.height - (2 * this.Get('chart.gutter'));
423 var total = RGraph.array_max(this.data);
424 var accheight = this.Get('chart.gutter');
428 * Loop through each segment to draw
431 // Set a shadow if it's been requested
432 if (this.Get('chart.shadow')) {
433 context.shadowColor = this.Get('chart.shadow.color');
434 context.shadowBlur = this.Get('chart.shadow.blur');
435 context.shadowOffsetX = this.Get('chart.shadow.offsetx');
436 context.shadowOffsetY = this.Get('chart.shadow.offsety');
439 for (i=0; i<this.data.length; ++i) {
443 var curvalue = this.data[i];
444 var curwidth = (curvalue / total) * width;
445 var curheight = height / this.data.length;
446 var halfCurWidth = (curwidth / 2);
447 var nextvalue = this.data[i + 1] ? this.data[i + 1] : 0;
448 var nextwidth = this.data[i + 1] ? (nextvalue / total) * width : 0;
449 var halfNextWidth = (nextwidth / 2);
450 var center = (canvas.width / 2);
451 var gutter = this.Get('chart.gutter');
457 var x1 = center - halfCurWidth;
459 var x2 = center + halfCurWidth;
461 var x3 = center + halfNextWidth;
462 var y3 = accheight + curheight;
463 var x4 = center - halfNextWidth;
464 var y4 = accheight + curheight;
467 * Subsequent segments
470 var x1 = center - halfCurWidth;
472 var x2 = center + halfCurWidth;
474 var x3 = center + halfNextWidth;
475 var y3 = accheight + curheight;
476 var x4 = center - halfNextWidth;
477 var y4 = accheight + curheight;
481 * Set the fill colour. If i is over 0 then don't use an offset
483 if (document.all && this.Get('chart.shadow')) {
484 this.DrawIEShadow([x1, y1, x2, y2, x3, y3, x4, y4], i > 0 && this.Get('chart.shadow.offsety') < 0);
487 context.strokeStyle = this.Get('chart.strokestyle');
488 context.fillStyle = this.Get('chart.colors')[i];
491 context.moveTo(x1, y1);
492 context.lineTo(x2, y2);
493 context.lineTo(x3, y3);
494 context.lineTo(x4, y4);
498 * Store the coordinates
500 this.coords.push([x1, y1, x2, y2, x3, y3, x4, y4]);
502 // The redrawing if the shadow is on will do the stroke
503 if (!this.Get('chart.shadow')) {
509 accheight += curheight;
513 * If the shadow is enabled, redraw every segment, in order to allow for shadows going upwards
515 if (this.Get('chart.shadow')) {
517 RGraph.NoShadow(this);
519 for (i=0; i<this.coords.length; ++i) {
521 context.strokeStyle = this.Get('chart.strokestyle');
522 context.fillStyle = this.Get('chart.colors')[i];
525 context.moveTo(this.coords[i][0], this.coords[i][1]);
526 context.lineTo(this.coords[i][2], this.coords[i][3]);
527 context.lineTo(this.coords[i][4], this.coords[i][5]);
528 context.lineTo(this.coords[i][6], this.coords[i][7]);
537 * Lastly, draw the key if necessary
539 if (this.Get('chart.key') && this.Get('chart.key').length) {
540 RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
547 RGraph.Funnel.prototype.DrawLabels = function ()
552 if (this.Get('chart.labels') && this.Get('chart.labels').length > 0) {
554 var context = this.context;
556 for (var j=0; j<this.coords.length; ++j) { // MUST be "j"
559 // Set the color back to black
560 context.strokeStyle = 'black';
561 context.fillStyle = this.Get('chart.text.color');
563 // Turn off any shadow
564 RGraph.NoShadow(this);
566 var label = this.Get('chart.labels')[j];
568 RGraph.Text(context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.Get('chart.text.halign') == 'left' ? 15 : this.canvas.width / 2, this.coords[j][1], label, 'center', this.Get('chart.text.halign') == 'left' ? 'left' : 'center', true, null, this.Get('chart.text.boxed') ? 'white' : null);
575 * This function is used by MSIE only to manually draw the shadow
577 * @param array coords The coords for the bar
579 RGraph.Funnel.prototype.DrawIEShadow = function (coords, noOffset)
581 var prevFillStyle = this.context.fillStyle;
582 var offsetx = this.Get('chart.shadow.offsetx');
583 var offsety = this.Get('chart.shadow.offsety');
584 var context = this.context;
586 context.lineWidth = 1;
587 context.fillStyle = this.Get('chart.shadow.color');
591 context.moveTo(coords[0] + (noOffset ? 0 : offsetx), coords[1] + (noOffset ? 0 : offsety));
592 context.lineTo(coords[2] + (noOffset ? 0 : offsetx), coords[3] + (noOffset ? 0 : offsety));
593 context.lineTo(coords[4] + (noOffset ? 0 : offsetx), coords[5] + (noOffset ? 0 : offsety));
594 context.lineTo(coords[6] + (noOffset ? 0 : offsetx), coords[7] + (noOffset ? 0 : offsety));
601 // Change the fillstyle back to what it was
602 this.context.fillStyle = prevFillStyle;