Stefan Stidl пре 3 дана
родитељ
комит
0570377a6d
6 измењених фајлова са 730 додато и 658 уклоњено
  1. 7 2
      .eslintrc.json
  2. 1 1
      .prettierrc.json
  3. 8 8
      design-switch.js
  4. 568 501
      index-classic.html
  5. 136 139
      index-modern.html
  6. 10 7
      index.html

+ 7 - 2
.eslintrc.json

@@ -27,10 +27,15 @@
     "addEventListener": "readonly"
   },
   "rules": {
-    "no-unused-vars": ["warn", { "args": "none" }],
+    "no-unused-vars": [
+      "warn",
+      {
+        "args": "none"
+      }
+    ],
     "no-console": "off",
     "no-empty": "warn",
     "no-undef": "warn",
     "no-const-assign": "error"
   }
-}
+}

+ 1 - 1
.prettierrc.json

@@ -9,4 +9,4 @@
   "arrowParens": "avoid",
   "endOfLine": "lf",
   "htmlWhitespaceSensitivity": "ignore"
-}
+}

+ 8 - 8
design-switch.js

@@ -9,30 +9,30 @@
  * 
  * Note: This script is only loaded on the root index.html
  */
-(function() {
+(function () {
     'use strict';
-    
+
     // Don't run this script if we're already on a specific design page
     // This prevents infinite redirect loops
     const currentPath = window.location.pathname;
     if (currentPath.includes('index-classic.html') || currentPath.includes('index-modern.html')) {
         return;
     }
-    
+
     // Check URL parameters first (they override config)
     const urlParams = new URLSearchParams(window.location.search);
     const designParam = urlParams.get('design');
-    
+
     if (designParam === 'new') {
         redirectToNewDesign();
         return;
     }
-    
+
     if (designParam === 'old' || designParam === 'classic') {
         redirectToOldDesign();
         return;
     }
-    
+
     // Check config.json for design preference
     try {
         const xhr = new XMLHttpRequest();
@@ -57,13 +57,13 @@
         console.log('Using default (old) design:', error.message || 'config error');
         redirectToOldDesign();
     }
-    
+
     function redirectToNewDesign() {
         // Preserve any URL parameters when redirecting
         const currentParams = window.location.search;
         window.location.href = 'index-modern.html' + currentParams;
     }
-    
+
     function redirectToOldDesign() {
         // Preserve any URL parameters when redirecting
         const currentParams = window.location.search;

+ 568 - 501
index-classic.html

@@ -1,518 +1,585 @@
 <!DOCTYPE html>
 <html>
+
 <head>
-<link rel="shortcut icon" href="favicon.ico">
-<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
-<meta charset="UTF-8" />
-<script type="text/javascript" src="speedtest.js"></script>
-<script type="text/javascript">
-function I(i){return document.getElementById(i);}
-
-//LIST OF TEST SERVERS. Leave empty if you're doing a standalone installation. See documentation for details
-var SPEEDTEST_SERVERS=[
-	/*{	//this server doesn't actually exist, remove it
-		name:"Example Server 1", //user friendly name for the server
-		server:"//test1.mydomain.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
-		dlURL:"backend/garbage.php",  //path to download test on this server (garbage.php or replacement)
-		ulURL:"backend/empty.php",  //path to upload test on this server (empty.php or replacement)
-		pingURL:"backend/empty.php",  //path to ping/jitter test on this server (empty.php or replacement)
-		getIpURL:"backend/getIP.php"  //path to getIP on this server (getIP.php or replacement)
-	},
-	{	//this server doesn't actually exist, remove it
-		name:"Example Server 2", //user friendly name for the server
-		server:"//test2.example.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
-		dlURL:"garbage.php",  //path to download test on this server (garbage.php or replacement)
-		ulURL:"empty.php",  //path to upload test on this server (empty.php or replacement)
-		pingURL:"empty.php",  //path to ping/jitter test on this server (empty.php or replacement)
-		getIpURL:"getIP.php"  //path to getIP on this server (getIP.php or replacement)
-	}*/
-	//add other servers here, comma separated
-];
-
-//INITIALIZE SPEEDTEST
-var s=new Speedtest(); //create speed test object
-s.setParameter("telemetry_level","basic"); //enable basic telemetry (for results sharing)
-
-//SERVER AUTO SELECTION
-function initServers(){
-	if(SPEEDTEST_SERVERS.length==0){ //standalone installation
-		//just make the UI visible
-		I("loading").className="hidden";
-		I("serverArea").style.display="none";
-		I("testWrapper").className="visible";
-		initUI();
-	}else{ //multiple servers
-		var noServersAvailable=function(){
-			I("message").innerHTML="No servers available";
-		}
-		var runServerSelect=function(){
-			s.selectServer(function(server){
-				if(server!=null){ //at least 1 server is available
-					I("loading").className="hidden"; //hide loading message
-					//populate server list for manual selection
-					for(var i=0;i<SPEEDTEST_SERVERS.length;i++){
-						if(SPEEDTEST_SERVERS[i].pingT==-1) continue;
-						var option=document.createElement("option");
-						option.value=i;
-						option.textContent=SPEEDTEST_SERVERS[i].name;
-						if(SPEEDTEST_SERVERS[i]===server) option.selected=true;
-						I("server").appendChild(option);
-					}
-					//show test UI
-					I("testWrapper").className="visible";
-					initUI();
-				}else{ //no servers are available, the test cannot proceed
-					noServersAvailable();
+	<link rel="shortcut icon" href="favicon.ico">
+	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
+	<meta charset="UTF-8" />
+	<script type="text/javascript" src="speedtest.js"></script>
+	<script type="text/javascript">
+		function I(i) { return document.getElementById(i); }
+
+		//LIST OF TEST SERVERS. Leave empty if you're doing a standalone installation. See documentation for details
+		var SPEEDTEST_SERVERS = [
+			/*{	//this server doesn't actually exist, remove it
+				name:"Example Server 1", //user friendly name for the server
+				server:"//test1.mydomain.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
+				dlURL:"backend/garbage.php",  //path to download test on this server (garbage.php or replacement)
+				ulURL:"backend/empty.php",  //path to upload test on this server (empty.php or replacement)
+				pingURL:"backend/empty.php",  //path to ping/jitter test on this server (empty.php or replacement)
+				getIpURL:"backend/getIP.php"  //path to getIP on this server (getIP.php or replacement)
+			},
+			{	//this server doesn't actually exist, remove it
+				name:"Example Server 2", //user friendly name for the server
+				server:"//test2.example.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
+				dlURL:"garbage.php",  //path to download test on this server (garbage.php or replacement)
+				ulURL:"empty.php",  //path to upload test on this server (empty.php or replacement)
+				pingURL:"empty.php",  //path to ping/jitter test on this server (empty.php or replacement)
+				getIpURL:"getIP.php"  //path to getIP on this server (getIP.php or replacement)
+			}*/
+			//add other servers here, comma separated
+		];
+
+		//INITIALIZE SPEEDTEST
+		var s = new Speedtest(); //create speed test object
+		s.setParameter("telemetry_level", "basic"); //enable basic telemetry (for results sharing)
+
+		//SERVER AUTO SELECTION
+		function initServers() {
+			if (SPEEDTEST_SERVERS.length == 0) { //standalone installation
+				//just make the UI visible
+				I("loading").className = "hidden";
+				I("serverArea").style.display = "none";
+				I("testWrapper").className = "visible";
+				initUI();
+			} else { //multiple servers
+				var noServersAvailable = function () {
+					I("message").innerHTML = "No servers available";
+				}
+				var runServerSelect = function () {
+					s.selectServer(function (server) {
+						if (server != null) { //at least 1 server is available
+							I("loading").className = "hidden"; //hide loading message
+							//populate server list for manual selection
+							for (var i = 0; i < SPEEDTEST_SERVERS.length; i++) {
+								if (SPEEDTEST_SERVERS[i].pingT == -1) continue;
+								var option = document.createElement("option");
+								option.value = i;
+								option.textContent = SPEEDTEST_SERVERS[i].name;
+								if (SPEEDTEST_SERVERS[i] === server) option.selected = true;
+								I("server").appendChild(option);
+							}
+							//show test UI
+							I("testWrapper").className = "visible";
+							initUI();
+						} else { //no servers are available, the test cannot proceed
+							noServersAvailable();
+						}
+					});
 				}
-			});
-		}
-		if(typeof SPEEDTEST_SERVERS === "string"){
-			//need to fetch list of servers from specified URL
-			s.loadServerList(SPEEDTEST_SERVERS,function(servers){
-				if(servers==null){ //failed to load server list
-					noServersAvailable();
-				}else{ //server list loaded
-					SPEEDTEST_SERVERS=servers;
+				if (typeof SPEEDTEST_SERVERS === "string") {
+					//need to fetch list of servers from specified URL
+					s.loadServerList(SPEEDTEST_SERVERS, function (servers) {
+						if (servers == null) { //failed to load server list
+							noServersAvailable();
+						} else { //server list loaded
+							SPEEDTEST_SERVERS = servers;
+							runServerSelect();
+						}
+					});
+				} else {
+					//hardcoded server list
+					s.addTestPoints(SPEEDTEST_SERVERS);
 					runServerSelect();
 				}
-			});
-		}else{
-			//hardcoded server list
-			s.addTestPoints(SPEEDTEST_SERVERS);
-			runServerSelect();
-		}
-	}
-}
-
-var meterBk=/Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent)?"#EAEAEA":"#80808040";
-var dlColor="#6060AA",
-	ulColor="#616161";
-var progColor=meterBk;
-
-//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=12*sizScale;
-	ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,Math.PI*0.1);
-	ctx.stroke();
-	ctx.beginPath();
-	ctx.strokeStyle=fg;
-	ctx.lineWidth=12*sizScale;
-	ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,amount*Math.PI*1.2-Math.PI*1.1);
-	ctx.stroke();
-	if(typeof progress !== "undefined"){
-		ctx.fillStyle=prog;
-		ctx.fillRect(c.width*0.3,c.height-16*sizScale,c.width*0.4*progress,4*sizScale);
-	}
-}
-function mbpsToAmount(s){
-	return 1-(1/(Math.pow(1.3,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){
-		//speed test is running, abort
-		s.abort();
-		data=null;
-		I("startStopBtn").className="";
-		I("server").disabled=false;
-		initUI();
-	}else{
-		//test is not running, begin
-		I("startStopBtn").className="running";
-		I("shareArea").style.display="none";
-		I("server").disabled=true;
-		s.onupdate=function(data){
-            uiData=data;
-		};
-		s.onend=function(aborted){
-            I("startStopBtn").className="";
-            I("server").disabled=false;
-            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=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);
-	I("jitText").textContent=format(uiData.jitterStatus);
-}
-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);
-	I("dlText").textContent="";
-	I("ulText").textContent="";
-	I("pingText").textContent="";
-	I("jitText").textContent="";
-	I("ip").textContent="";
-}
-</script>
-<style type="text/css">
-	html,body{
-		border:none; padding:0; margin:0;
-		background:#FFFFFF;
-		color:#202020;
-	}
-	body{
-		text-align:center;
-		font-family:"Roboto",sans-serif;
-	}
-	h1{
-		color:#404040;
-	}
-	#loading{
-		background-color:#FFFFFF;
-		color:#404040;
-		text-align:center;
-	}
-	span.loadCircle{
-		display:inline-block;
-		width:2em;
-		height:2em;
-		vertical-align:middle;
-		background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
-		background-size:2em 2em;
-		margin-right:0.5em;
-		animation: spin 0.6s linear infinite;
-	}
-	@keyframes spin{
-		0%{transform:rotate(0deg);}
-		100%{transform:rotate(359deg);}
-	}
-	#startStopBtn{
-		display:inline-block;
-		margin:0 auto;
-		color:#6060AA;
-		background-color:rgba(0,0,0,0);
-		border:0.15em solid #6060FF;
-		border-radius:0.3em;
-		transition:all 0.3s;
-		box-sizing:border-box;
-		width:8em; 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);
-	}
-	#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:#FF3030;
-		border-color:#FF6060;
-		color:#FFFFFF;
-	}
-	#startStopBtn:before{
-		content:"Start";
-	}
-	#startStopBtn.running:before{
-		content:"Abort";
-	}
-	#serverArea{
-		margin-top:1em;
-	}
-	#server{
-		font-size:1em;
-		padding:0.2em;
-	}
-	#test{
-		margin-top:2em;
-		margin-bottom:12em;
-	}
-	div.testArea{
-		display:inline-block;
-		width:16em;
-		height:12.5em;
-		position:relative;
-		box-sizing:border-box;
-	}
-	div.testArea2{
-		display:inline-block;
-		width:14em;
-		height:7em;
-		position:relative;
-		box-sizing:border-box;
-		text-align:center;
-	}
-	div.testArea div.testName{
-		position:absolute;
-		top:0.1em; left:0;
-		width:100%;
-		font-size:1.4em;
-		z-index:9;
-	}
-	div.testArea2 div.testName{
-        display:block;
-        text-align:center;
-        font-size:1.4em;
-	}
-	div.testArea div.meterText{
-		position:absolute;
-		bottom:1.55em; left:0;
-		width:100%;
-		font-size:2.5em;
-		z-index:9;
-	}
-	div.testArea2 div.meterText{
-        display:inline-block;
-        font-size:2.5em;
-	}
-	div.meterText:empty:before{
-		content:"0.00";
-	}
-	div.testArea div.unit{
-		position:absolute;
-		bottom:2em; left:0;
-		width:100%;
-		z-index:9;
-	}
-	div.testArea2 div.unit{
-		display:inline-block;
-	}
-	div.testArea canvas{
-		position:absolute;
-		top:0; left:0; width:100%; height:100%;
-		z-index:1;
-	}
-	div.testGroup{
-		display:block;
-        margin: 0 auto;
-	}
-	#shareArea{
-		width:95%;
-		max-width:40em;
-		margin:0 auto;
-		margin-top:2em;
-	}
-	#shareArea > *{
-		display:block;
-		width:100%;
-		height:auto;
-		margin: 0.25em 0;
-	}
-	#privacyPolicy{
-        position:fixed;
-        top:2em;
-        bottom:2em;
-        left:2em;
-        right:2em;
-        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;
-	}
-	a.privacy{
-        text-align:center;
-        font-size:0.8em;
-        color:#808080;
-        padding: 0 3em;
-	}
-    div.closePrivacyPolicy {
-        width: 100%;
-        text-align: center;
-    }
-    div.closePrivacyPolicy a.privacy {
-        padding: 1em 3em;
-    }
-	@media all and (max-width:40em){
-		body{
-			font-size:0.8em;
-		}
-	}
-	div.visible{
-		animation: fadeIn 0.4s;
-		display:block;
-	}
-	div.hidden{
-		animation: fadeOut 0.4s;
-		display:none;
-	}
-	@keyframes fadeIn{
-		0%{
-			opacity:0;
-		}
-		100%{
-			opacity:1;
-		}
-	}
-	@keyframes fadeOut{
-		0%{
-			display:block;
-			opacity:1;
-		}
-		100%{
-			display:block;
-			opacity:0;
-		}
-	}
-	@media all and (prefers-color-scheme: dark){
-		html,body,#loading{
-			background:#202020;
-			color:#F4F4F4;
-			color-scheme:dark;
-		}
-		h1{
-			color:#E0E0E0;
-		}
-		a{
-			color:#9090FF;
-		}
-		#privacyPolicy{
-			background:#000000;
-		}
-		#resultsImg{
-			filter: invert(1);
-		}
-	}
-</style>
-<title>LibreSpeed</title>
+			}
+		}
+
+		var meterBk = /Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent) ? "#EAEAEA" : "#80808040";
+		var dlColor = "#6060AA",
+			ulColor = "#616161";
+		var progColor = meterBk;
+
+		//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 = 12 * sizScale;
+			ctx.arc(c.width / 2, c.height - 58 * sizScale, c.height / 1.8 - ctx.lineWidth, -Math.PI * 1.1, Math.PI * 0.1);
+			ctx.stroke();
+			ctx.beginPath();
+			ctx.strokeStyle = fg;
+			ctx.lineWidth = 12 * sizScale;
+			ctx.arc(c.width / 2, c.height - 58 * sizScale, c.height / 1.8 - ctx.lineWidth, -Math.PI * 1.1, amount * Math.PI * 1.2 - Math.PI * 1.1);
+			ctx.stroke();
+			if (typeof progress !== "undefined") {
+				ctx.fillStyle = prog;
+				ctx.fillRect(c.width * 0.3, c.height - 16 * sizScale, c.width * 0.4 * progress, 4 * sizScale);
+			}
+		}
+		function mbpsToAmount(s) {
+			return 1 - (1 / (Math.pow(1.3, 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) {
+				//speed test is running, abort
+				s.abort();
+				data = null;
+				I("startStopBtn").className = "";
+				I("server").disabled = false;
+				initUI();
+			} else {
+				//test is not running, begin
+				I("startStopBtn").className = "running";
+				I("shareArea").style.display = "none";
+				I("server").disabled = true;
+				s.onupdate = function (data) {
+					uiData = data;
+				};
+				s.onend = function (aborted) {
+					I("startStopBtn").className = "";
+					I("server").disabled = false;
+					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 = 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);
+			I("jitText").textContent = format(uiData.jitterStatus);
+		}
+		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);
+			I("dlText").textContent = "";
+			I("ulText").textContent = "";
+			I("pingText").textContent = "";
+			I("jitText").textContent = "";
+			I("ip").textContent = "";
+		}
+	</script>
+	<style type="text/css">
+		html,
+		body {
+			border: none;
+			padding: 0;
+			margin: 0;
+			background: #FFFFFF;
+			color: #202020;
+		}
+
+		body {
+			text-align: center;
+			font-family: "Roboto", sans-serif;
+		}
+
+		h1 {
+			color: #404040;
+		}
+
+		#loading {
+			background-color: #FFFFFF;
+			color: #404040;
+			text-align: center;
+		}
+
+		span.loadCircle {
+			display: inline-block;
+			width: 2em;
+			height: 2em;
+			vertical-align: middle;
+			background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
+			background-size: 2em 2em;
+			margin-right: 0.5em;
+			animation: spin 0.6s linear infinite;
+		}
+
+		@keyframes spin {
+			0% {
+				transform: rotate(0deg);
+			}
+
+			100% {
+				transform: rotate(359deg);
+			}
+		}
+
+		#startStopBtn {
+			display: inline-block;
+			margin: 0 auto;
+			color: #6060AA;
+			background-color: rgba(0, 0, 0, 0);
+			border: 0.15em solid #6060FF;
+			border-radius: 0.3em;
+			transition: all 0.3s;
+			box-sizing: border-box;
+			width: 8em;
+			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);
+		}
+
+		#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: #FF3030;
+			border-color: #FF6060;
+			color: #FFFFFF;
+		}
+
+		#startStopBtn:before {
+			content: "Start";
+		}
+
+		#startStopBtn.running:before {
+			content: "Abort";
+		}
+
+		#serverArea {
+			margin-top: 1em;
+		}
+
+		#server {
+			font-size: 1em;
+			padding: 0.2em;
+		}
+
+		#test {
+			margin-top: 2em;
+			margin-bottom: 12em;
+		}
+
+		div.testArea {
+			display: inline-block;
+			width: 16em;
+			height: 12.5em;
+			position: relative;
+			box-sizing: border-box;
+		}
+
+		div.testArea2 {
+			display: inline-block;
+			width: 14em;
+			height: 7em;
+			position: relative;
+			box-sizing: border-box;
+			text-align: center;
+		}
+
+		div.testArea div.testName {
+			position: absolute;
+			top: 0.1em;
+			left: 0;
+			width: 100%;
+			font-size: 1.4em;
+			z-index: 9;
+		}
+
+		div.testArea2 div.testName {
+			display: block;
+			text-align: center;
+			font-size: 1.4em;
+		}
+
+		div.testArea div.meterText {
+			position: absolute;
+			bottom: 1.55em;
+			left: 0;
+			width: 100%;
+			font-size: 2.5em;
+			z-index: 9;
+		}
+
+		div.testArea2 div.meterText {
+			display: inline-block;
+			font-size: 2.5em;
+		}
+
+		div.meterText:empty:before {
+			content: "0.00";
+		}
+
+		div.testArea div.unit {
+			position: absolute;
+			bottom: 2em;
+			left: 0;
+			width: 100%;
+			z-index: 9;
+		}
+
+		div.testArea2 div.unit {
+			display: inline-block;
+		}
+
+		div.testArea canvas {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: 100%;
+			z-index: 1;
+		}
+
+		div.testGroup {
+			display: block;
+			margin: 0 auto;
+		}
+
+		#shareArea {
+			width: 95%;
+			max-width: 40em;
+			margin: 0 auto;
+			margin-top: 2em;
+		}
+
+		#shareArea>* {
+			display: block;
+			width: 100%;
+			height: auto;
+			margin: 0.25em 0;
+		}
+
+		#privacyPolicy {
+			position: fixed;
+			top: 2em;
+			bottom: 2em;
+			left: 2em;
+			right: 2em;
+			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;
+		}
+
+		a.privacy {
+			text-align: center;
+			font-size: 0.8em;
+			color: #808080;
+			padding: 0 3em;
+		}
+
+		div.closePrivacyPolicy {
+			width: 100%;
+			text-align: center;
+		}
+
+		div.closePrivacyPolicy a.privacy {
+			padding: 1em 3em;
+		}
+
+		@media all and (max-width:40em) {
+			body {
+				font-size: 0.8em;
+			}
+		}
+
+		div.visible {
+			animation: fadeIn 0.4s;
+			display: block;
+		}
+
+		div.hidden {
+			animation: fadeOut 0.4s;
+			display: none;
+		}
+
+		@keyframes fadeIn {
+			0% {
+				opacity: 0;
+			}
+
+			100% {
+				opacity: 1;
+			}
+		}
+
+		@keyframes fadeOut {
+			0% {
+				display: block;
+				opacity: 1;
+			}
+
+			100% {
+				display: block;
+				opacity: 0;
+			}
+		}
+
+		@media all and (prefers-color-scheme: dark) {
+
+			html,
+			body,
+			#loading {
+				background: #202020;
+				color: #F4F4F4;
+				color-scheme: dark;
+			}
+
+			h1 {
+				color: #E0E0E0;
+			}
+
+			a {
+				color: #9090FF;
+			}
+
+			#privacyPolicy {
+				background: #000000;
+			}
+
+			#resultsImg {
+				filter: invert(1);
+			}
+		}
+	</style>
+	<title>LibreSpeed</title>
 </head>
