speedtest-go/web/assets/index.html
2021-09-17 21:06:00 +08:00

524 lines
18 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no"/>
<meta charset="UTF-8"/>
<link rel="shortcut icon" href="../../favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Oswald:300,400|Source+Sans+Pro:400,600">
<script type="text/javascript" src="speedtest.js"></script>
<script type="text/javascript">
function I(i) {
return document.getElementById(i);
}
var meterBk = "#ebecf0"; // ui--input-border
var dlColor = "#02b159", // grass-green
ulColor = "#02b159", // grass-green
pingColor = "#02b159", // grass-green
jitColor = "#02b159"; // grass-green
var progColor = "#0ac9f7"; // sky-blue
function setPageTitle() {
var pageTitleDiv = document.getElementById('pageTitle');
var titlePrefixNode = document.createTextNode("You're currently using this server:");
var lineBreakElement = document.createElement('br');
var locationNode = document.createTextNode('Unknown');
switch (window.location.hostname) {
case 'us1.zzz.cat':
locationNode.textContent = 'Unites States 1 (Fremont, Linode)';
break;
case 'us2.zzz.cat':
locationNode.textContent = 'Unites States 2 (LAX (CN2), Bandwagon)';
break;
case 'us3.zzz.cat':
locationNode.textContent = 'Unites States 3 (LAX (CN2 GIA-E), Bandwagon)';
break;
case 'jp1.zzz.cat':
locationNode.textContent = 'Japan 1 (Tokyo 2, Linode)';
break;
case 'jp2.zzz.cat':
locationNode.textContent = 'Japan 2 (Tokyo 2, Linode)';
break;
case 'sg1.zzz.cat':
locationNode.textContent = 'Singapore (Singapore, Linode)';
break;
case 'ca1.zzz.cat':
locationNode.textContent = 'Canada (Toronto, Linode)';
break;
default:
locationNode.textContent = window.location.hostname;
break;
}
pageTitleDiv.appendChild(titlePrefixNode);
pageTitleDiv.appendChild(lineBreakElement);
pageTitleDiv.appendChild(locationNode);
}
//INITIALIZE SPEEDTEST
var s = new Speedtest(); //create speedtest object
//CODE FOR GAUGES
function drawMeter(c, amount, bk, fg, progress, prog) {
var ctx = c.getContext("2d");
var dp = window.devicePixelRatio || 1;
var cw = c.clientWidth * dp, ch = c.clientHeight * dp;
var sizScale = ch * 0.0055;
if (c.width == cw && c.height == ch) {
ctx.clearRect(0, 0, cw, ch);
} else {
c.width = cw;
c.height = ch;
}
ctx.beginPath();
ctx.strokeStyle = bk;
ctx.lineWidth = 4 * sizScale;
ctx.arc(c.width / 2, c.height / 2, c.height / 2 - ctx.lineWidth, -Math.PI * 1.2, Math.PI * 0.2);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = fg;
ctx.lineWidth = 4 * sizScale;
ctx.arc(c.width / 2, c.height / 2, c.height / 2 - ctx.lineWidth, -Math.PI * 1.2, amount * Math.PI * 1.2 - Math.PI * 1.2);
ctx.stroke();
if (typeof progress !== "undefined") {
ctx.fillStyle = prog;
ctx.fillRect(c.width * 0.3, c.height - 28 * sizScale, c.width * 0.4 * progress, 4 * sizScale);
}
}
function mbpsToAmount(s) {
return 1 - (1 / (Math.pow(1.3, Math.sqrt(s))));
}
function msToAmount(s) {
return 1 - (1 / (Math.pow(1.08, Math.sqrt(s))));
}
function format(d) {
d = Number(d);
if (d < 10) return d.toFixed(2);
if (d < 100) return d.toFixed(1);
return d.toFixed(0);
}
//UI CODE
var uiData = null;
function startStop() {
if (s.getState() == 3) {
//speedtest is running, abort
s.abort();
data = null;
I("startStopBtn").className = "";
initUI();
} else {
//test is not running, begin
I("startStopBtn").className = "running";
I("shareArea").style.display = "none";
s.onupdate = function (data) {
uiData = data;
};
s.onend = function (aborted) {
I("startStopBtn").className = "";
updateUI(true);
if (!aborted) {
//if testId is present, show sharing panel, otherwise do nothing
try {
var testId = uiData.testId;
if (testId != null) {
var shareURL = window.location.href.substring(0, window.location.href.lastIndexOf("/")) + "/results?id=" + testId;
I("resultsImg").src = shareURL;
I("resultsURL").value = shareURL;
I("testId").innerHTML = testId;
I("shareArea").style.display = "";
}
} catch (e) {
}
}
};
s.start();
}
}
//this function reads the data sent back by the test and updates the UI
function updateUI(forced) {
if (!forced && s.getState() != 3) return;
if (uiData == null) return;
var status = uiData.testState;
I("ip").textContent = "IP Address: " + uiData.clientIp;
I("dlText").textContent = (status == 1 && uiData.dlStatus == 0) ? "..." : format(uiData.dlStatus);
drawMeter(I("dlMeter"), mbpsToAmount(Number(uiData.dlStatus * (status == 1 ? oscillate() : 1))), meterBk, dlColor, Number(uiData.dlProgress), progColor);
I("ulText").textContent = (status == 3 && uiData.ulStatus == 0) ? "..." : format(uiData.ulStatus);
drawMeter(I("ulMeter"), mbpsToAmount(Number(uiData.ulStatus * (status == 3 ? oscillate() : 1))), meterBk, ulColor, Number(uiData.ulProgress), progColor);
I("pingText").textContent = format(uiData.pingStatus);
drawMeter(I("pingMeter"), msToAmount(Number(uiData.pingStatus * (status == 2 ? oscillate() : 1))), meterBk, pingColor, Number(uiData.pingProgress), progColor);
I("jitText").textContent = format(uiData.jitterStatus);
drawMeter(I("jitMeter"), msToAmount(Number(uiData.jitterStatus * (status == 2 ? oscillate() : 1))), meterBk, jitColor, Number(uiData.pingProgress), progColor);
}
function oscillate() {
return 1 + 0.02 * Math.sin(Date.now() / 100);
}
//update the UI every frame
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || (function (callback, element) {
setTimeout(callback, 1000 / 60);
});
function frame() {
requestAnimationFrame(frame);
updateUI();
}
frame(); //start frame loop
//function to (re)initialize UI
function initUI() {
drawMeter(I("dlMeter"), 0, meterBk, dlColor, 0);
drawMeter(I("ulMeter"), 0, meterBk, ulColor, 0);
drawMeter(I("pingMeter"), 0, meterBk, pingColor, 0);
drawMeter(I("jitMeter"), 0, meterBk, jitColor, 0);
I("dlText").textContent = "";
I("ulText").textContent = "";
I("pingText").textContent = "";
I("jitText").textContent = "";
I("ip").textContent = "";
}
</script>
<style type="text/css">
:root {
--dark-gray: #32363b;
--grass-green: #02b159;
}
html, body {
background-color: white;
border: none;
box-sizing: border-box;
color: var(--dark-gray);
padding: 0;
margin: 0;
}
html {
line-height: 1.15;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
font-size: 18px;
font-size: 1.125rem;
line-height: 1.33333;
font-family: "Source Sans Pro", sans-serif;
font-weight: 400;
letter-spacing: 0;
margin: 1rem 1rem 2rem;
text-align: center;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
*, *:before, *:after {
box-sizing: inherit;
}
#startStopBtn {
display: inline-block;
margin: 0 auto;
padding-left: 2rem;
padding-right: 2rem;
color: white;
background-color: var(--grass-green);
border: 0.15em solid var(--grass-green);
border-radius: 0.3em;
transition: all 0.3s;
box-sizing: border-box;
height: 3em;
line-height: 2.7em;
cursor: pointer;
/*box-shadow:0 0 0 rgba(0,0,0,0.1), inset 0 0 0 rgba(0,0,0,0.1);*/
font-weight: 600;
}
#startStopBtn:hover {
/*box-shadow: 0 0 2em rgba(0,0,0,0.1), inset 0 0 1em rgba(0,0,0,0.1);*/
}
#startStopBtn.running {
background-color: var(--dark-gray);
border-color: var(--dark-gray);
color: white;
}
#startStopBtn:before {
content: "Start Speed Test";
}
#startStopBtn.running:before {
content: "Abort";
}
#test {
margin-top: 2rem;
margin-bottom: 2rem;
}
div.testArea {
display: inline-block;
width: calc(100% - 2rem - 2rem); /* subtract margins */
max-width: 16em;
margin: 2rem;
position: relative;
box-sizing: border-box;
}
div.testArea::before {
content: '';
display: block;
padding-top: 100%;
}
div.testName {
bottom: 0;
font-size: 1rem;
font-weight: 600;
left: 0;
position: absolute;
width: 100%;
z-index: 9;
}
div.meterText {
bottom: 1.8em;
font-family: "Oswald", sans-serif;
font-size: 4em;
font-weight: 300;
left: 0;
line-height: 1;
position: absolute;
width: 100%;
z-index: 9;
}
div.meterText:empty:before {
content: "0.00";
}
div.unit {
bottom: 3.5em;
font-family: "Oswald", sans-serif;
font-size: 1.5em;
font-weight: 400;
letter-spacing: 0.5px;
left: 0;
line-height: 1;
position: absolute;
text-transform: uppercase;
width: 100%;
z-index: 9;
}
div.testArea canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
#ipArea {
margin-top: 2rem;
}
#shareArea {
width: 95%;
max-width: 25em;
margin: 0 auto;
margin-top: 2em;
}
#shareArea > * {
display: block;
width: 100%;
height: auto;
margin: 0.25em 0;
}
#privacyPolicy {
position: fixed;
top: 10em;
bottom: 10em;
left: 15em;
right: 15em;
overflow-y: auto;
width: auto;
height: auto;
box-shadow: 0 0 3em 1em #000000;
z-index: 999999;
text-align: left;
background-color: #FFFFFF;
padding: 1em;
}
#privacyCloseBtn {
display: inline-block;
margin: 0 auto;
padding-left: 2rem;
padding-right: 2rem;
color: white;
background-color: var(--grass-green);
border: 0.15em solid var(--grass-green);
border-radius: 0.3em;
transition: all 0.3s;
box-sizing: border-box;
height: 3em;
line-height: 2.7em;
cursor: pointer;
/*box-shadow:0 0 0 rgba(0,0,0,0.1), inset 0 0 0 rgba(0,0,0,0.1);*/
font-weight: 600;
}
#resultsURL {
color: #333;
font-size: 1.2rem;
padding: 0.5rem 1rem;
border-radius: 0.2rem;
background-color: rgb(255, 255, 255);
border: green;
display: block;
border-style: dashed;
border-width: 2px;
margin: 0.8em 0 0.8em 0;
}
#testId {
margin-top: 1.4em;
}
a.privacy {
margin-top: 2em;
text-align: center;
font-size: 0.8em;
color: #808080;
display: block;
}
@media all and (max-width: 65em) {
/* 1040 px */
div.testGroup {
margin-left: -1rem;
margin-right: -1rem;
}
div.testArea {
font-size: 1.5384615vw; /* 16 px */
margin: 1rem;
width: calc(100% - 1rem - 1rem); /* subtract margins */
}
}
@media all and (max-width: 40em) {
/* 640 px */
div.testArea {
font-size: 0.8em; /* 12.8 px */
}
}
</style>
<title>zzz.cat Speedtest</title>
</head>
<body>
<div id="testWrapper">
<h1 id="pageTitle"></h1>
<div id="startStopBtn" onclick="startStop()"></div>
<br/>
<div id="test">
<div class="testGroup">
<div class="testArea">
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Ping</div>
<canvas id="pingMeter" class="meter"></canvas>
<div id="pingText" class="meterText"></div>
<div class="unit">ms</div>
</div>
<div class="testArea">
<div class="testName">Jitter</div>
<canvas id="jitMeter" class="meter"></canvas>
<div id="jitText" class="meterText"></div>
<div class="unit">ms</div>
</div>
</div>
<div id="ipArea">
<span id="ip"></span>
</div>
<div><a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a></div>
<div id="shareArea" style="display:none">
<h3>Share your results</h3>
<input type="text" value="" id="resultsURL" readonly="readonly"
onclick="this.select();this.focus();this.select();document.execCommand('copy');alert('Link copied')"/>
<img src="" id="resultsImg"/>
<p>Test ID: <span id="testId"></span></p>
</div>
</div>
</div>
<div id="privacyPolicy" style="display:none">
<h2>Privacy Policy</h2>
<p>This HTML5 Speedtest server is configured with telemetry enabled.</p>
<h4>What data we collect</h4>
<p>
At the end of the test, the following data is collected and stored:
<ul>
<li>Test ID</li>
<li>Time of testing</li>
<li>Test results (download and upload speed, ping and jitter)</li>
<li>IP address</li>
<li>ISP information</li>
<li>Approximate location (inferred from IP address, not GPS)</li>
<li>User agent and browser locale</li>
<li>Test log (contains no personal information)</li>
</ul>
</p>
<h4>How we use the data</h4>
<p>
Data collected through this service is used to:
<ul>
<li>Allow sharing of test results (sharable image for forums, etc.)</li>
<li>To improve the service offered to you (for instance, to detect problems on our side)</li>
</ul>
No personal information is disclosed to third parties.
</p>
<h4>Your consent</h4>
<p>
By starting the test, you consent to the terms of this privacy policy.
</p>
<h4>Data removal</h4>
<p>
If you want to have your information deleted, you need to provide either the ID of the test or your IP address.
This is the only way to identify your data, without this information we won't be able to comply with your
request.<br/><br/>
Contact this email address for all deletion requests: maddie at zzz dot cat.
</p>
<br/><br/>
<a id="privacyCloseBtn" class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a><br/>
</div>
<script type="text/javascript">setPageTitle();</script>
<script type="text/javascript">setTimeout(function () {
initUI()
}, 100);</script>
</body>
</html>