moved qdb here because matt is lazy
[public/www-new.git] / pub / qdb / res / themes / default / js / graph.js
1 /*\r
2 ###############################################################################\r
3 # Chirpy! 0.3, a quote management system                                      #\r
4 # Copyright (C) 2005-2007 Tim De Pauw <ceetee@users.sourceforge.net>          #\r
5 ###############################################################################\r
6 # This program is free software; you can redistribute it and/or modify it     #\r
7 # under the terms of the GNU General Public License as published by the Free  #\r
8 # Software Foundation; either version 2 of the License, or (at your option)   #\r
9 # any later version.                                                          #\r
10 #                                                                             #\r
11 # This program is distributed in the hope that it will be useful, but WITHOUT #\r
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #\r
13 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for   #\r
14 # more details.                                                               #\r
15 #                                                                             #\r
16 # You should have received a copy of the GNU General Public License along     #\r
17 # with this program; if not, write to the Free Software Foundation, Inc., 51  #\r
18 # Franklin St, Fifth Floor, Boston, MA  02110-1301  USA                       #\r
19 ###############################################################################\r
20 \r
21 ###############################################################################\r
22 # graph.js                                                                    #\r
23 # Turns graph data into nice graphs                                           #\r
24 ###############################################################################\r
25 # $Id:: graph.js 308 2007-02-09 01:31:25Z ceetee                            $ #\r
26 ###############################################################################\r
27 */\r
28 \r
29 var graphConfig = new Array();\r
30 graphConfig["bar_chart_values"] = 5;\r
31 graphConfig["pie_chart_radius"] = 175;\r
32 graphConfig["pie_chart_extrusion"] = 5;\r
33 graphConfig["pie_chart_colors"] = new Array(\r
34         "#DCDCDC", "#CCCCCC", "#BCBCBC", "#ACACAC"\r
35 );\r
36 graphConfig["pie_chart_border_width"] = 1;\r
37 graphConfig["pie_chart_border_color"] = "#ACACAC";\r
38 graphConfig["pie_chart_random_rotation"] = false;\r
39 graphConfig["ogive_values"] = 5;\r
40 graphConfig["ogive_chart_width"] = 660;\r
41 graphConfig["ogive_chart_height"] = 360;\r
42 graphConfig["ogive_chart_color"] = "#DCDCDC";\r
43 graphConfig["ogive_average_color"] = "#BCBCBC";\r
44 graphConfig["decimal_point_is_comma"] = false;\r
45 graphConfig["average_decimal_count"] = 2;\r
46 \r
47 function checkForGraphs () {\r
48         var dls = document.getElementsByTagName("dl");\r
49         for (var i = dls.length - 1; i >= 0; i--) {\r
50                 var dl = dls[i];\r
51                 var func;\r
52                 if (hasClassName(dl, "bar-chart-data")) {\r
53                         func = createBarChart;\r
54                 }\r
55                 else if (hasClassName(dl, "pie-chart-data")) {\r
56                         func = createPieChart;\r
57                 }\r
58                 else if (hasClassName(dl, "ogive-data")) {\r
59                         func = createOgive;\r
60                 }\r
61                 if (func) {\r
62                         var chartData = extractChartData(dl);\r
63                         var samples = extractChartLabelCount(dl);\r
64                         var node = func(dl, chartData, samples);\r
65                         node.id = dl.id;\r
66                 }\r
67         }\r
68 }\r
69 \r
70 function createBarChart (sourceNode, chartData, samples) {\r
71         var div = document.createElement("div");\r
72         div.className = "chart bar-chart";\r
73         var graph = document.createElement("div");\r
74         graph.className = "bar-chart-graph";\r
75         div.appendChild(graph);\r
76         var max = 0;\r
77         for (var i = 0; i < chartData.length; i++) {\r
78                 var value = chartData[i][1];\r
79                 if (value > max) {\r
80                         max = value;\r
81                 }\r
82         }\r
83         createChartPane(div, graph, chartData, samples,\r
84                 graphConfig["bar_chart_values"], 0, max);\r
85         var barWidth = 100 / chartData.length;\r
86         var total = 0;\r
87         for (var i = 0; i < chartData.length; i++) {\r
88                 var data = chartData[i];\r
89                 var text = data[0];\r
90                 var value = data[1];\r
91                 var column = document.createElement("div");\r
92                 column.className = "bar-chart-column";\r
93                 column.style.left = i * barWidth + "%";\r
94                 column.style.width = barWidth + "%";\r
95                 column.title = data[0] + " "\r
96                         + String.fromCharCode(0x2192) + " " + data[1];\r
97                 if (value > 0) {\r
98                         var bar = document.createElement("div");\r
99                         bar.className = "bar-chart-bar";\r
100                         bar.style.height = Math.round(100 * value / max) + "%";\r
101                         var innerBar = document.createElement("div");\r
102                         innerBar.className = "bar-chart-inner-bar";\r
103                         bar.appendChild(innerBar);\r
104                         column.appendChild(bar);\r
105                 }\r
106                 graph.appendChild(column);\r
107                 total += value;\r
108         }\r
109         var avg = total / chartData.length;\r
110         var avgDiv = document.createElement("div");\r
111         avgDiv.className = "bar-chart-average-container";\r
112         avgDiv.style.top = (100 - (100 * avg / max)) + "%";\r
113         var stdDevTotal = 0;\r
114         for (var i = 0; i < chartData.length; i++) {\r
115                 var temp = chartData[i][1] - avg;\r
116                 stdDevTotal += temp * temp;\r
117         }\r
118         var stdDev = roundToDecimals(\r
119                 Math.sqrt(stdDevTotal / chartData.length),\r
120                 graphConfig["average_decimal_count"]);\r
121         var avgText = roundToDecimals(avg, graphConfig["average_decimal_count"]);\r
122         if (graphConfig["decimal_point_is_comma"]) {\r
123                 avgText = ("" + avgText).replace(".", ",");\r
124                 stdDev = ("" + stdDev).replace(".", ",");\r
125         }\r
126         var avgSpan = document.createElement("span");\r
127         avgSpan.appendChild(document.createTextNode(avgText));\r
128         avgSpan.className = "bar-chart-average";\r
129         var stdDevSpan = document.createElement("span");\r
130         stdDevSpan.appendChild(\r
131                 document.createTextNode(String.fromCharCode(0x00B1) + " " + stdDev));\r
132         stdDevSpan.className = "bar-chart-standard-deviation";\r
133         var avgTextDiv = document.createElement("div");\r
134         avgTextDiv.appendChild(avgSpan);\r
135         avgTextDiv.appendChild(document.createTextNode(" "));\r
136         avgTextDiv.appendChild(stdDevSpan);\r
137         avgDiv.appendChild(avgTextDiv);\r
138         graph.appendChild(avgDiv);\r
139         sourceNode.parentNode.replaceChild(div, sourceNode);\r
140         return div;\r
141 }\r
142 \r
143 function createPieChart (sourceNode, chartData) {\r
144         var cnv = document.createElement("canvas");\r
145         var rad = graphConfig["pie_chart_radius"];\r
146         var ext = graphConfig["pie_chart_extrusion"];\r
147         var side = (rad + ext) * 2;\r
148         cnv.width = cnv.height = side;\r
149         cnv.style.width = cnv.style.height = side + "px";\r
150         var graph = document.createElement("div");\r
151         graph.appendChild(cnv);\r
152         cnv = ensureCanvas(cnv);\r
153         if (!cnv) {\r
154                 return createBarChart(sourceNode, chartData);\r
155         }\r
156         var div = document.createElement("div");\r
157         div.className = "chart pie-chart";\r
158         sourceNode.parentNode.replaceChild(div, sourceNode);\r
159         graph.className = "pie-chart-graph";\r
160         var legend = document.createElement("dl");\r
161         legend.className = "pie-chart-legend";\r
162         div.appendChild(graph);\r
163         div.appendChild(legend);\r
164         drawPieChart(cnv, legend, chartData);\r
165         return div;\r
166 }\r
167 \r
168 function drawPieChart (canvas, legend, data) {\r
169         var ctx = canvas.getContext("2d");\r
170         var total = 0;\r
171         for (var i = 0; i < data.length; i++) {\r
172                 total += data[i][1];\r
173         }\r
174         var runningTotal = 0;\r
175         var rad = graphConfig["pie_chart_radius"];\r
176         var ext = graphConfig["pie_chart_extrusion"];\r
177         var xCenter = rad + ext;\r
178         var yCenter = rad + ext;\r
179         var radius = rad;\r
180         var colors = graphConfig["pie_chart_colors"];\r
181         var stroke;\r
182         if (graphConfig["pie_chart_border_width"]) {\r
183                 ctx.strokeStyle = graphConfig["pie_chart_border_color"];\r
184                 var w = graphConfig["pie_chart_border_width"];\r
185                 ctx.lineWidth = w;\r
186                 radius -= 2 * w;\r
187                 stroke = true;\r
188         }\r
189         else {\r
190                 stroke = false;\r
191         }\r
192         var initialAngle = (graphConfig["pie_chart_random_rotation"]\r
193                 ? Math.round(2 * Math.PI * Math.random())\r
194                 : - Math.PI / 2);\r
195         for (var i = 0; i < data.length; i++) {\r
196                 var name = data[i][0];\r
197                 var value = data[i][1];\r
198                 var color = colors[i % colors.length];\r
199                 if (value > 0) {\r
200                         var startAngle = runningTotal / total * 2 * Math.PI + initialAngle;\r
201                         runningTotal += value;\r
202                         var endAngle = runningTotal / total * 2 * Math.PI + initialAngle;\r
203                         var diff = (startAngle + endAngle) / 2;\r
204                         var x = xCenter + Math.cos(diff) * ext;\r
205                         var y = yCenter + Math.sin(diff) * ext;\r
206                         ctx.fillStyle = color;\r
207                         ctx.beginPath();\r
208                         ctx.moveTo(x, y);\r
209                         ctx.arc(x, y, radius, startAngle, endAngle, false);\r
210                         ctx.lineTo(x, y);\r
211                         ctx.closePath();\r
212                         ctx.fill();\r
213                         if (stroke) {\r
214                                 // Safari doesn't like it when we don't recreate the path\r
215                                 ctx.beginPath();\r
216                                 ctx.moveTo(x, y);\r
217                                 ctx.arc(x, y, radius, startAngle, endAngle, false);\r
218                                 ctx.lineTo(x, y);\r
219                                 ctx.closePath();\r
220                                 ctx.stroke();\r
221                         }\r
222                 }\r
223                 var block = document.createElement("div");\r
224                 block.style.backgroundColor = color;\r
225                 var dt = document.createElement("dt");\r
226                 dt.appendChild(block);\r
227                 legend.appendChild(dt);\r
228                 var dd = document.createElement("dd");\r
229                 var l = document.createElement("span");\r
230                 l.className = "label";\r
231                 l.appendChild(document.createTextNode(name));\r
232                 dd.appendChild(l);\r
233                 var v = document.createElement("span");\r
234                 v.className = "value";\r
235                 v.appendChild(document.createTextNode(value));\r
236                 dd.appendChild(v);\r
237                 var p = document.createElement("span");\r
238                 p.className = "percentage";\r
239                 p.appendChild(document.createTextNode(\r
240                         (value == 0 ? 0 : Math.round(100 * value / total)) + "%"));\r
241                 dd.appendChild(p);\r
242                 legend.appendChild(dd);\r
243         }\r
244 }\r
245 \r
246 function createOgive (sourceNode, chartData, samples) {\r
247         var cnv = document.createElement("canvas");\r
248         cnv.width = graphConfig["ogive_chart_width"];\r
249         cnv.height = graphConfig["ogive_chart_height"];\r
250         cnv.style.width = graphConfig["ogive_chart_width"] + "px";\r
251         cnv.style.height = graphConfig["ogive_chart_height"] + "px";\r
252         var graph = document.createElement("div");\r
253         graph.appendChild(cnv);\r
254         cnv = ensureCanvas(cnv);\r
255         if (!cnv) {\r
256                 var total = 0;\r
257                 for (var i = 0; i < chartData.length; i++) {\r
258                         var old = chartData[i][1];\r
259                         chartData[i][1] += total;\r
260                         total += old;\r
261                 }\r
262                 return createBarChart(sourceNode, chartData, samples);\r
263         }\r
264         var div = document.createElement("div");\r
265         div.className = "chart ogive";\r
266         div.appendChild(graph);\r
267         sourceNode.parentNode.replaceChild(div, sourceNode);\r
268         graph.className = "ogive-graph";\r
269         var ignoreFirst = extractOgiveIgnoreCount(sourceNode);\r
270         var totalIgnored = 0;\r
271         if (ignoreFirst) {\r
272                 for (var i = 0; i < ignoreFirst; i++) {\r
273                         totalIgnored += chartData[i][1];\r
274                 }\r
275                 var newChartData = new Array();\r
276                 for (var i = ignoreFirst; i < chartData.length; i++) {\r
277                         newChartData[i - ignoreFirst] = chartData[i];\r
278                 }\r
279                 chartData = newChartData;\r
280         }\r
281         var chartCumulData = new Array();\r
282         var total = 0;\r
283         for (var i = 0; i < chartData.length; i++) {\r
284                 total += chartData[i][1];\r
285                 chartCumulData[i] = total;\r
286         }\r
287         var width = graphConfig["ogive_chart_width"];\r
288         var max = chartCumulData[chartCumulData.length - 1];\r
289         var xy = new Array();\r
290         for (var i = 0; i < chartCumulData.length; i++) {\r
291                 xy[i + 1] = chartCumulData[i];\r
292         }\r
293         var regrParam = powerRegression(xy);\r
294         var a = regrParam[0];\r
295         var b = regrParam[1];\r
296         var drawAvg;\r
297         if (!isNaN(a) && !isNaN(b)) {\r
298                 var chartAvgData = new Array();\r
299                 for (var x = 0; x < width; x++) {\r
300                         var i = x / width * chartData.length;\r
301                         chartAvgData[x] = a * Math.pow(i, b);\r
302                         if (chartAvgData[x] > max) {\r
303                                 max = chartAvgData[x];\r
304                         }\r
305                 }\r
306                 drawAvg = true;\r
307         }\r
308         else {\r
309                 drawAvg = false;\r
310         }\r
311         createChartPane(div, graph, chartData, samples,\r
312                 graphConfig["ogive_values"], 0, max, totalIgnored);\r
313                 var scale = graphConfig["ogive_chart_height"] / max;\r
314         drawOgive(cnv, chartCumulData, false, scale);\r
315         if (drawAvg) {\r
316                 drawOgive(cnv, chartAvgData, true, scale);\r
317                 var equation = document.createElement("div");\r
318                 equation.className = "regression-equation";\r
319                 equation.appendChild(document.createTextNode("y = "));\r
320                 var aRounded = roundToDecimals(a, 2);\r
321                 var bRounded = roundToDecimals(b, 2);\r
322                 if (graphConfig["decimal_point_is_comma"]) {\r
323                         aRounded = ("" + aRounded).replace(".", ",");\r
324                         bRounded = ("" + bRounded).replace(".", ",");\r
325                 }\r
326                 if (aRounded != 1) {\r
327                         var aTxt = document.createElement("span");\r
328                         aTxt.appendChild(document.createTextNode(aRounded));\r
329                         equation.appendChild(aTxt);\r
330                 }\r
331                 equation.appendChild(document.createTextNode("x"));\r
332                 if (bRounded != 1) {\r
333                         var bTxt = document.createElement("sup");\r
334                         bTxt.appendChild(document.createTextNode(bRounded));\r
335                         equation.appendChild(bTxt);\r
336                 }\r
337                 equation.style.position = "absolute";\r
338                 var xPos = 3/4;\r
339                 var xPad = 15;\r
340                 var yBase = Math.round(chartAvgData[Math.round(xPos * chartAvgData.length)]\r
341                         / max * graphConfig["ogive_chart_height"]);\r
342                 /*if (b < 1) {*/\r
343                         equation.style.left = xPad + Math.round(\r
344                                 xPos * graphConfig["ogive_chart_width"]) + "px";\r
345                         equation.style.bottom = yBase + "px";\r
346                 /*}\r
347                 else {\r
348                         equation.style.right = xPad + Math.round(\r
349                                 (1 - xPos) * graphConfig["ogive_chart_width"]) + "px";\r
350                         equation.style.top = graphConfig["ogive_chart_height"] - yBase + "px";\r
351                 }*/\r
352                 div.appendChild(equation);\r
353         }\r
354         return div;\r
355 }\r
356 \r
357 function drawOgive (canvas, chartData, avg, yScale) {\r
358         var ctx = canvas.getContext("2d");\r
359         var graphWidth = graphConfig["ogive_chart_width"];\r
360         var graphHeight = graphConfig["ogive_chart_height"];\r
361         if (avg) {\r
362                 ctx.strokeStyle = graphConfig["ogive_average_color"];\r
363                 ctx.lineWidth = 1;\r
364         }\r
365         else {\r
366                 ctx.fillStyle = graphConfig["ogive_chart_color"];\r
367         }\r
368         var xScale = graphWidth / chartData.length;\r
369         var x0 = 0;\r
370         var y0 = graphHeight;\r
371         ctx.beginPath();\r
372         var notFirst = false;\r
373         for (var i = 0; i < chartData.length; i++) {\r
374                 if (chartData[i] == null) continue;\r
375                 var x = Math.round(x0 + (i + 1) * xScale);\r
376                 var y = Math.round(y0 - chartData[i] * yScale);\r
377                 if (notFirst) {\r
378                         ctx.lineTo(x, y);\r
379                 }\r
380                 else {\r
381                         ctx.moveTo(x, y);\r
382                         notFirst = true;\r
383                 }\r
384         }\r
385         if (avg) {\r
386                 ctx.stroke();\r
387         }\r
388         else {\r
389                 ctx.lineTo(graphWidth, graphHeight);\r
390                 ctx.lineTo(x0, y0);\r
391                 ctx.fill();\r
392         }\r
393         return yScale;\r
394 }\r
395 \r
396 function extractChartData (dl) {\r
397         var name, label;\r
398         var data = new Array();\r
399         for (var i = 0; i < dl.childNodes.length; i++) {\r
400                 var child = dl.childNodes[i];\r
401                 var eln = child.nodeName.toLowerCase();\r
402                 switch (eln) {\r
403                         case "dt":\r
404                                 name = child.firstChild.nodeValue;\r
405                                 label = (child.title ? child.title : null);\r
406                                 break;\r
407                         case "dd":\r
408                                 var value = parseInt(child.firstChild.nodeValue);\r
409                                 data.push(new Array(name, value, label));\r
410                                 break;\r
411                 }\r
412         }\r
413         return data;\r
414 }\r
415 \r
416 function extractChartLabelCount (dl) {\r
417         var result = extractChartParameter(dl, "label-count");\r
418         if (result == null) return 0;\r
419         return result;\r
420 }\r
421 \r
422 function extractOgiveIgnoreCount (dl) {\r
423         var result = extractChartParameter(dl, "ignore-first");\r
424         if (result == null) return 0;\r
425         return result;\r
426 }\r
427 \r
428 function extractChartParameter (dl, name) {\r
429         name += "-";\r
430         var classNames = dl.className.split(/\s+/);\r
431         for (var i = 0; i < classNames.length; i++) {\r
432                 var className = classNames[i];\r
433                 if (className.indexOf(name) == 0) {\r
434                         return parseInt(className.substring(name.length));\r
435                 }\r
436         }\r
437         return null;\r
438 }\r
439 \r
440 function createChartPane (div, graph, chartData, samples, values, min, max, delta) {\r
441         if (!delta) delta = 0;\r
442         div.appendChild(createChartValues(min + delta, max + delta, values, graph, delta));\r
443         div.appendChild(createChartLabels(chartData, samples));\r
444 }\r
445 \r
446 function createChartValues (min, max, count, graph, delta) {\r
447         var values = document.createElement("div");\r
448         values.className = "chart-values";\r
449         var top = createChartValue(max);\r
450         top.style.top = "0";\r
451         top.style.marginTop = "0";\r
452         var bottom = createChartValue(min);\r
453         bottom.style.bottom = "0";\r
454         bottom.style.marginBottom = "0";\r
455         values.appendChild(top);\r
456         var step = (max - min) / (count - 1);\r
457         for (var i = 1; i < count - 1; i++) {\r
458                 var val = Math.round(step * i) + delta;\r
459                 var perc = 100 / (count - 1) * i + "%";\r
460                 var v = createChartValue(val);\r
461                 v.style.bottom = perc;\r
462                 values.appendChild(v);\r
463                 var line = document.createElement("div");\r
464                 line.className = "chart-line";\r
465                 line.style.top = perc;\r
466                 graph.appendChild(line);\r
467         }\r
468         var line = document.createElement("div");\r
469         line.className = "chart-line";\r
470         line.style.top = "0";\r
471         graph.appendChild(line);\r
472         values.appendChild(bottom);\r
473         return values;\r
474 }\r
475 \r
476 function createChartValue (value) {\r
477         var v = document.createElement("div");\r
478         v.className = "chart-value";\r
479         v.appendChild(document.createTextNode(Math.round(value)));\r
480         return v;\r
481 }\r
482 \r
483 function createChartLabels (chartData, samples) {\r
484         var labels = document.createElement("div");\r
485         labels.className = "chart-labels";\r
486         var sample = (samples > 0 && chartData.length > samples);\r
487         var barWidth = 100 / chartData.length;\r
488         var sampleInterval, labelWidth;\r
489         if (sample) {\r
490                 if (chartData.length < samples * 2) {\r
491                         samples = Math.ceil(chartData.length / 2);\r
492                 }\r
493                 sampleInterval = chartData.length / samples;\r
494                 labelWidth = 100 / samples;\r
495                 var firstLabel = createChartLabel(\r
496                         chartData[0], 0, labelWidth + "%",\r
497                         "left");\r
498                 labels.appendChild(firstLabel);\r
499                 var lastLabel = createChartLabel(\r
500                         chartData[chartData.length - 1],\r
501                         (100 - labelWidth) + "%", labelWidth + "%",\r
502                         "right");\r
503                 labels.appendChild(lastLabel);\r
504         }\r
505         else {\r
506                 labelWidth = barWidth;\r
507         }\r
508         var labelCount = 0;\r
509         for (var i = 0; i < chartData.length; i++) {\r
510                 var data = chartData[i];\r
511                 if (!sample || Math.floor((labelCount + 0.5) * sampleInterval) == i) {\r
512                         if (sample && (++labelCount == 1 || labelCount == samples)) continue;\r
513                         var left = ((i + 0.5) * barWidth - labelWidth / 2) + "%";\r
514                         var label = createChartLabel(data, left, labelWidth + "%", "center");\r
515                         labels.appendChild(label);\r
516                 }\r
517         }\r
518         return labels;\r
519 }\r
520 \r
521 function createChartLabel (data, position, width, align) {\r
522         var label = document.createElement("div");\r
523         label.className = "chart-label";\r
524         label.style.left = position;\r
525         label.style.width = width;\r
526         var innerLabel = document.createElement("div");\r
527         innerLabel.className = "chart-inner-label";\r
528         var text = (data[2] != null ? data[2] : data[0]);\r
529         innerLabel.appendChild(document.createTextNode(text));\r
530         innerLabel.style.textAlign = align;\r
531         label.appendChild(innerLabel);\r
532         return label;\r
533 }\r
534 \r
535 function ensureCanvas (canvas) {\r
536         if (!canvas.getContext && useExCanvas()) {\r
537                 canvas = G_vmlCanvasManager.initElement(canvas);\r
538         }\r
539         return (canvas.getContext ? canvas : null);\r
540 }\r
541 \r
542 function useExCanvas () {\r
543         return (typeof G_vmlCanvasManager != "undefined");\r
544 }\r
545 \r
546 function hasClassName (element, className) {\r
547         return element.className.match(new RegExp("\\b" + className + "\\b"));\r
548 }\r
549 \r
550 function powerRegression (data) {\r
551         var n = data.length;\r
552         var sumX = 0;\r
553         var sumY = 0;\r
554         var sumXX = 0;\r
555         var sumXY = 0;\r
556         for (i in data) {\r
557                 var x = Math.log(i);\r
558                 var y = Math.log(data[i]);\r
559                 sumX += x;\r
560                 sumY += y;\r
561                 sumXX += x * x;\r
562                 sumXY += x * y;\r
563         }\r
564         var sxx = sumXX - (sumX * sumX) / n;\r
565         var sxy = sumXY - (sumX * sumY) / n;\r
566         var xbar = sumX / n;\r
567         var ybar = sumY / n;\r
568         var b = sxy / sxx;\r
569         var a = Math.pow(Math.exp(1), ybar - b * xbar);\r
570         var result = new Array();\r
571         result[0] = a;\r
572         result[1] = b;\r
573         return result;\r
574 }\r
575 \r
576 function roundToDecimals (number, decimals) {\r
577         var factor = Math.pow(10, decimals);\r
578         return Math.round(number * factor) / factor;\r
579 }\r
580 \r
581 function addOnloadFunction (f) {\r
582         if (window.onload != null) {\r
583                 var old = window.onload;\r
584                 window.onload = function (e) {\r
585                         old(e);\r
586                         f();\r
587                 };\r
588         }\r
589         else {\r
590                 window.onload = f;\r
591         }\r
592 }\r
593 \r
594 addOnloadFunction(checkForGraphs);\r