+
 <body onload="initServers()">
-<h1>LibreSpeed</h1>
-<div id="loading" class="visible">
-	<p id="message"><span class="loadCircle"></span>Selecting a server...</p>
-</div>
-<div id="testWrapper" class="hidden">
-	<div id="startStopBtn" onclick="startStop()"></div><br/>
-	<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
-	<div id="serverArea">
-		Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
+	<h1>LibreSpeed</h1>
+	<div id="loading" class="visible">
+		<p id="message"><span class="loadCircle"></span>Selecting a server...</p>
 	</div>
-	<div id="test">
-		<div class="testGroup">
-            <div class="testArea2">
-				<div class="testName">Ping</div>
-				<div id="pingText" class="meterText" style="color:#AA6060"></div>
-				<div class="unit">ms</div>
+	<div id="testWrapper" class="hidden">
+		<div id="startStopBtn" onclick="startStop()"></div><br />
+		<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
+		<div id="serverArea">
+			Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
+		</div>
+		<div id="test">
+			<div class="testGroup">
+				<div class="testArea2">
+					<div class="testName">Ping</div>
+					<div id="pingText" class="meterText" style="color:#AA6060"></div>
+					<div class="unit">ms</div>
+				</div>
+				<div class="testArea2">
+					<div class="testName">Jitter</div>
+					<div id="jitText" class="meterText" style="color:#AA6060"></div>
+					<div class="unit">ms</div>
+				</div>
 			</div>
