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 pie chart constructor |
---|
19 | * |
---|
20 | * @param data array The data to be represented on the pie chart |
---|
21 | */ |
---|
22 | RGraph.Pie = function (id, data) |
---|
23 | { |
---|
24 | this.id = id; |
---|
25 | this.canvas = document.getElementById(id); |
---|
26 | this.context = this.canvas.getContext("2d"); |
---|
27 | this.canvas.__object__ = this; |
---|
28 | this.total = 0; |
---|
29 | this.subTotal = 0; |
---|
30 | this.angles = []; |
---|
31 | this.data = data; |
---|
32 | this.properties = []; |
---|
33 | this.type = 'pie'; |
---|
34 | this.isRGraph = true; |
---|
35 | |
---|
36 | |
---|
37 | /** |
---|
38 | * Compatibility with older browsers |
---|
39 | */ |
---|
40 | RGraph.OldBrowserCompat(this.context); |
---|
41 | |
---|
42 | this.properties = { |
---|
43 | 'chart.colors': ['rgb(255,0,0)', '#ddd', 'rgb(0,255,0)', 'rgb(0,0,255)', 'pink', 'yellow', '#000'], |
---|
44 | 'chart.strokestyle': '#999', |
---|
45 | 'chart.linewidth': 1, |
---|
46 | 'chart.labels': [], |
---|
47 | 'chart.labels.sticks': false, |
---|
48 | 'chart.labels.sticks.color': '#aaa', |
---|
49 | 'chart.segments': [], |
---|
50 | 'chart.gutter.left': 25, |
---|
51 | 'chart.gutter.right': 25, |
---|
52 | 'chart.gutter.top': 25, |
---|
53 | 'chart.gutter.bottom': 25, |
---|
54 | 'chart.title': '', |
---|
55 | 'chart.title.background': null, |
---|
56 | 'chart.title.hpos': null, |
---|
57 | 'chart.title.vpos': null, |
---|
58 | 'chart.shadow': false, |
---|
59 | 'chart.shadow.color': 'rgba(0,0,0,0.5)', |
---|
60 | 'chart.shadow.offsetx': 3, |
---|
61 | 'chart.shadow.offsety': 3, |
---|
62 | 'chart.shadow.blur': 3, |
---|
63 | 'chart.text.size': 10, |
---|
64 | 'chart.text.color': 'black', |
---|
65 | 'chart.text.font': 'Verdana', |
---|
66 | 'chart.contextmenu': null, |
---|
67 | 'chart.tooltips': [], |
---|
68 | 'chart.tooltips.event': 'onclick', |
---|
69 | 'chart.tooltips.effect': 'fade', |
---|
70 | 'chart.tooltips.css.class': 'RGraph_tooltip', |
---|
71 | 'chart.tooltips.highlight': true, |
---|
72 | 'chart.highlight.style': '3d', |
---|
73 | 'chart.highlight.style.2d.fill': 'rgba(255,255,255,0.5)', |
---|
74 | 'chart.highlight.style.2d.stroke': 'rgba(255,255,255,0)', |
---|
75 | 'chart.radius': null, |
---|
76 | 'chart.border': false, |
---|
77 | 'chart.border.color': 'rgba(255,255,255,0.5)', |
---|
78 | 'chart.key': null, |
---|
79 | 'chart.key.background': 'white', |
---|
80 | 'chart.key.position': 'graph', |
---|
81 | 'chart.key.halign': 'right', |
---|
82 | 'chart.key.shadow': false, |
---|
83 | 'chart.key.shadow.color': '#666', |
---|
84 | 'chart.key.shadow.blur': 3, |
---|
85 | 'chart.key.shadow.offsetx': 2, |
---|
86 | 'chart.key.shadow.offsety': 2, |
---|
87 | 'chart.key.position.gutter.boxed': true, |
---|
88 | 'chart.key.position.x': null, |
---|
89 | 'chart.key.position.y': null, |
---|
90 | 'chart.key.color.shape': 'square', |
---|
91 | 'chart.key.rounded': true, |
---|
92 | 'chart.key.linewidth': 1, |
---|
93 | 'chart.annotatable': false, |
---|
94 | 'chart.annotate.color': 'black', |
---|
95 | 'chart.align': 'center', |
---|
96 | 'chart.zoom.factor': 1.5, |
---|
97 | 'chart.zoom.fade.in': true, |
---|
98 | 'chart.zoom.fade.out': true, |
---|
99 | 'chart.zoom.hdir': 'right', |
---|
100 | 'chart.zoom.vdir': 'down', |
---|
101 | 'chart.zoom.frames': 10, |
---|
102 | 'chart.zoom.delay': 50, |
---|
103 | 'chart.zoom.shadow': true, |
---|
104 | 'chart.zoom.mode': 'canvas', |
---|
105 | 'chart.zoom.thumbnail.width': 75, |
---|
106 | 'chart.zoom.thumbnail.height': 75, |
---|
107 | 'chart.zoom.background': true, |
---|
108 | 'chart.zoom.action': 'zoom', |
---|
109 | 'chart.resizable': false, |
---|
110 | 'chart.resize.handle.adjust': [0,0], |
---|
111 | 'chart.resize.handle.background': null, |
---|
112 | 'chart.variant': 'pie', |
---|
113 | 'chart.variant.donut.color': 'white', |
---|
114 | 'chart.exploded': [] |
---|
115 | } |
---|
116 | |
---|
117 | /** |
---|
118 | * Calculate the total |
---|
119 | */ |
---|
120 | for (var i=0,len=data.length; i<len; i++) { |
---|
121 | this.total += data[i]; |
---|
122 | } |
---|
123 | |
---|
124 | |
---|
125 | /** |
---|
126 | * Set the .getShape commonly named method |
---|
127 | */ |
---|
128 | this.getShape = this.getSegment; |
---|
129 | } |
---|
130 | |
---|
131 | |
---|
132 | /** |
---|
133 | * A generic setter |
---|
134 | */ |
---|
135 | RGraph.Pie.prototype.Set = function (name, value) |
---|
136 | { |
---|
137 | if (name == 'chart.highlight.style.2d.color') { |
---|
138 | name = 'chart.highlight.style.2d.fill'; |
---|
139 | } |
---|
140 | |
---|
141 | this.properties[name] = value; |
---|
142 | } |
---|
143 | |
---|
144 | |
---|
145 | /** |
---|
146 | * A generic getter |
---|
147 | */ |
---|
148 | RGraph.Pie.prototype.Get = function (name) |
---|
149 | { |
---|
150 | if (name == 'chart.highlight.style.2d.color') { |
---|
151 | name = 'chart.highlight.style.2d.fill'; |
---|
152 | } |
---|
153 | |
---|
154 | return this.properties[name]; |
---|
155 | } |
---|
156 | |
---|
157 | |
---|
158 | /** |
---|
159 | * This draws the pie chart |
---|
160 | */ |
---|
161 | RGraph.Pie.prototype.Draw = function () |
---|
162 | { |
---|
163 | /** |
---|
164 | * Fire the onbeforedraw event |
---|
165 | */ |
---|
166 | RGraph.FireCustomEvent(this, 'onbeforedraw'); |
---|
167 | |
---|
168 | |
---|
169 | /** |
---|
170 | * This bit of code converts chart.exploded from an integer to an array |
---|
171 | * that consists of that value, repeated n times, where n is equal to |
---|
172 | * the number of segments that you have |
---|
173 | */ |
---|
174 | var explosion = this.Get('chart.exploded'); |
---|
175 | if (typeof(explosion) == 'number') { |
---|
176 | this.Set('chart.exploded', RGraph.array_pad([], this.data.length, explosion)); |
---|
177 | } |
---|
178 | |
---|
179 | /** |
---|
180 | * This is new in May 2011 and facilitates indiviual gutter settings, |
---|
181 | * eg chart.gutter.left |
---|
182 | */ |
---|
183 | this.gutterLeft = this.Get('chart.gutter.left'); |
---|
184 | this.gutterRight = this.Get('chart.gutter.right'); |
---|
185 | this.gutterTop = this.Get('chart.gutter.top'); |
---|
186 | this.gutterBottom = this.Get('chart.gutter.bottom'); |
---|
187 | |
---|
188 | /** |
---|
189 | * Reset this to an empty array |
---|
190 | */ |
---|
191 | this.Set('chart.segments', []); |
---|
192 | |
---|
193 | /** |
---|
194 | * Clear all of this canvases event handlers (the ones installed by RGraph) |
---|
195 | */ |
---|
196 | RGraph.ClearEventListeners(this.id); |
---|
197 | |
---|
198 | |
---|
199 | this.diameter = Math.min(RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom, RGraph.GetWidth(this)) - this.gutterLeft - this.gutterRight; |
---|
200 | this.radius = this.Get('chart.radius') ? this.Get('chart.radius') : this.diameter / 2; |
---|
201 | // this.centerx now defined below |
---|
202 | this.centery = ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; |
---|
203 | this.subTotal = 0; |
---|
204 | this.angles = []; |
---|
205 | |
---|
206 | /** |
---|
207 | * Alignment (Pie is center aligned by default) Only if centerx is not defined - donut defines the centerx |
---|
208 | */ |
---|
209 | if (this.Get('chart.align') == 'left') { |
---|
210 | this.centerx = this.radius + this.gutterLeft; |
---|
211 | |
---|
212 | } else if (this.Get('chart.align') == 'right') { |
---|
213 | this.centerx = RGraph.GetWidth(this) - this.radius - this.gutterRight; |
---|
214 | |
---|
215 | } else { |
---|
216 | this.centerx = RGraph.GetWidth(this) / 2; |
---|
217 | } |
---|
218 | |
---|
219 | /** |
---|
220 | * Draw the shadow if required |
---|
221 | */ |
---|
222 | if (this.Get('chart.shadow') && 0) { |
---|
223 | |
---|
224 | var offsetx = document.all ? this.Get('chart.shadow.offsetx') : 0; |
---|
225 | var offsety = document.all ? this.Get('chart.shadow.offsety') : 0; |
---|
226 | |
---|
227 | this.context.beginPath(); |
---|
228 | this.context.fillStyle = this.Get('chart.shadow.color'); |
---|
229 | |
---|
230 | this.context.shadowColor = this.Get('chart.shadow.color'); |
---|
231 | this.context.shadowBlur = this.Get('chart.shadow.blur'); |
---|
232 | this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); |
---|
233 | this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); |
---|
234 | |
---|
235 | this.context.arc(this.centerx + offsetx, this.centery + offsety, this.radius, 0, 6.28, 0); |
---|
236 | |
---|
237 | this.context.fill(); |
---|
238 | |
---|
239 | // Now turn off the shadow |
---|
240 | RGraph.NoShadow(this); |
---|
241 | } |
---|
242 | |
---|
243 | /** |
---|
244 | * The total of the array of values |
---|
245 | */ |
---|
246 | this.total = RGraph.array_sum(this.data); |
---|
247 | |
---|
248 | for (var i=0,len=this.data.length; i<len; i++) { |
---|
249 | var angle = (this.data[i] / this.total) * 360; |
---|
250 | |
---|
251 | // Draw the segment |
---|
252 | this.DrawSegment(angle,this.Get('chart.colors')[i],i == (this.data.length - 1), i); |
---|
253 | } |
---|
254 | |
---|
255 | RGraph.NoShadow(this); |
---|
256 | |
---|
257 | |
---|
258 | /** |
---|
259 | * Redraw the seperating lines |
---|
260 | */ |
---|
261 | this.DrawBorders(); |
---|
262 | |
---|
263 | /** |
---|
264 | * Now draw the segments again with shadow turned off. This is always performed, |
---|
265 | * not just if the shadow is on. |
---|
266 | */ |
---|
267 | for (var i=0; i<this.angles.length; i++) { |
---|
268 | |
---|
269 | this.context.beginPath(); |
---|
270 | this.context.strokeStyle = this.Get('chart.strokestyle'); |
---|
271 | this.context.fillStyle = this.Get('chart.colors')[i]; |
---|
272 | this.context.moveTo(this.angles[i][2], this.angles[i][3]); |
---|
273 | this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, this.angles[i][1] / 57.3, false); |
---|
274 | this.context.lineTo(this.angles[i][2], this.angles[i][3]); |
---|
275 | this.context.closePath(); |
---|
276 | this.context.fill(); |
---|
277 | this.context.stroke(); |
---|
278 | } |
---|
279 | |
---|
280 | /** |
---|
281 | * Draw label sticks |
---|
282 | */ |
---|
283 | if (this.Get('chart.labels.sticks')) { |
---|
284 | this.DrawSticks(); |
---|
285 | |
---|
286 | // Redraw the border going around the Pie chart if the stroke style is NOT white |
---|
287 | if ( |
---|
288 | this.Get('chart.strokestyle') != 'white' |
---|
289 | && this.Get('chart.strokestyle') != '#fff' |
---|
290 | && this.Get('chart.strokestyle') != '#fffffff' |
---|
291 | && this.Get('chart.strokestyle') != 'rgb(255,255,255)' |
---|
292 | && this.Get('chart.strokestyle') != 'rgba(255,255,255,0)' |
---|
293 | ) { |
---|
294 | |
---|
295 | // Again (?) |
---|
296 | this.DrawBorders(); |
---|
297 | } |
---|
298 | } |
---|
299 | |
---|
300 | /** |
---|
301 | * Draw the labels |
---|
302 | */ |
---|
303 | this.DrawLabels(); |
---|
304 | |
---|
305 | /** |
---|
306 | * Draw the title |
---|
307 | */ |
---|
308 | if (this.Get('chart.align') == 'left') { |
---|
309 | var centerx = this.radius + this.Get('chart.gutter.left'); |
---|
310 | |
---|
311 | } else if (this.Get('chart.align') == 'right') { |
---|
312 | var centerx = RGraph.GetWidth(this) - (this.radius + this.gutterRight); |
---|
313 | |
---|
314 | } else { |
---|
315 | var centerx = null; |
---|
316 | } |
---|
317 | |
---|
318 | RGraph.DrawTitle(this.canvas, this.Get('chart.title'), (this.canvas.height / 2) - this.radius - 5, centerx, this.Get('chart.text.size') + 2); |
---|
319 | |
---|
320 | |
---|
321 | /** |
---|
322 | * Setup the context menu if required |
---|
323 | */ |
---|
324 | if (this.Get('chart.contextmenu')) { |
---|
325 | RGraph.ShowContext(this); |
---|
326 | } |
---|
327 | |
---|
328 | /** |
---|
329 | * Tooltips |
---|
330 | */ |
---|
331 | if (this.Get('chart.tooltips').length) { |
---|
332 | |
---|
333 | /** |
---|
334 | * Register this object for redrawing |
---|
335 | */ |
---|
336 | RGraph.Register(this); |
---|
337 | |
---|
338 | /** |
---|
339 | * The onclick event |
---|
340 | */ |
---|
341 | //this.canvas.onclick = function (e) |
---|
342 | var canvas_onclick_func = function (e) |
---|
343 | { |
---|
344 | RGraph.HideZoomedCanvas(); |
---|
345 | |
---|
346 | e = RGraph.FixEventObject(e); |
---|
347 | |
---|
348 | var mouseCoords = RGraph.getMouseXY(e); |
---|
349 | |
---|
350 | var canvas = e.target; |
---|
351 | var context = canvas.getContext('2d'); |
---|
352 | var obj = e.target.__object__; |
---|
353 | |
---|
354 | |
---|
355 | |
---|
356 | /** |
---|
357 | * If it's actually a donut make sure the hyp is bigger |
---|
358 | * than the size of the hole in the middle |
---|
359 | */ |
---|
360 | if (obj.Get('chart.variant') == 'donut' && Math.abs(hyp) < (obj.radius / 2)) { |
---|
361 | return; |
---|
362 | } |
---|
363 | |
---|
364 | /** |
---|
365 | * The angles for each segment are stored in "angles", |
---|
366 | * so go through that checking if the mouse position corresponds |
---|
367 | */ |
---|
368 | var isDonut = obj.Get('chart.variant') == 'donut'; |
---|
369 | var hStyle = obj.Get('chart.highlight.style'); |
---|
370 | var segment = obj.getSegment(e); |
---|
371 | |
---|
372 | if (segment) { |
---|
373 | |
---|
374 | var x = mouseCoords[0] - segment[0]; |
---|
375 | var y = mouseCoords[1] - segment[1]; |
---|
376 | var theta = Math.atan(y / x); // RADIANS |
---|
377 | var hyp = y / Math.sin(theta); |
---|
378 | |
---|
379 | |
---|
380 | if (RGraph.Registry.Get('chart.tooltip') && segment[5] == RGraph.Registry.Get('chart.tooltip').__index__) { |
---|
381 | return; |
---|
382 | } else { |
---|
383 | RGraph.Redraw(); |
---|
384 | } |
---|
385 | |
---|
386 | |
---|
387 | if (isDonut || hStyle == '2d') { |
---|
388 | |
---|
389 | context.beginPath(); |
---|
390 | |
---|
391 | context.strokeStyle = obj.Get('chart.highlight.style.2d.stroke'); |
---|
392 | context.fillStyle = obj.Get('chart.highlight.style.2d.fill'); |
---|
393 | |
---|
394 | //context.moveTo(obj.centerx, obj.centery); |
---|
395 | |
---|
396 | context.moveTo(segment[0], segment[1]); |
---|
397 | context.arc(segment[0], segment[1], segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0); |
---|
398 | context.lineTo(segment[0], segment[1]); |
---|
399 | context.closePath(); |
---|
400 | |
---|
401 | context.stroke(); |
---|
402 | context.fill(); |
---|
403 | |
---|
404 | //Removed 7th December 2010 |
---|
405 | //context.stroke(); |
---|
406 | |
---|
407 | } else if (hStyle == 'explode') { |
---|
408 | |
---|
409 | var exploded = []; |
---|
410 | |
---|
411 | exploded[segment[5]] = 0; |
---|
412 | |
---|
413 | RGraph.Registry.Set('chart.pie.exploded', obj); |
---|
414 | |
---|
415 | setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 25); |
---|
416 | setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 50); |
---|
417 | setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 75); |
---|
418 | setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 100); |
---|
419 | setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 125); |
---|
420 | |
---|
421 | setTimeout(function () {RGraph.Registry.Get('chart.pie.exploded').Set('chart.exploded', []);}, 150); |
---|
422 | |
---|
423 | } else { |
---|
424 | |
---|
425 | context.lineWidth = 2; |
---|
426 | |
---|
427 | /** |
---|
428 | * Draw a white segment where the one that has been clicked on was |
---|
429 | */ |
---|
430 | context.fillStyle = 'white'; |
---|
431 | context.strokeStyle = 'white'; |
---|
432 | context.beginPath(); |
---|
433 | context.moveTo(segment[0], segment[1]); |
---|
434 | context.arc(segment[0], segment[1], segment[2], obj.angles[segment[5]][0] / 57.3, obj.angles[segment[5]][1] / 57.3, 0); |
---|
435 | context.stroke(); |
---|
436 | context.fill(); |
---|
437 | |
---|
438 | context.lineWidth = 1; |
---|
439 | |
---|
440 | context.shadowColor = '#666'; |
---|
441 | context.shadowBlur = 3; |
---|
442 | context.shadowOffsetX = 3; |
---|
443 | context.shadowOffsetY = 3; |
---|
444 | |
---|
445 | // Draw the new segment |
---|
446 | context.beginPath(); |
---|
447 | context.fillStyle = obj.Get('chart.colors')[segment[5]]; |
---|
448 | context.strokeStyle = obj.Get('chart.strokestyle'); |
---|
449 | context.moveTo(segment[0] - 3, segment[1] - 3); |
---|
450 | context.arc(segment[0] - 3, segment[1] - 3, segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0); |
---|
451 | context.lineTo(segment[0] - 3, segment[1] - 3); |
---|
452 | context.closePath(); |
---|
453 | |
---|
454 | context.stroke(); |
---|
455 | context.fill(); |
---|
456 | |
---|
457 | // Turn off the shadow |
---|
458 | RGraph.NoShadow(obj); |
---|
459 | |
---|
460 | /** |
---|
461 | * If a border is defined, redraw that |
---|
462 | */ |
---|
463 | if (obj.Get('chart.border')) { |
---|
464 | context.beginPath(); |
---|
465 | context.strokeStyle = obj.Get('chart.border.color'); |
---|
466 | context.lineWidth = 5; |
---|
467 | context.arc(segment[0] - 3, segment[1] - 3, obj.radius - 2, RGraph.degrees2Radians(obj.angles[i][0]), RGraph.degrees2Radians(obj.angles[i][1]), 0); |
---|
468 | context.stroke(); |
---|
469 | } |
---|
470 | } |
---|
471 | |
---|
472 | /** |
---|
473 | * If a tooltip is defined, show it |
---|
474 | */ |
---|
475 | |
---|
476 | /** |
---|
477 | * Get the tooltip text |
---|
478 | */ |
---|
479 | if (typeof(obj.Get('chart.tooltips')) == 'function') { |
---|
480 | var text = String(obj.Get('chart.tooltips')(segment[5])); |
---|
481 | |
---|
482 | } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[5]]) == 'function') { |
---|
483 | var text = String(obj.Get('chart.tooltips')[segment[5]](segment[5])); |
---|
484 | |
---|
485 | } else if (typeof(obj.Get('chart.tooltips')) == 'object') { |
---|
486 | var text = String(obj.Get('chart.tooltips')[segment[5]]); |
---|
487 | |
---|
488 | } else { |
---|
489 | var text = ''; |
---|
490 | } |
---|
491 | |
---|
492 | if (text) { |
---|
493 | RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[5]); |
---|
494 | } |
---|
495 | |
---|
496 | /** |
---|
497 | * Need to redraw the key? |
---|
498 | */ |
---|
499 | if (obj.Get('chart.key') && obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') { |
---|
500 | RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors')); |
---|
501 | } |
---|
502 | |
---|
503 | e.stopPropagation(); |
---|
504 | |
---|
505 | return; |
---|
506 | } else if (obj.Get('chart.tooltips.event') == 'onclick') { |
---|
507 | RGraph.Redraw(); |
---|
508 | } |
---|
509 | } |
---|
510 | var event_name = this.Get('chart.tooltips.event') == 'onmousemove' ? 'mousemove' : 'click'; |
---|
511 | |
---|
512 | this.canvas.addEventListener(event_name, canvas_onclick_func, false); |
---|
513 | RGraph.AddEventListener(this.id, event_name, canvas_onclick_func); |
---|
514 | |
---|
515 | |
---|
516 | |
---|
517 | |
---|
518 | |
---|
519 | |
---|
520 | /** |
---|
521 | * The onmousemove event for changing the cursor |
---|
522 | */ |
---|
523 | //this.canvas.onmousemove = function (e) |
---|
524 | var canvas_onmousemove_func = function (e) |
---|
525 | { |
---|
526 | RGraph.HideZoomedCanvas(); |
---|
527 | |
---|
528 | e = RGraph.FixEventObject(e); |
---|
529 | |
---|
530 | var obj = e.target.__object__; |
---|
531 | var segment = obj.getSegment(e); |
---|
532 | |
---|
533 | if (segment) { |
---|
534 | e.target.style.cursor = 'pointer'; |
---|
535 | |
---|
536 | return; |
---|
537 | } |
---|
538 | |
---|
539 | /** |
---|
540 | * Put the cursor back to null |
---|
541 | */ |
---|
542 | e.target.style.cursor = 'default'; |
---|
543 | } |
---|
544 | this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); |
---|
545 | RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); |
---|
546 | |
---|
547 | |
---|
548 | |
---|
549 | |
---|
550 | |
---|
551 | |
---|
552 | |
---|
553 | |
---|
554 | |
---|
555 | /** |
---|
556 | * The window onclick function |
---|
557 | */ |
---|
558 | var window_onclick_func = function (e) |
---|
559 | { |
---|
560 | RGraph.HideZoomedCanvas(); |
---|
561 | |
---|
562 | e = RGraph.FixEventObject(e); |
---|
563 | |
---|
564 | RGraph.Redraw(); |
---|
565 | |
---|
566 | /** |
---|
567 | * Put the cursor back to null |
---|
568 | */ |
---|
569 | e.target.style.cursor = 'default'; |
---|
570 | } |
---|
571 | window.addEventListener('click', window_onclick_func, false); |
---|
572 | RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func); |
---|
573 | } |
---|
574 | |
---|
575 | |
---|
576 | /** |
---|
577 | * If a border is pecified, draw it |
---|
578 | */ |
---|
579 | if (this.Get('chart.border')) { |
---|
580 | this.context.beginPath(); |
---|
581 | this.context.lineWidth = 5; |
---|
582 | this.context.strokeStyle = this.Get('chart.border.color'); |
---|
583 | |
---|
584 | this.context.arc(this.centerx, |
---|
585 | this.centery, |
---|
586 | this.radius - 2, |
---|
587 | 0, |
---|
588 | 6.28, |
---|
589 | 0); |
---|
590 | |
---|
591 | this.context.stroke(); |
---|
592 | } |
---|
593 | |
---|
594 | /** |
---|
595 | * Draw the kay if desired |
---|
596 | */ |
---|
597 | if (this.Get('chart.key') != null) { |
---|
598 | //this.Set('chart.key.position', 'graph'); |
---|
599 | RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); |
---|
600 | } |
---|
601 | |
---|
602 | |
---|
603 | /** |
---|
604 | * If this is actually a donut, draw a big circle in the middle |
---|
605 | */ |
---|
606 | if (this.Get('chart.variant') == 'donut') { |
---|
607 | this.context.beginPath(); |
---|
608 | this.context.strokeStyle = this.Get('chart.strokestyle'); |
---|
609 | this.context.fillStyle = this.Get('chart.variant.donut.color'); |
---|
610 | this.context.arc(this.centerx, this.centery, this.radius / 2, 0, 6.28, 0); |
---|
611 | this.context.stroke(); |
---|
612 | this.context.fill(); |
---|
613 | } |
---|
614 | |
---|
615 | RGraph.NoShadow(this); |
---|
616 | |
---|
617 | /** |
---|
618 | * If the canvas is annotatable, do install the event handlers |
---|
619 | */ |
---|
620 | if (this.Get('chart.annotatable')) { |
---|
621 | RGraph.Annotate(this); |
---|
622 | } |
---|
623 | |
---|
624 | /** |
---|
625 | * This bit shows the mini zoom window if requested |
---|
626 | */ |
---|
627 | if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { |
---|
628 | RGraph.ShowZoomWindow(this); |
---|
629 | } |
---|
630 | |
---|
631 | |
---|
632 | /** |
---|
633 | * This function enables resizing |
---|
634 | */ |
---|
635 | if (this.Get('chart.resizable')) { |
---|
636 | RGraph.AllowResizing(this); |
---|
637 | } |
---|
638 | |
---|
639 | /** |
---|
640 | * Fire the RGraph ondraw event |
---|
641 | */ |
---|
642 | RGraph.FireCustomEvent(this, 'ondraw'); |
---|
643 | } |
---|
644 | |
---|
645 | |
---|
646 | /** |
---|
647 | * Draws a single segment of the pie chart |
---|
648 | * |
---|
649 | * @param int degrees The number of degrees for this segment |
---|
650 | */ |
---|
651 | RGraph.Pie.prototype.DrawSegment = function (degrees, color, last, index) |
---|
652 | { |
---|
653 | var context = this.context; |
---|
654 | var canvas = this.canvas; |
---|
655 | var subTotal = this.subTotal; |
---|
656 | |
---|
657 | context.beginPath(); |
---|
658 | |
---|
659 | context.fillStyle = color; |
---|
660 | context.strokeStyle = this.Get('chart.strokestyle'); |
---|
661 | context.lineWidth = 0; |
---|
662 | |
---|
663 | if (this.Get('chart.shadow')) { |
---|
664 | RGraph.SetShadow(this, this.Get('chart.shadow.color'),this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); |
---|
665 | } |
---|
666 | |
---|
667 | /** |
---|
668 | * Exploded segments |
---|
669 | */ |
---|
670 | if ( (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[index] > 0)) { |
---|
671 | var explosion = this.Get('chart.exploded')[index]; |
---|
672 | var x = 0; |
---|
673 | var y = 0; |
---|
674 | var h = explosion; |
---|
675 | var t = (subTotal + (degrees / 2)) / (360/6.2830); |
---|
676 | var x = (Math.cos(t) * explosion); |
---|
677 | var y = (Math.sin(t) * explosion); |
---|
678 | |
---|
679 | this.context.moveTo(this.centerx + x, this.centery + y); |
---|
680 | } else { |
---|
681 | var x = 0; |
---|
682 | var y = 0; |
---|
683 | } |
---|
684 | |
---|
685 | context.arc(this.centerx + x, |
---|
686 | this.centery + y, |
---|
687 | this.radius, |
---|
688 | subTotal / 57.3, |
---|
689 | (last ? 360 : subTotal + degrees) / 57.3, |
---|
690 | 0); |
---|
691 | |
---|
692 | context.lineTo(this.centerx + x, this.centery + y); |
---|
693 | |
---|
694 | // Keep hold of the angles |
---|
695 | this.angles.push([subTotal, subTotal + degrees, this.centerx + x, this.centery + y]) |
---|
696 | this.context.closePath(); |
---|
697 | |
---|
698 | |
---|
699 | |
---|
700 | //this.context.stroke(); |
---|
701 | this.context.fill(); |
---|
702 | |
---|
703 | /** |
---|
704 | * Calculate the segment angle |
---|
705 | */ |
---|
706 | this.Get('chart.segments').push([subTotal, subTotal + degrees]); |
---|
707 | this.subTotal += degrees; |
---|
708 | } |
---|
709 | |
---|
710 | /** |
---|
711 | * Draws the graphs labels |
---|
712 | */ |
---|
713 | RGraph.Pie.prototype.DrawLabels = function () |
---|
714 | { |
---|
715 | var hAlignment = 'left'; |
---|
716 | var vAlignment = 'center'; |
---|
717 | var labels = this.Get('chart.labels'); |
---|
718 | var context = this.context; |
---|
719 | |
---|
720 | /** |
---|
721 | * Turn the shadow off |
---|
722 | */ |
---|
723 | RGraph.NoShadow(this); |
---|
724 | |
---|
725 | context.fillStyle = 'black'; |
---|
726 | context.beginPath(); |
---|
727 | |
---|
728 | /** |
---|
729 | * Draw the key (ie. the labels) |
---|
730 | */ |
---|
731 | if (labels && labels.length) { |
---|
732 | |
---|
733 | var text_size = this.Get('chart.text.size'); |
---|
734 | |
---|
735 | for (i=0; i<labels.length; ++i) { |
---|
736 | |
---|
737 | /** |
---|
738 | * T|his ensures that if we're given too many labels, that we don't get an error |
---|
739 | */ |
---|
740 | if (typeof(this.Get('chart.segments')[i]) == 'undefined') { |
---|
741 | continue; |
---|
742 | } |
---|
743 | |
---|
744 | // Move to the centre |
---|
745 | context.moveTo(this.centerx,this.centery); |
---|
746 | |
---|
747 | var a = this.Get('chart.segments')[i][0] + ((this.Get('chart.segments')[i][1] - this.Get('chart.segments')[i][0]) / 2); |
---|
748 | |
---|
749 | /** |
---|
750 | * Alignment |
---|
751 | */ |
---|
752 | if (a < 90) { |
---|
753 | hAlignment = 'left'; |
---|
754 | vAlignment = 'center'; |
---|
755 | } else if (a < 180) { |
---|
756 | hAlignment = 'right'; |
---|
757 | vAlignment = 'center'; |
---|
758 | } else if (a < 270) { |
---|
759 | hAlignment = 'right'; |
---|
760 | vAlignment = 'center'; |
---|
761 | } else if (a < 360) { |
---|
762 | hAlignment = 'left'; |
---|
763 | vAlignment = 'center'; |
---|
764 | } |
---|
765 | |
---|
766 | |
---|
767 | /** |
---|
768 | * Handle the additional "explosion" offset |
---|
769 | */ |
---|
770 | if (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[i]) { |
---|
771 | |
---|
772 | var t = ((this.angles[i][1] - this.angles[i][0]) / 2) / (360/6.2830); |
---|
773 | var seperation = this.Get('chart.exploded')[i]; |
---|
774 | var angle = ((this.angles[i][1] - this.angles[i][0]) / 2) + this.angles[i][0]; |
---|
775 | |
---|
776 | // Adjust the angles |
---|
777 | var explosion_offsetx = (Math.cos(angle / 57.29) * seperation); |
---|
778 | var explosion_offsety = (Math.sin(angle / 57.29) * seperation); |
---|
779 | } else { |
---|
780 | var explosion_offsetx = 0; |
---|
781 | var explosion_offsety = 0; |
---|
782 | } |
---|
783 | |
---|
784 | context.fillStyle = this.Get('chart.text.color'); |
---|
785 | |
---|
786 | RGraph.Text(context, |
---|
787 | this.Get('chart.text.font'), |
---|
788 | text_size, |
---|
789 | this.centerx + explosion_offsetx + ((this.radius + 10)* Math.cos(a / 57.3)) + (this.Get('chart.labels.sticks') ? (a < 90 || a > 270 ? 2 : -2) : 0), |
---|
790 | this.centery + explosion_offsety + (((this.radius + 10) * Math.sin(a / 57.3))), |
---|
791 | labels[i], |
---|
792 | vAlignment, |
---|
793 | hAlignment); |
---|
794 | } |
---|
795 | |
---|
796 | context.fill(); |
---|
797 | } |
---|
798 | } |
---|
799 | |
---|
800 | |
---|
801 | /** |
---|
802 | * This function draws the pie chart sticks (for the labels) |
---|
803 | */ |
---|
804 | RGraph.Pie.prototype.DrawSticks = function () |
---|
805 | { |
---|
806 | var context = this.context; |
---|
807 | var segments = this.Get('chart.segments'); |
---|
808 | var offset = this.Get('chart.linewidth') / 2; |
---|
809 | var exploded = this.Get('chart.exploded'); |
---|
810 | var sticks = this.Get('chart.labels.sticks'); |
---|
811 | |
---|
812 | for (var i=0; i<segments.length; ++i) { |
---|
813 | |
---|
814 | // This allows the chart.labels.sticks to be an array as well as a boolean |
---|
815 | if (typeof(sticks) == 'object' && !sticks[i]) { |
---|
816 | continue; |
---|
817 | } |
---|
818 | |
---|
819 | var degrees = segments[i][1] - segments[i][0]; |
---|
820 | |
---|
821 | context.beginPath(); |
---|
822 | context.strokeStyle = this.Get('chart.labels.sticks.color'); |
---|
823 | context.lineWidth = 1; |
---|
824 | |
---|
825 | var midpoint = (segments[i][0] + (degrees / 2)) / 57.3; |
---|
826 | |
---|
827 | if (exploded && exploded[i]) { |
---|
828 | var extra = exploded[i]; |
---|
829 | } else { |
---|
830 | var extra = 0; |
---|
831 | } |
---|
832 | |
---|
833 | context.lineJoin = 'round'; |
---|
834 | context.lineWidth = 1; |
---|
835 | |
---|
836 | context.arc(this.centerx, |
---|
837 | this.centery, |
---|
838 | this.radius + 7 + extra, |
---|
839 | midpoint, |
---|
840 | midpoint + 0.01, |
---|
841 | 0); |
---|
842 | |
---|
843 | context.arc(this.centerx, |
---|
844 | this.centery, |
---|
845 | this.radius - offset + extra, |
---|
846 | midpoint, |
---|
847 | midpoint + 0.01, |
---|
848 | 0); |
---|
849 | |
---|
850 | context.stroke(); |
---|
851 | } |
---|
852 | } |
---|
853 | |
---|
854 | |
---|
855 | /** |
---|
856 | * The (now Pie chart specific) getSegment function |
---|
857 | * |
---|
858 | * @param object e The event object |
---|
859 | */ |
---|
860 | RGraph.Pie.prototype.getSegment = function (e) |
---|
861 | { |
---|
862 | RGraph.FixEventObject(e); |
---|
863 | |
---|
864 | // The optional arg provides a way of allowing some accuracy (pixels) |
---|
865 | var accuracy = arguments[1] ? arguments[1] : 0; |
---|
866 | |
---|
867 | var obj = e.target.__object__; |
---|
868 | var canvas = obj.canvas; |
---|
869 | var context = obj.context; |
---|
870 | var mouseCoords = RGraph.getMouseXY(e); |
---|
871 | var r = obj.radius; |
---|
872 | var angles = obj.angles; |
---|
873 | var ret = []; |
---|
874 | |
---|
875 | for (var i=0; i<angles.length; ++i) { |
---|
876 | |
---|
877 | var x = mouseCoords[0] - angles[i][2]; |
---|
878 | var y = mouseCoords[1] - angles[i][3]; |
---|
879 | var theta = Math.atan(y / x); // RADIANS |
---|
880 | var hyp = y / Math.sin(theta); |
---|
881 | var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy; |
---|
882 | // Put theta in DEGREES |
---|
883 | theta *= 57.3 |
---|
884 | |
---|
885 | /** |
---|
886 | * Account for the correct quadrant |
---|
887 | */ |
---|
888 | if (x < 0 && y >= 0) { |
---|
889 | theta += 180; |
---|
890 | } else if (x < 0 && y < 0) { |
---|
891 | theta += 180; |
---|
892 | } else if (x > 0 && y < 0) { |
---|
893 | theta += 360; |
---|
894 | } |
---|
895 | |
---|
896 | if (theta > 360) { |
---|
897 | theta -= 360; |
---|
898 | } |
---|
899 | |
---|
900 | if (theta >= angles[i][0] && theta < angles[i][1]) { |
---|
901 | |
---|
902 | hyp = Math.abs(hyp); |
---|
903 | |
---|
904 | if (!hyp || (obj.radius && hyp > obj.radius) ) { |
---|
905 | return null; |
---|
906 | } |
---|
907 | |
---|
908 | if (obj.type == 'pie' && obj.Get('chart.variant') == 'donut' && (hyp > obj.radius || hyp < (obj.radius / 2) ) ) { |
---|
909 | return null; |
---|
910 | } |
---|
911 | |
---|
912 | ret[0] = angles[i][2]; |
---|
913 | ret[1] = angles[i][3]; |
---|
914 | ret[2] = (obj.type == 'rose') ? angles[i][2] : obj.radius; |
---|
915 | ret[3] = angles[i][0]; |
---|
916 | ret[4] = angles[i][1]; |
---|
917 | ret[5] = i; |
---|
918 | |
---|
919 | |
---|
920 | |
---|
921 | if (ret[3] < 0) ret[3] += 360; |
---|
922 | if (ret[4] > 360) ret[4] -= 360; |
---|
923 | |
---|
924 | return ret; |
---|
925 | } |
---|
926 | } |
---|
927 | |
---|
928 | return null; |
---|
929 | } |
---|
930 | |
---|
931 | |
---|
932 | RGraph.Pie.prototype.DrawBorders = function () |
---|
933 | { |
---|
934 | if (this.Get('chart.linewidth') > 0) { |
---|
935 | |
---|
936 | this.context.lineWidth = this.Get('chart.linewidth'); |
---|
937 | this.context.strokeStyle = this.Get('chart.strokestyle'); |
---|
938 | |
---|
939 | for (var i=0,len=this.angles.length; i<len; ++i) { |
---|
940 | this.context.beginPath(); |
---|
941 | this.context.moveTo(this.angles[i][2], this.angles[i][3]); |
---|
942 | this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, (this.angles[i][0] + 0.01) / 57.3, 0); |
---|
943 | this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, (this.angles[i][1]) / 57.3, 0); |
---|
944 | this.context.closePath(); |
---|
945 | |
---|
946 | this.context.stroke(); |
---|
947 | } |
---|
948 | } |
---|
949 | } |
---|