diff --git a/speedtest/index.php b/speedtest/index.php
new file mode 100644
index 0000000..4571fcf
--- /dev/null
+++ b/speedtest/index.php
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/speedtest/ui.css b/speedtest/ui.css
new file mode 100644
index 0000000..5bdff4b
--- /dev/null
+++ b/speedtest/ui.css
@@ -0,0 +1,74 @@
+ #startStopBtn:before{
+ content:"Start";
+ }
+ #startStopBtn.running:before{
+ content:"Abort";
+ }
+ #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;
+ }
+ @media all and (max-width:40em){
+ body{
+ font-size:0.8em;
+ }
+ }
diff --git a/speedtest/ui.js b/speedtest/ui.js
new file mode 100644
index 0000000..6512186
--- /dev/null
+++ b/speedtest/ui.js
@@ -0,0 +1,101 @@
+function I(i){return document.getElementById(i);}
+//INITIALIZE SPEEDTEST
+var s=new Speedtest(); //create speedtest object
+
+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){
+ //speedtest is running, abort
+ s.abort();
+ data=null;
+ I("startStopBtn").className="";
+ initUI();
+ }else{
+ //test is not running, begin
+ I("startStopBtn").className="running";
+ s.onupdate=function(data){
+ uiData=data;
+ };
+ s.onend=function(aborted){
+ I("startStopBtn").className="";
+ updateUI(true);
+ };
+ 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="";
+}