-			<div class="testArea2">
-				<div class="testName">Jitter</div>
-				<div id="jitText" class="meterText" style="color:#AA6060"></div>
-				<div class="unit">ms</div>
+			<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>
-		</div>
-		<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 id="ipArea">
+				<span id="ip"></span>
 			</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 id="shareArea" style="display:none">
+				<h3>Share results</h3>
+				<p>Test ID: <span id="testId"></span></p>
+				<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" />
 			</div>
 		</div>
-		<div id="ipArea">
-			<span id="ip"></span>
-		</div>
-		<div id="shareArea" style="display:none">
-			<h3>Share results</h3>
-			<p>Test ID: <span id="testId"></span></p>
-			<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" />
+		<a href="index.html?design=new">Try the modern design</a><br>
+		<a href="https://github.com/librespeed/speedtest">Source code</a>
+	</div>
+	<div id="privacyPolicy" style="display:none">
+		<h2>Privacy Policy</h2>
+		<p>This HTML5 speed test 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: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY
+				DEVELOPER</a>.
+		</p>
+		<br /><br />
+		<div class="closePrivacyPolicy">
+			<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a>
 		</div>
+		<br />
 	</div>
-	<a href="index.html?design=new">Try the modern design</a><br>
-	<a href="https://github.com/librespeed/speedtest">Source code</a>
-</div>
-<div id="privacyPolicy" style="display:none">
-    <h2>Privacy Policy</h2>
-    <p>This HTML5 speed test 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: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
-    </p>
-    <br/><br/>
-    <div class="closePrivacyPolicy">
-        <a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a>
-    </div>
-    <br/>
-</div>
 </body>
