Project

General

Profile

Download (15 KB) Statistics
| Branch: | Tag: | Revision:
1 5b237745 Scott Ullrich
<?php
2
/*
3
	graph.php
4 6afa3a82 Scott Ullrich
	part of m0n0wall (http://m0n0.ch/wall)
5 2c5fda82 Jose Luis Duran
6 ce77a9c4 Phil Davis
	Copyright (C) 2013-2015 Electric Sheep Fencing, LP
7 985a897f Scott Ullrich
	Copyright (C) 2004-2006 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net>
8 6afa3a82 Scott Ullrich
	and Jonathan Watt <jwatt@jwatt.org>.
9 5b237745 Scott Ullrich
	All rights reserved.
10 2c5fda82 Jose Luis Duran
11 5b237745 Scott Ullrich
	Redistribution and use in source and binary forms, with or without
12
	modification, are permitted provided that the following conditions are met:
13 2c5fda82 Jose Luis Duran
14 5b237745 Scott Ullrich
	1. Redistributions of source code must retain the above copyright notice,
15
	   this list of conditions and the following disclaimer.
16 2c5fda82 Jose Luis Duran
17 5b237745 Scott Ullrich
	2. Redistributions in binary form must reproduce the above copyright
18
	   notice, this list of conditions and the following disclaimer in the
19
	   documentation and/or other materials provided with the distribution.
20 2c5fda82 Jose Luis Duran
21 5b237745 Scott Ullrich
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
	POSSIBILITY OF SUCH DAMAGE.
31
*/
32 7ac5a4cb Scott Ullrich
/*
33
	pfSense_MODULE:	graph
34
*/
35 5b237745 Scott Ullrich
36 6b07c15a Matthew Grooms
##|+PRIV
37
##|*IDENT=page-diagnostics-interfacetraffic
38
##|*NAME=Diagnostics: Interface Traffic page
39
##|*DESCR=Allow access to the 'Diagnostics: Interface Traffic' page.
40
##|*MATCH=graph.php*
41
##|-PRIV
42
43 12e437a2 Scott Ullrich
require("globals.inc");
44 8b379e47 Scott Ullrich
require("guiconfig.inc");
45 beb54c8f Scott Ullrich
46 45581172 Ermal Luçi
header("Last-Modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
47
header("Expires: " . gmdate( "D, j M Y H:i:s", time() ) . " GMT" );
48 2ebbb0bc Jose Luis Duran
header("Cache-Control: no-cache, no-store, must-revalidate" ); // HTTP/1.1
49 45581172 Ermal Luçi
header("Pragma: no-cache"); // HTTP/1.0
50 6afa3a82 Scott Ullrich
header("Content-type: image/svg+xml");
51 5b237745 Scott Ullrich
52
/********** HTTP GET Based Conf ***********/
53 6afa3a82 Scott Ullrich
$ifnum=@$_GET["ifnum"];  // BSD / SNMP interface name / number
54 ce2078f7 Scott Ullrich
$ifnum = get_real_interface($ifnum);
55 6afa3a82 Scott Ullrich
$ifname=@$_GET["ifname"]?$_GET["ifname"]:"Interface $ifnum";  //Interface name that will be showed on top right of graph
56 5b237745 Scott Ullrich
57
/********* Other conf *******/
58 0c8fb222 dwayne voelker
if (isset($config["widgets"]["trafficgraphs"]["scale_type"]))
59
	$scale_type = $config["widgets"]["trafficgraphs"]["scale_type"];
60
else
61
	$scale_type = "up";
62
63 6afa3a82 Scott Ullrich
$nb_plot=120;                   //NB plot in graph
64 358cb12c Scott Dale
if ($_GET["timeint"])
65
	$time_interval = $_GET["timeint"];		//Refresh time Interval
66
else
67 920e60b5 Scott Ullrich
	$time_interval = 3;
68 0543e1e9 Scott Ullrich
69 9228de6c jim-p
if ($_GET["initdelay"])
70
	$init_delay = $_GET["initdelay"];		//Initial Delay
71
else
72
	$init_delay = 3;
73
74 6afa3a82 Scott Ullrich
//SVG attributes
75
$attribs['axis']='fill="black" stroke="black"';
76 a85f75c2 Scott Ullrich
$attribs['in']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
77
$attribs['out']='fill="#000000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
78
$attribs['graph_in']='fill="none" stroke="#FF0000" stroke-opacity="0.8"';
79
$attribs['graph_out']='fill="none" stroke="#000000" stroke-opacity="0.8"';
80 6afa3a82 Scott Ullrich
$attribs['legend']='fill="black" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
81 a85f75c2 Scott Ullrich
$attribs['graphname']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="8"';
82 6afa3a82 Scott Ullrich
$attribs['grid_txt']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="6"';
83
$attribs['grid']='stroke="gray" stroke-opacity="0.5"';
84 a85f75c2 Scott Ullrich
$attribs['switch_unit']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
85
$attribs['switch_scale']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
86 6afa3a82 Scott Ullrich
$attribs['error']='fill="blue" font-family="Arial" font-size="4"';
87
$attribs['collect_initial']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
88 5b237745 Scott Ullrich
89
//Error text if we cannot fetch data : depends on which method is used
90 8625c24f Scott Ullrich
$error_text = "Cannot get data about interface " . htmlspecialchars($ifnum);
91 5b237745 Scott Ullrich
92 6afa3a82 Scott Ullrich
$height=100;            //SVG internal height : do not modify
93
$width=200;             //SVG internal width : do not modify
94 5b237745 Scott Ullrich
95 8625c24f Scott Ullrich
$fetch_link = "ifstats.php?if=" . htmlspecialchars($ifnum);
96 72d1b575 Scott Ullrich
97
/* check for custom theme colors */
98
if(file_exists("/usr/local/www/themes/{$g['theme']}/graph.php")) {
99
	$themetxt = file_get_contents("/usr/local/www/themes/{$g['theme']}/graph.php");
100
	eval($themetxt);
101 2c5fda82 Jose Luis Duran
}
102 72d1b575 Scott Ullrich
103 5b237745 Scott Ullrich
/********* Graph DATA **************/
104 cd94a9a8 Renato Botelho
print('<?xml version="1.0" encoding="UTF-8"?>' . "\n");?>
105 6afa3a82 Scott Ullrich
<svg width="100%" height="100%" viewBox="0 0 <?=$width?> <?=$height?>" preserveAspectRatio="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
106
  <g id="graph">
107
    <rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/>
108
    <line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/>
109
    <line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/>
110
    <path id="graph_out" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_out']?>/>
111
    <path id="graph_in"  d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_in']?>/>
112
    <path id="grid"  d="M0 <?=$height/4*1?> L <?=$width?> <?=$height/4*1?> M0 <?=$height/4*2?> L <?=$width?> <?=$height/4*2?> M0 <?=$height/4*3?> L <?=$width?> <?=$height/4*3?>" <?=$attribs['grid']?>/>
113
    <text id="grid_txt1" x="<?=$width?>" y="<?=$height/4*1?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
114
    <text id="grid_txt2" x="<?=$width?>" y="<?=$height/4*2?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
115
    <text id="grid_txt3" x="<?=$width?>" y="<?=$height/4*3?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
116 8454080a Carlos Eduardo Ramos
    <text id="graph_in_lbl" x="5" y="8" <?=$attribs['in']?>><?=gettext("In"); ?></text>
117
    <text id="graph_out_lbl" x="5" y="16" <?=$attribs['out']?>><?=gettext("Out"); ?></text>
118 6afa3a82 Scott Ullrich
    <text id="graph_in_txt" x="20" y="8" <?=$attribs['in']?>> </text>
119
    <text id="graph_out_txt" x="20" y="16" <?=$attribs['out']?>> </text>
120 8625c24f Scott Ullrich
    <text id="ifname" x="<?=$width?>" y="8" <?=$attribs['graphname']?> text-anchor="end"><?=htmlspecialchars($ifname)?></text>
121 8454080a Carlos Eduardo Ramos
    <text id="switch_unit" x="<?=$width*0.55?>" y="5" <?=$attribs['switch_unit']?>><?=gettext("Switch to bytes/s"); ?></text>
122
    <text id="switch_scale" x="<?=$width*0.55?>" y="11" <?=$attribs['switch_scale']?>><?=gettext("AutoScale"); ?> (<?=$scale_type?>)</text>
123 3bae4ee4 Charlie Marshall
    <text id="date" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text>
124
    <text id="time" x="<?=$width*0.33?>" y="11" <?=$attribs['legend']?>> </text>
125 8454080a Carlos Eduardo Ramos
    <text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>><?=gettext("Graph shows last"); ?> <?=$time_interval*$nb_plot?> <?=gettext("seconds"); ?></text>
126 6afa3a82 Scott Ullrich
    <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/>
127
    <text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text>
128 8454080a Carlos Eduardo Ramos
    <text id="collect_initial" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['collect_initial']?> text-anchor="middle"><?=gettext("Collecting initial data, please wait"); ?>...</text>
129 6afa3a82 Scott Ullrich
  </g>
130
  <script type="text/ecmascript">
131
    <![CDATA[
132
133
/**
134
 * getURL is a proprietary Adobe function, but it's simplicity has made it very
135
 * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest.
136
 */
137
if (typeof getURL == 'undefined') {
138
  getURL = function(url, callback) {
139
    if (!url)
140 8454080a Carlos Eduardo Ramos
      throw '<?=gettext("No URL for getURL"); ?>';
141 6afa3a82 Scott Ullrich
142
    try {
143
      if (typeof callback.operationComplete == 'function')
144
        callback = callback.operationComplete;
145
    } catch (e) {}
146
    if (typeof callback != 'function')
147 8454080a Carlos Eduardo Ramos
      throw '<?=gettext("No callback function for getURL"); ?>';
148 6afa3a82 Scott Ullrich
149
    var http_request = null;
150
    if (typeof XMLHttpRequest != 'undefined') {
151
      http_request = new XMLHttpRequest();
152
    }
153
    else if (typeof ActiveXObject != 'undefined') {
154
      try {
155
        http_request = new ActiveXObject('Msxml2.XMLHTTP');
156
      } catch (e) {
157
        try {
158
          http_request = new ActiveXObject('Microsoft.XMLHTTP');
159
        } catch (e) {}
160
      }
161
    }
162
    if (!http_request)
163 8454080a Carlos Eduardo Ramos
      throw '<?=gettext("Both getURL and XMLHttpRequest are undefined"); ?>';
164 6afa3a82 Scott Ullrich
165
    http_request.onreadystatechange = function() {
166
      if (http_request.readyState == 4) {
167
        callback( { success : true,
168
                    content : http_request.responseText,
169
                    contentType : http_request.getResponseHeader("Content-Type") } );
170
      }
171
    }
172
    http_request.open('GET', url, true);
173
    http_request.send(null);
174
  }
175
}
176
177
var SVGDoc = null;
178
var last_ifin = 0;
179
var last_ifout = 0;
180
var last_ugmt = 0;
181 5b237745 Scott Ullrich
var max = 0;
182 6afa3a82 Scott Ullrich
var plot_in = new Array();
183
var plot_out = new Array();
184 5b237745 Scott Ullrich
185 6afa3a82 Scott Ullrich
var max_num_points = <?=$nb_plot?>;  // maximum number of plot data points
186
var step = <?=$width?> / max_num_points ;
187 5b237745 Scott Ullrich
var unit = 'bits';
188
var scale_type = '<?=$scale_type?>';
189
190
function init(evt) {
191 6afa3a82 Scott Ullrich
  SVGDoc = evt.target.ownerDocument;
192
  SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
193
  SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);
194 5b237745 Scott Ullrich
195 6afa3a82 Scott Ullrich
  fetch_data();
196 5b237745 Scott Ullrich
}
197
198
function switch_unit(event)
199
{
200 8454080a Carlos Eduardo Ramos
  SVGDoc.getElementById('switch_unit').firstChild.data = '<?=gettext("Switch to"); ?> ' + unit + '/s';
201 6afa3a82 Scott Ullrich
  unit = (unit == 'bits') ? 'bytes' : 'bits';
202 5b237745 Scott Ullrich
}
203
204
function switch_scale(event)
205
{
206 8454080a Carlos Eduardo Ramos
  scale_type = (scale_type == 'up') ? '<?=gettext("follow"); ?>' : '<?=gettext("up"); ?>';
207 6afa3a82 Scott Ullrich
  SVGDoc.getElementById('switch_scale').firstChild.data = 'AutoScale (' + scale_type + ')';
208 5b237745 Scott Ullrich
}
209
210 6afa3a82 Scott Ullrich
function fetch_data() {
211
  getURL('<?=$fetch_link?>', plot_data);
212 5b237745 Scott Ullrich
}
213
214 6afa3a82 Scott Ullrich
function plot_data(obj) {
215
  // Show datetimelegend
216
  var now = new Date();
217 3bae4ee4 Charlie Marshall
  var time = LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds());
218
  SVGDoc.getElementById('time').firstChild.data = time;
219
  var date = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear();
220
  SVGDoc.getElementById('date').firstChild.data = date;
221 6afa3a82 Scott Ullrich
222
  if (!obj.success)
223
    return handle_error();  // getURL failed to get data
224
225
  var t = obj.content.split("|");
226
  var ugmt = parseFloat(t[0]);  // ugmt is an unixtimestamp style
227 73567959 Darren Embry
  var ifin = parseInt(t[1], 10);    // number of bytes received by the interface
228
  var ifout = parseInt(t[2], 10);   // number of bytes sent by the interface
229 6afa3a82 Scott Ullrich
  var scale;
230
231
  if (!isNumber(ifin) || !isNumber(ifout))
232
    return handle_error();
233
234
  var diff_ugmt  = ugmt - last_ugmt;
235 67c04e80 Seth Mos
  var diff_ifin  = ifin - last_ifin;
236
  var diff_ifout = ifout - last_ifout;
237
238 6afa3a82 Scott Ullrich
  if (diff_ugmt == 0)
239
    diff_ugmt = 1;  /* avoid division by zero */
240
241
  last_ugmt = ugmt;
242
  last_ifin = ifin;
243
  last_ifout = ifout;
244 9228de6c jim-p
  var graphTimerId = 0;
245 6afa3a82 Scott Ullrich
  switch (plot_in.length) {
246
  	case 0:
247
  		SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible');
248
		plot_in[0] = diff_ifin / diff_ugmt;
249
		plot_out[0] = diff_ifout / diff_ugmt;
250 9228de6c jim-p
		setTimeout('fetch_data()',<?=1000*($time_interval + $init_delay)?>);
251 6afa3a82 Scott Ullrich
		return;
252
	case 1:
253
    	SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden');
254
    	break;
255
    case max_num_points:
256
		// shift plot to left if the maximum number of plot points has been reached
257
		var i = 0;
258
		while (i < max_num_points) {
259
		  plot_in[i] = plot_in[i+1];
260
		  plot_out[i] = plot_out[++i];
261 5b237745 Scott Ullrich
		}
262 6afa3a82 Scott Ullrich
		plot_in.length--;
263
		plot_out.length--;
264
  }
265
266
  plot_in[plot_in.length] = diff_ifin / diff_ugmt;
267
  plot_out[plot_out.length]= diff_ifout / diff_ugmt;
268
  var index_plot = plot_in.length - 1;
269
270
  SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit);
271
  SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit);
