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 traditional radar chart constructor
20 * @param string id The ID of the canvas
21 * @param array data An array of data to represent
23 RGraph.Tradar = function (id, data)
26 this.canvas = document.getElementById(id);
27 this.context = this.canvas.getContext('2d');
28 this.canvas.__object__ = this;
29 this.size = null;// Set in the .Draw() method
31 this.max = RGraph.array_max(this.data);
38 * Compatibility with older browsers
40 RGraph.OldBrowserCompat(this.context);
48 'chart.circle.fill': 'red',
49 'chart.circle.stroke': 'black',
51 'chart.labels.offsetx': 10,
52 'chart.labels.offsety': 10,
53 'chart.background.circles': true,
54 'chart.text.size': 10,
55 'chart.text.font': 'Verdana',
56 'chart.text.color': 'black',
58 'chart.title.background': null,
59 'chart.title.hpos': null,
60 'chart.title.vpos': null,
61 'chart.title.color': 'black',
64 'chart.key.background': 'white',
65 'chart.key.position': 'gutter',
66 'chart.key.shadow': false,
67 'chart.key.shadow.color': '#666',
68 'chart.key.shadow.blur': 3,
69 'chart.key.shadow.offsetx': 2,
70 'chart.key.shadow.offsety': 2,
72 'chart.key.background': 'white',
73 'chart.key.position': 'gutter',
74 'chart.key.shadow': false,
75 'chart.key.shadow.color': '#666',
76 'chart.key.shadow.blur': 3,
77 'chart.key.shadow.offsetx': 2,
78 'chart.key.shadow.offsety': 2,
79 'chart.key.position.gutter.boxed': true,
80 'chart.key.position.x': null,
81 'chart.key.position.y': null,
82 'chart.key.color.shape': 'square',
83 'chart.key.rounded': true,
84 'chart.contextmenu': null,
85 'chart.annotatable': false,
86 'chart.annotate.color': 'black',
87 'chart.zoom.factor': 1.5,
88 'chart.zoom.fade.in': true,
89 'chart.zoom.fade.out': true,
90 'chart.zoom.hdir': 'right',
91 'chart.zoom.vdir': 'down',
92 'chart.zoom.frames': 10,
93 'chart.zoom.delay': 50,
94 'chart.zoom.shadow': true,
95 'chart.zoom.mode': 'canvas',
96 'chart.zoom.thumbnail.width': 75,
97 'chart.zoom.thumbnail.height': 75,
98 'chart.zoom.background': true,
99 'chart.zoom.action': 'zoom',
100 'chart.tooltips.effect': 'fade',
101 'chart.tooltips.css.class': 'RGraph_tooltip',
102 'chart.tooltips.highlight': true,
103 'chart.resizable': false,
104 'chart.labels.axes': 'nsew',
108 // Must have at least 3 points
109 if (this.data.length < 3) {
110 alert('[TRADAR] You must specify at least 3 data points');
114 // Check the common library has been included
115 if (typeof(RGraph) == 'undefined') {
116 alert('[TRADAR] Fatal error: The RGraph common library does not appear to have been included');
124 * @param string name The name of the property to set
125 * @param string value The value of the property
127 RGraph.Tradar.prototype.Set = function (name, value)
129 this.properties[name] = value;
132 * If the name is chart.color, set chart.colors too
134 if (name == 'chart.color') {
135 this.properties['chart.colors'] = [value];
143 * @param string name The name of the property to get
145 RGraph.Tradar.prototype.Get = function (name)
147 return this.properties[name];
152 * The draw method which does all the brunt of the work
154 RGraph.Tradar.prototype.Draw = function ()
157 * Fire the onbeforedraw event
159 RGraph.FireCustomEvent(this, 'onbeforedraw');
162 * Clear all of this canvases event handlers (the ones installed by RGraph)
164 RGraph.ClearEventListeners(this.id);
166 this.centerx = this.canvas.width / 2;
167 this.centery = this.canvas.height / 2;
168 this.size = Math.min(this.canvas.width, this.canvas.height) - (2 * this.Get('chart.gutter'));
170 // Work out the maximum value and the sum
171 if (!this.Get('chart.ymax')) {
172 this.scale = RGraph.getScale(RGraph.array_max(this.data), this);
173 this.max = this.scale[4];
175 var ymax = this.Get('chart.ymax');
184 this.max = this.scale[4];
187 this.DrawBackground();
190 this.DrawAxisLabels();
195 if (this.Get('chart.title')) {
196 RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.Get('chart.gutter'))
199 // Draw the key if necessary
201 if (this.Get('chart.key')) {
202 RGraph.DrawKey(this, this.Get('chart.key'), [this.Get('chart.color'), this.Get('chart.circle.fill')]);
206 * Show the context menu
208 if (this.Get('chart.contextmenu')) {
209 RGraph.ShowContext(this);
213 * If the canvas is annotatable, do install the event handlers
215 if (this.Get('chart.annotatable')) {
216 RGraph.Annotate(this);
220 * This bit shows the mini zoom window if requested
222 if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
223 RGraph.ShowZoomWindow(this);
228 * This function enables resizing
230 if (this.Get('chart.resizable')) {
231 RGraph.AllowResizing(this);
236 * This function enables adjusting
238 if (this.Get('chart.adjustable')) {
239 RGraph.AllowAdjusting(this);
243 * Fire the RGraph ondraw event
245 RGraph.FireCustomEvent(this, 'ondraw');
250 * Draws the background circles
252 RGraph.Tradar.prototype.DrawBackground = function ()
257 * Draws the background circles
259 if (this.Get('chart.background.circles')) {
261 this.context.strokeStyle = color;
262 this.context.beginPath();
264 for (var r=5; r<(this.size / 2); r+=15) {
266 this.context.moveTo(this.centerx, this.centery);
267 this.context.arc(this.centerx, this.centery,r, 0, 6.28, 0);
270 this.context.stroke();
277 this.context.strokeStyle = color;
278 for (var i=0; i<360; i+=15) {
279 this.context.beginPath();
280 this.context.arc(this.centerx, this.centery, this.size / 2, (i / 360) * (2 * Math.PI), ((i+0.01) / 360) * (2 * Math.PI), 0); // The 0.01 avoids a bug in Chrome 6
281 this.context.lineTo(this.centerx, this.centery);
282 this.context.stroke();
290 RGraph.Tradar.prototype.DrawAxes = function ()
292 this.context.strokeStyle = 'black';
294 var halfsize = this.size / 2;
296 this.context.beginPath();
301 this.context.moveTo(this.centerx, this.centery + halfsize);
302 this.context.lineTo(this.centerx, this.centery - halfsize);
305 // Draw the bits at either end of the Y axis
306 this.context.moveTo(this.centerx - 5, this.centery + halfsize);
307 this.context.lineTo(this.centerx + 5, this.centery + halfsize);
308 this.context.moveTo(this.centerx - 5, this.centery - halfsize);
309 this.context.lineTo(this.centerx + 5, this.centery - halfsize);
311 // Draw X axis tick marks
312 for (var y=(this.centery - halfsize); y<(this.centery + halfsize); y+=15) {
313 this.context.moveTo(this.centerx - 3, y);
314 this.context.lineTo(this.centerx + 3, y);
320 this.context.moveTo(this.centerx - halfsize, this.centery);
321 this.context.lineTo(this.centerx + halfsize, this.centery);
323 // Draw the bits at the end of the X axis
324 this.context.moveTo(this.centerx - halfsize, this.centery - 5);
325 this.context.lineTo(this.centerx - halfsize, this.centery + 5);
326 this.context.moveTo(this.centerx + halfsize, this.centery - 5);
327 this.context.lineTo(this.centerx + halfsize, this.centery + 5);
329 // Draw X axis tick marks
330 for (var x=(this.centerx - halfsize); x<(this.centerx + halfsize); x+=15) {
331 this.context.moveTo(x, this.centery - 3);
332 this.context.lineTo(x, this.centery + 3);
336 * Finally draw it to the canvas
338 this.context.stroke();
343 * The function which actually draws the radar chart
345 RGraph.Tradar.prototype.DrawChart = function ()
347 for (var i=0; i<this.data.length; ++i) {
348 this.coords[i] = this.GetCoordinates(i);
352 * Now go through the coords and draw the chart itself
354 this.context.strokeStyle = this.Get('chart.strokestyle');
355 this.context.fillStyle = this.Get('chart.color');
356 this.context.lineWidth = this.Get('chart.linewidth');
357 this.context.beginPath();
359 for (i=0; i<this.coords.length; ++i) {
361 this.context.moveTo(this.coords[i][0], this.coords[i][1]);
363 this.context.lineTo(this.coords[i][0], this.coords[i][1]);
367 this.context.closePath();
370 this.context.stroke();
373 * Can now handletooltips
375 if (this.Get('chart.tooltips')) {
377 RGraph.Register(this);
379 var canvas_onmousemove_func = function (e)
381 e = RGraph.FixEventObject(e);
383 var canvas = document.getElementById(this.id);
384 var obj = canvas.__object__;
387 var overHotspot = false;
389 for (var i=0; i<obj.coords.length; ++i) {
391 var xCoord = obj.coords[i][0];
392 var yCoord = obj.coords[i][1];
393 var tooltips = obj.Get('chart.tooltips');
397 (tooltips[i] || tooltips) // The order here is important due to short circuiting
404 if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__index__ != idx) {
407 * Get the tooltip text
409 if (typeof(obj.Get('chart.tooltips')) == 'function') {
410 var text = String(obj.Get('chart.tooltips')(i));
412 } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'function') {
413 var text = String(obj.Get('chart.tooltips')[i](i));
415 } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'string') {
416 var text = String(obj.Get('chart.tooltips')[i]);
422 if (typeof(text) == 'string' && text.length) {
425 obj.canvas.style.cursor = 'pointer';
427 RGraph.Clear(obj.canvas);
430 if (obj.Get('chart.tooltips.highlight')) {
431 obj.context.beginPath();
432 obj.context.strokeStyle = 'gray';
433 obj.context.fillStyle = 'white';
434 obj.context.arc(xCoord, yCoord, 2, 0, 6.28, 0);
436 obj.context.stroke();
439 RGraph.Tooltip(obj.canvas, text, e.pageX, e.pageY, idx);
441 } else if (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ == idx) {
443 obj.canvas.style.cursor = 'pointer';
449 obj.canvas.style.cursor = 'default';
452 this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
453 RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
459 * Gets the coordinates for a particular mark
461 * @param number i The index of the data (ie which one it is)
462 * @return array A two element array of the coordinates
464 RGraph.Tradar.prototype.GetCoordinates = function (i)
466 // The number of data points
467 var len = this.data.length;
469 // The magnitude of the data (NOT the x/y coords)
470 var mag = (this.data[i] / this.max) * (this.size / 2);
475 var angle = (6.28 / len) * i; // In radians
478 * Work out the X/Y coordinates
480 var x = Math.cos(angle) * mag;
481 var y = Math.sin(angle) * mag;
484 * Put the coordinate in the right quadrant
486 x = this.centerx + x;
487 y = this.centery + (i == 0 ? 0 : y);
494 * This function adds the labels to the chart
496 RGraph.Tradar.prototype.DrawLabels = function ()
498 var labels = this.Get('chart.labels');
500 if (labels && labels.length > 0) {
502 this.context.lineWidth = 1;
503 this.context.fillStyle = this.Get('chart.text.color');
505 var offsetx = this.Get('chart.labels.offsetx'); // Not used yet
506 var offsety = this.Get('chart.labels.offsety'); // Not used yet
508 for (var i=0; i<labels.length; ++i) {
510 var x = this.coords[i][0];
511 var y = this.coords[i][1];
512 var text = labels[i];
513 var hAlign = 'center';
514 var vAlign = 'center';
515 var quartile = (i / this.coords.length);
516 var offsetx = this.Get('chart.labels.offsetx');
517 var offsety = this.Get('chart.labels.offsety');
519 // ~Manually do labels on the right middle axis
527 hAlign = (x < this.centerx) ? 'right' : 'left';
528 vAlign = (y < this.centery) ? 'bottom' : 'top';
529 x += (x < this.centerx) ? (-1 * offsetx) : offsetx;
530 y += (y < this.centery) ? (-1 * offsety) : offsety;
532 if (i / this.data.length == 0.25) { x -= offsetx; hAlign = 'center';
533 } else if (i / this.data.length == 0.5) { y -= offsety; vAlign = 'center';
534 } else if (i / this.data.length == 0.75) { x += offsetx; hAlign = 'center'; }
537 // context, font, size, x, y, text
538 RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, text, vAlign, hAlign, true, null, 'white');
545 * Draws the circle. No arguments as it gets the information from the object properties.
547 RGraph.Tradar.prototype.DrawCircle = function ()
550 circle.limit = this.Get('chart.circle');
551 circle.fill = this.Get('chart.circle.fill');
552 circle.stroke = this.Get('chart.circle.stroke');
556 var r = (circle.limit / this.max) * (this.size / 2);
558 this.context.fillStyle = circle.fill;
559 this.context.strokeStyle = circle.stroke;
561 this.context.beginPath();
562 this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0);
564 this.context.stroke();
570 * Unsuprisingly, draws the labels
572 RGraph.Tradar.prototype.DrawAxisLabels = function ()
574 this.context.lineWidth = 1;
576 // Set the color to black
577 this.context.fillStyle = 'black';
578 this.context.strokeStyle = 'black';
580 var r = (this.size/ 2);
581 var font_face = this.Get('chart.text.font');
582 var font_size = this.Get('chart.text.size');
583 var context = this.context;
584 var axes = this.Get('chart.labels.axes').toLowerCase();
585 var color = 'rgba(255,255,255,0.8)';
587 // The "North" axis labels
588 if (axes.indexOf('n') > -1) {
589 RGraph.Text(context,font_face,font_size,this.centerx,this.centery - (r * 0.2),String(this.scale[0]),'center','center',true,false,color);
590 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
591 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
592 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
593 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color);
596 // The "South" axis labels
597 if (axes.indexOf('s') > -1) {
598 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.2), String(this.scale[0]), 'center', 'center', true, false, color);
599 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
600 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
601 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
602 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color);
605 // The "East" axis labels
606 if (axes.indexOf('e') > -1) {
607 RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
608 RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
609 RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
610 RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
611 RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
614 // The "West" axis labels
615 if (axes.indexOf('w') > -1) {
616 RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
617 RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
618 RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
619 RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
620 RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
623 RGraph.Text(context, font_face, font_size, this.centerx, this.centery, '0', 'center', 'center', true, false, color);