-</html>
+
+</html>

+ 136 - 139
index-modern.html

@@ -1,151 +1,148 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <meta
-      name="viewport"
-      content="width=device-width, initial-scale=1, shrink-to-fit=no"
-    />
-    <meta
-      name="description"
-      content="Free and Open Source Speedtest. Run it right now in your browser, or self-host on a PHP, Golang, Rust or Node server. License: LGPL."
-    />
-    <link rel="shortcut icon" href="images/favicon.svg" />
-    <script type="text/javascript" src="speedtest.js"></script>
-    <script type="text/javascript" src="javascript/index.js"></script>
-    <link rel="stylesheet" type="text/css" href="styling/index.css" />
-    <title>LibreSpeed - Free and Open Source Speedtest</title>
-  </head>
-
-  <body>
-    <header>
-      <img src="images/logo.svg" alt="LibreSpeed" />
-    </header>
-    <main>
-      <h1>Free and Open Source Speedtest.</h1>
-      <p class="tagline">No Flash, No Java, No Websockets, No Bullsh*t</p>
-
-      <div class="server-selector">
-        <div class="chosen">
-          <div class="chevron">
-            <img src="images/chevron.svg" alt="select..." />
-          </div>
-          <p>current server</p>
-          <h2 id="selected-server">searching nearest server...</h2>
-        </div>
-        <ul class="servers"></ul>
-        <p class="sponsor" id="sponsor">&nbsp;</p>
-      </div>
 