272
273
  /* determine peak for sensible scaling */
274
  if (scale_type == 'up') {
275
    if (plot_in[index_plot] > max)
276
      max = plot_in[index_plot];
277
    if (plot_out[index_plot] > max)
278
      max = plot_out[index_plot];
279
  }
280
  else if (scale_type == 'follow') {
281
    i = 0;
282
    max = 0;
283
    while (i < plot_in.length) {
284
      if (plot_in[i] > max)
285
        max = plot_in[i];
286
      if (plot_out[i] > max)
287
        max = plot_out[i];
288
      i++;
289
    }
290
  }
291
292
  var rmax;  // max, rounded up
293
294
  if (unit == 'bits') {
295
    /* round up max, such that
296
         100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
297
    rmax = 12500;
298
    i = 0;
299
    while (max > rmax) {
300
      i++;
301
      if (i && (i % 4 == 0))
302
        rmax *= 1.25;
303
      else
304
        rmax *= 2;
305
    }
306
  } else {
307
    /* round up max, such that
308
         10 KB/s -> 20 KB/s -> 40 KB/s -> 80 KB/s -> 100 KB/s -> 200 KB/s -> 400 KB/s -> 800 KB/s -> 1 MB/s ... */
309
    rmax = 10240;
310
    i = 0;
311
    while (max > rmax) {
312
      i++;
313
      if (i && (i % 4 == 0))
314
        rmax *= 1.25;
315
      else
316
        rmax *= 2;
317 2c5fda82 Jose Luis Duran
318 6afa3a82 Scott Ullrich
      if (i == 8)
319
        rmax *= 1.024;
320
    }
321
  }
