Initial commit
This commit is contained in:
474
index.html
Normal file
474
index.html
Normal file
@@ -0,0 +1,474 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Speedtest data</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@700&family=Inter:wght@400;900&display=swap" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style type="text/css">
|
||||
/* highcharts */
|
||||
.highcharts-figure,
|
||||
.highcharts-data-table table {
|
||||
/* min-width: 560px;
|
||||
max-width: 1000px;*/
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
.highcharts-data-table table {
|
||||
font-family: Verdana, sans-serif;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #ebebeb;
|
||||
margin: 10px auto;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
/*max-width: 500px;*/
|
||||
}
|
||||
|
||||
.highcharts-data-table caption {
|
||||
padding: 1em 0;
|
||||
font-size: 1.2em;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.highcharts-data-table th {
|
||||
font-weight: 600;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.highcharts-data-table td,
|
||||
.highcharts-data-table th,
|
||||
.highcharts-data-table caption {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.highcharts-data-table thead tr,
|
||||
.highcharts-data-table tr:nth-child(even) {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.highcharts-data-table tr:hover {
|
||||
background: #f1f7ff;
|
||||
}
|
||||
|
||||
/* General */
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
h1, h2, h5 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Recent boxes */
|
||||
#recent_samples {
|
||||
width: max-content;
|
||||
margin: auto;
|
||||
}
|
||||
#recent .recent_sample {
|
||||
display: block;
|
||||
background-color: #eee;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#recent .recent_sample .recent_dn,
|
||||
#recent .recent_sample .recent_up {
|
||||
font-family: "Fira Mono", sans-serif;
|
||||
color: #777;
|
||||
background-color: #fff;
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
padding: 0.3em;
|
||||
}
|
||||
#recent .recent_sample .recent_dn {
|
||||
color: #2caffe;
|
||||
}
|
||||
#recent .recent_sample .recent_up {
|
||||
color: #544fc5;
|
||||
padding-left: 0.3em;
|
||||
}
|
||||
|
||||
#recent .recent_spd {
|
||||
}
|
||||
|
||||
.recent_box .recent_fld {
|
||||
font-family: "Inter", sans-serif;
|
||||
display: table-row;
|
||||
}
|
||||
.recent_box {
|
||||
margin: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
.recent_box .recent_fld .recent_fld_name {
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
}
|
||||
.recent_box .recent_fld .recent_fld_val {
|
||||
color: #333;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
.recent_box .recent_fld .recent_fld_name,
|
||||
.recent_box .recent_fld .recent_fld_val {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
/* Spinner */
|
||||
#spinner {
|
||||
border: 16px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 16px solid #544fc5;
|
||||
border-bottom: 16px solid #2caffe;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
-webkit-animation: spin 2s linear infinite; /* Safari */
|
||||
animation: spin 2s linear infinite;
|
||||
|
||||
margin: 5em auto;
|
||||
}
|
||||
#spinner-desc {
|
||||
margin: 0 auto;
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
padding-top: 3em;
|
||||
}
|
||||
|
||||
/* Safari */
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.result_url {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media only screen and (max-width: 1100px) {
|
||||
#recent #recent_1 .recent_dn, #recent #recent_1 .recent_up {
|
||||
font-size: 2em;
|
||||
}
|
||||
#recent #recent_2 .recent_dn, #recent #recent_2 .recent_up {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
#recent #recent_3 .recent_dn, #recent #recent_3 .recent_up,
|
||||
#recent #recent_4 .recent_dn, #recent #recent_4 .recent_up,
|
||||
#recent #recent_5 .recent_dn, #recent #recent_5 .recent_up {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#recent #recent_1 .recent_fld {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
#recent #recent_2 .recent_fld {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
#recent #recent_3 .recent_fld,
|
||||
#recent #recent_4 .recent_fld,
|
||||
#recent #recent_5 .recent_fld {
|
||||
font-size: 0.4em;
|
||||
}
|
||||
|
||||
#recent {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
.recent_box {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
#recent .recent_sample {
|
||||
flex-basis: content;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.sep {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.highcharts-figure {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 1100px) {
|
||||
#recent #recent_1 .recent_dn, #recent #recent_1 .recent_up {
|
||||
font-size: 3em;
|
||||
}
|
||||
#recent #recent_2 .recent_dn, #recent #recent_2 .recent_up {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
#recent #recent_3 .recent_dn, #recent #recent_3 .recent_up,
|
||||
#recent #recent_4 .recent_dn, #recent #recent_4 .recent_up,
|
||||
#recent #recent_5 .recent_dn, #recent #recent_5 .recent_up {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#recent #recent_1 .recent_fld {
|
||||
font-size: 1em;
|
||||
}
|
||||
#recent #recent_2 .recent_fld {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
#recent #recent_3 .recent_fld,
|
||||
#recent #recent_4 .recent_fld,
|
||||
#recent #recent_5 .recent_fld {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
|
||||
#recent {
|
||||
display: inline-block;
|
||||
width: max-content;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
#recent .recent_sample {
|
||||
float: left;
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="spinner"></div>
|
||||
<div id="spinner-desc">Preparing data ...</div>
|
||||
<div id="recent_samples">
|
||||
<div id="recent">
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear:both"></div>
|
||||
<figure class="highcharts-figure">
|
||||
<div id="container"></div>
|
||||
</figure>
|
||||
|
||||
<script src="https://code.highcharts.com/highcharts.js"></script>
|
||||
<script src="https://code.highcharts.com/modules/boost.js"></script>
|
||||
<script src="https://code.highcharts.com/modules/exporting.js"></script>
|
||||
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||
<script>
|
||||
|
||||
var shortContent = ($(window).width() < 1000);
|
||||
var refDate = new Date;
|
||||
var isp = "";
|
||||
|
||||
function getDate(d) {
|
||||
var dt = Date.parse(d);
|
||||
dt = dt - refDate.getTimezoneOffset() * 60 * 1000;
|
||||
return dt;
|
||||
}
|
||||
function getFmtDate(d) {
|
||||
const dt = new Date(d);
|
||||
return dt.toDateString()
|
||||
+ ", " + dt.getHours() + ":" + dt.getMinutes() + ":" + dt.getSeconds();
|
||||
}
|
||||
|
||||
function getDownloadData(d) {
|
||||
var arr = [];
|
||||
d.forEach((sd) => {
|
||||
if (sd.error == undefined) {
|
||||
var dt = getDate(sd.timestamp);
|
||||
arr.push([
|
||||
dt,
|
||||
sd.download.bandwidth*8/1000000
|
||||
]);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
function getUploadData(d) {
|
||||
var arr = [];
|
||||
d.forEach((sd) => {
|
||||
if (sd.error == undefined) {
|
||||
var dt = getDate(sd.timestamp);
|
||||
arr.push([
|
||||
dt,
|
||||
sd.upload.bandwidth*8/1000000
|
||||
]);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function prepare_chart_data() {
|
||||
dndata = getDownloadData(speeddata);
|
||||
updata = getUploadData(speeddata);
|
||||
}
|
||||
|
||||
function show_chart() {
|
||||
prepare_chart_data();
|
||||
|
||||
console.time('line');
|
||||
|
||||
Highcharts.chart('container', {
|
||||
|
||||
chart: {
|
||||
type: 'spline',
|
||||
zoomType: 'x',
|
||||
height: '40%'
|
||||
},
|
||||
|
||||
title: {
|
||||
text: 'Trend in last 500 samples'
|
||||
},
|
||||
|
||||
subtitle: {
|
||||
text: 'Measured hourly by Speedtest CLI'
|
||||
},
|
||||
|
||||
accessibility: {
|
||||
screenReaderSection: {
|
||||
beforeChartFormat: '<{headingTagName}>{chartTitle}</{headingTagName}><div>{chartSubtitle}</div><div>{chartLongdesc}</div><div>{xAxisDescription}</div><div>{yAxisDescription}</div>'
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
valueDecimals: 2
|
||||
},
|
||||
|
||||
xAxis: {
|
||||
type: 'datetime'
|
||||
},
|
||||
yAxis: {
|
||||
title: {
|
||||
text: 'Speed (MB/s)'
|
||||
},
|
||||
plotBands: [{ // Fast
|
||||
from: 900,
|
||||
to: 1000,
|
||||
color: 'rgba(68, 170, 68, 0.1)',
|
||||
label: {
|
||||
text: 'Fast',
|
||||
style: {
|
||||
color: '#606060'
|
||||
}
|
||||
}
|
||||
}, { // Slow
|
||||
from: 300,
|
||||
to: 499,
|
||||
color: 'rgba(170, 50, 50, 0.1)',
|
||||
label: {
|
||||
text: 'Slow',
|
||||
style: {
|
||||
color: '#606060'
|
||||
}
|
||||
}
|
||||
}, { // Crawling
|
||||
from: 0,
|
||||
to: 299,
|
||||
color: 'rgba(200, 50, 50, 0.3)',
|
||||
label: {
|
||||
text: 'Crawling',
|
||||
style: {
|
||||
color: '#606060'
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: dndata,
|
||||
lineWidth: 2,
|
||||
name: 'Download (MB/s)'
|
||||
},
|
||||
{
|
||||
data: updata,
|
||||
lineWidth: 2,
|
||||
name: 'Upload (MB/s)'
|
||||
}
|
||||
]
|
||||
|
||||
});
|
||||
console.timeEnd('line');
|
||||
}
|
||||
|
||||
function get_speed(num) {
|
||||
return (num*8/1000000).toFixed(2);
|
||||
}
|
||||
|
||||
function get_up_dn_icon(dt, dtprev, isdn) {
|
||||
var icon = "•";
|
||||
var colr = "#0a0";
|
||||
var titl = "Upward trend";
|
||||
|
||||
if (isdn) {
|
||||
if (dt.download.bandwidth < dtprev.download.bandwidth) {
|
||||
colr = "#a00";
|
||||
titl = "Downward trend";
|
||||
}
|
||||
} else {
|
||||
if (dt.upload.bandwidth < dtprev.upload.bandwidth) {
|
||||
colr = "#a00";
|
||||
titl = "Downward trend";
|
||||
}
|
||||
}
|
||||
|
||||
return "<span style='color:" + colr + "' title='" + titl + "'>" + icon + "</span>";
|
||||
}
|
||||
|
||||
function show_one_sample(dt, dtprev, idx, dest) {
|
||||
dest.append($("<div class='recent_sample' id='recent_" + idx + "'></div>")
|
||||
.append($("<div class='recent_spd_tbl'></div>")
|
||||
.append($("<table></table>")
|
||||
.append($("<tr class='recent_spd'></tr>")
|
||||
.append($("<td class='recent_dn' title='Download speed in MB/s'>↓"
|
||||
+ get_speed(dt.download.bandwidth) + get_up_dn_icon(dt, dtprev, 1) + "</td>"),
|
||||
$("<td class='recent_up' title='Upload speed in MB/s'>↑"
|
||||
+ get_speed(dt.upload.bandwidth) + get_up_dn_icon(dt, dtprev, 0) + "</td>")))))
|
||||
.append($("<div class='recent_box'></div>")
|
||||
.append($("<div class='recent_fld'></div>")
|
||||
.append($("<div class='recent_fld_val'>" + getFmtDate(dt.timestamp) + "<a class='result_url' target='_blank' href='"
|
||||
+ dt.result.url + "'> ➔</a></div>")),
|
||||
$("<div class='recent_fld'></div>")
|
||||
.append($("<div class='recent_fld_val'>" + dt.server.name + " ("
|
||||
+ dt.server.location + ", "
|
||||
+ dt.server.country + ")</div>")))));
|
||||
}
|
||||
|
||||
function load_data() {
|
||||
var data_js = (shortContent) ? "speedtest.short.js" : "speedtest.js";
|
||||
var data_path = "/data/speed/" + data_js;
|
||||
|
||||
$.getScript(data_path, function() { show_content() });
|
||||
}
|
||||
|
||||
function show_content() {
|
||||
var snaps = 5;
|
||||
|
||||
isp = speeddata[speeddata.length-1].isp;
|
||||
console.log(isp);
|
||||
|
||||
for (let itr = speeddata.length-1, idx = 1; idx <= snaps; itr --, idx ++ ) {
|
||||
show_one_sample(speeddata[itr], speeddata[itr-1], idx, $('#recent'));
|
||||
}
|
||||
|
||||
if (!shortContent) {
|
||||
show_chart();
|
||||
}
|
||||
|
||||
// On done
|
||||
$("#spinner-desc").hide();
|
||||
$("#spinner")
|
||||
.after("<h2>Most recent samples</h2>")
|
||||
.after("<h5>(ISP: " + isp + ")</h5>")
|
||||
.after("<h1>Network Speed Data</h1>")
|
||||
.hide();
|
||||
console.timeEnd("speedtest");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
console.time("speedtest");
|
||||
load_data();
|
||||
});
|
||||
// vim: ts=4 sts=4 sw=4 et
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user