-      <p id="privacy-warning" class="hidden">
-        by clicking the start button you agree to our privacy policy<br />
-        <a href="#" id="choose-privacy">or choose your privacy options</a>
-      </p>
-      <button class="disabled" id="start-button"></button>
-
-      <div class="gauge-layout">
-        <div class="ping hidden">
-          <span class="label">Ping</span>:&nbsp;
-          <span class="value" id="ping">00</span>ms
+<head>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
+  <meta name="description"
+    content="Free and Open Source Speedtest. Run it right now in your browser, or self-host on a PHP, Golang, Rust or Node server. License: LGPL." />
+  <link rel="shortcut icon" href="images/favicon.svg" />
+  <script type="text/javascript" src="speedtest.js"></script>
+  <script type="text/javascript" src="javascript/index.js"></script>
+  <link rel="stylesheet" type="text/css" href="styling/index.css" />
+  <title>LibreSpeed - Free and Open Source Speedtest</title>
+</head>
+
+<body>
+  <header>
+    <img src="images/logo.svg" alt="LibreSpeed" />
+  </header>
+  <main>
+    <h1>Free and Open Source Speedtest.</h1>
+    <p class="tagline">No Flash, No Java, No Websockets, No Bullsh*t</p>
+
+    <div class="server-selector">
+      <div class="chosen">
+        <div class="chevron">
+          <img src="images/chevron.svg" alt="select..." />
         </div>