322
323
  scale = <?=$height?> / rmax;
324
325
  /* change labels accordingly */
326
  SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4,unit);
327
  SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4,unit);
328
  SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4,unit);
329
330
  var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale));
331
  var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale));
332
  for (i = 1; i < plot_in.length; i++)
333
  {
334
    var x = step * i;
335
    var y_in = <?=$height?> - (plot_in[i] * scale);
336
    var y_out = <?=$height?> - (plot_out[i] * scale);
337
    path_in += " L" + x + " " + y_in;
338
    path_out += " L" + x + " " + y_out;
339
  }
340
341
  SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden');
342
  SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in);
343
  SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out);
344
345
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
346 5b237745 Scott Ullrich
}
347
348 6afa3a82 Scott Ullrich
function handle_error() {
349
  SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
350
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
351 5b237745 Scott Ullrich
}
352
353
function isNumber(a) {
354 6afa3a82 Scott Ullrich
  return typeof a == 'number' && isFinite(a);
355 5b237745 Scott Ullrich
}
356
357 6afa3a82 Scott Ullrich
function formatSpeed(speed, unit) {
358
  if (unit == 'bits')
359
    return formatSpeedBits(speed);
360
  if (unit == 'bytes')
361
    return formatSpeedBytes(speed);
362 5b237745 Scott Ullrich
}
363
364
function formatSpeedBits(speed) {
365 6afa3a82 Scott Ullrich
  // format speed in bits/sec, input: bytes/sec
366
  if (speed < 125000)
367 6c83f226 Vinicius Coque
    return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
368 6afa3a82 Scott Ullrich
  if (speed < 125000000)
369 6c83f226 Vinicius Coque
    return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
370 6afa3a82 Scott Ullrich
  // else
371 6c83f226 Vinicius Coque
  return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
372 5b237745 Scott Ullrich
}
373 6afa3a82 Scott Ullrich
374 5b237745 Scott Ullrich
function formatSpeedBytes(speed) {
375 6afa3a82 Scott Ullrich
  // format speed in bytes/sec, input:  bytes/sec
376
  if (speed < 1048576)
377 6c83f226 Vinicius Coque
    return Math.round(speed / 10.24)/100 + " <?=gettext("KB/s"); ?>";
378 6afa3a82 Scott Ullrich
  if (speed < 1073741824)
379 6c83f226 Vinicius Coque
    return Math.round(speed / 10485.76)/100 + " <?=gettext("MB/s"); ?>";
380 6afa3a82 Scott Ullrich
  // else
381 6c83f226 Vinicius Coque
  return Math.round(speed / 10737418.24)/100 + " <?=gettext("GB/s"); ?>";  /* wow! */
382 5b237745 Scott Ullrich
}
383 6afa3a82 Scott Ullrich
384 5b237745 Scott Ullrich
function LZ(x) {
385 6afa3a82 Scott Ullrich
  return (x < 0 || x > 9 ? "" : "0") + x;
386 5b237745 Scott Ullrich
}
387 6afa3a82 Scott Ullrich
388
    ]]>
389
  </script>
390 985a897f Scott Ullrich
</svg>