+        <p>current server</p>
+        <h2 id="selected-server">searching nearest server...</h2>
+      </div>
+      <ul class="servers"></ul>
+      <p class="sponsor" id="sponsor">&nbsp;</p>
+    </div>
+
+    <p id="privacy-warning" class="hidden">
+      by clicking the start button you agree to our privacy policy<br />
+      <a href="#" id="choose-privacy">or choose your privacy options</a>
+    </p>
+    <button class="disabled" id="start-button"></button>
+
+    <div class="gauge-layout">
+      <div class="ping hidden">
+        <span class="label">Ping</span>:&nbsp;
+        <span class="value" id="ping">00</span>ms
+      </div>
 
-        <div class="gauge download" id="download-gauge">
-          <div class="progress"></div>
-          <div class="speed"></div>
-          <h1><span id="download-speed">00</span> Mbps</h1>
-          <h2>Download</h2>
-        </div>
+      <div class="gauge download" id="download-gauge">
+        <div class="progress"></div>
+        <div class="speed"></div>
+        <h1><span id="download-speed">00</span> Mbps</h1>
+        <h2>Download</h2>
+      </div>
 
-        <div class="gauge upload" id="upload-gauge">
-          <div class="progress"></div>
-          <div class="speed"></div>
-          <h1><span id="upload-speed">00</span> Mbps</h1>
-          <h2>Upload</h2>
-        </div>
+      <div class="gauge upload" id="upload-gauge">
+        <div class="progress"></div>
+        <div class="speed"></div>
+        <h1><span id="upload-speed">00</span> Mbps</h1>
+        <h2>Upload</h2>
+      </div>
 
-        <div class="jitter hidden">
-          <span class="label">Jitter</span>:&nbsp;
-          <span class="value" id="jitter">00</span>ms
-        </div>
+      <div class="jitter hidden">
+        <span class="label">Jitter</span>:&nbsp;
+        <span class="value" id="jitter">00</span>ms
       </div>
+    </div>
+
+    <button class="small inverted hidden" id="share-results">
+      Share results
+    </button>
+  </main>
+  <footer>
+    <p class="source">
+      <a href="https://github.com/librespeed/speedtest">source code</a>
+    </p>
+  </footer>
+
+  <dialog id="share">
+    <div class="close-dialog">
+      <img src="images/close-button.svg" alt="Close" />
+    </div>
+    <img id="results" src="" alt="Test results in graphical form" />
+    <button id="copy-link">Copy link</button>
+  </dialog>
+
+  <dialog id="privacy">
+    <div class="close-dialog">
+      <img src="images/close-button.svg" alt="Close" />
+    </div>
+    <section>
+      <h1>Privacy Policy</h1>
+      <p>
+        This HTML5 speed test server is configured with telemetry enabled.
+      </p>
 
-      <button class="small inverted hidden" id="share-results">
-        Share results
-      </button>
-    </main>
-    <footer>
-      <p class="source">
-        <a href="https://github.com/librespeed/speedtest">source code</a>
+      <h2>What data we collect</h2>
+      <p>
+        At the end of the test, the following data is collected and stored:
       </p>
-    </footer>
 
-    <dialog id="share">
-      <div class="close-dialog">
-        <img src="images/close-button.svg" alt="Close" />
-      </div>
-      <img id="results" src="" alt="Test results in graphical form" />
-      <button id="copy-link">Copy link</button>
-    </dialog>
+      <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>
+
+      <h2>How we use the data</h2>
+      <p>Data collected through this service is used to:</p>
+
+      <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>
+
+      <p>No personal information is disclosed to third parties.</p>
+
+      <h2>Your consent</h2>
+      <p>
+        By starting the test, you consent to the terms of this privacy policy.
+      </p>
 
-    <dialog id="privacy">
-      <div class="close-dialog">
-        <img src="images/close-button.svg" alt="Close" />
-      </div>
-      <section>
-        <h1>Privacy Policy</h1>
-        <p>
-          This HTML5 speed test server is configured with telemetry enabled.
-        </p>
-
-        <h2>What data we collect</h2>
-        <p>
-          At the end of the test, the following data is collected and stored:
-        </p>
-
-        <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>
-
-        <h2>How we use the data</h2>
-        <p>Data collected through this service is used to:</p>
-
-        <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>
-
-        <p>No personal information is disclosed to third parties.</p>
-
-        <h2>Your consent</h2>
-        <p>
-          By starting the test, you consent to the terms of this privacy policy.
-        </p>
-
-        <h2>Data removal</h2>
-        <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.
-        </p>
-        <p>
-          Contact this email address for all deletion requests:
-          <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
-        </p>
-      </section>
-      <button id="close-privacy">Close</button>
-    </dialog>
-  </body>
-</html>
+      <h2>Data removal</h2>
+      <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.
+      </p>
+      <p>
+        Contact this email address for all deletion requests:
+        <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
+      </p>
+    </section>
+    <button id="close-privacy">Close</button>
+  </dialog>
+</body>
+
+</html>

+ 10 - 7
index.html

@@ -1,13 +1,16 @@
 <!DOCTYPE html>
 <html>
+
 <head>
-<link rel="shortcut icon" href="favicon.ico">
-<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
-<meta charset="UTF-8" />
-<script type="text/javascript" src="design-switch.js"></script>
-<title>LibreSpeed</title>
+    <link rel="shortcut icon" href="favicon.ico">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
+    <meta charset="UTF-8" />
+    <script type="text/javascript" src="design-switch.js"></script>
+    <title>LibreSpeed</title>
 </head>
+
 <body>
-<p>Loading...</p>
+    <p>Loading...</p>
 </body>
-</html>
+
+</html>