diff --git a/README.md b/README.md
index 7a154d4..f826057 100644
--- a/README.md
+++ b/README.md
@@ -2,23 +2,23 @@
No Flash, No Java, No Websocket, No Bullshit.
-This is a very small (4k) Speedtest implemented in Javascript, that relies only on XMLHttpRequest.
+This is a very small (4k) Speedtest implemented in Javascript, using XMLHttpRequest and Web Workers.
## Try it
[Take a Speedtest](http://speedtest.adolfintel.com)
## Compatibility
-Microsoft Edge, Firefox 10+, Chrome 10+, Opera 15+, Safari 7 (not tested)
+Only modern browsers are supported (Edge 12+)
## Requirements
- - A reasonably fast web serve
+ - A reasonably fast web server
- Some way to generate garbage data using either the included PHP script, a [big file of random data](http://downloads.adolfintel.com/geth.php?r=speedtest-bigfile), or a symlink to /dev/urandom
- Your server must not compress the data it sends
- Your server must accept large POST requests (up to 10 Megabytes), otherwise the upload test will fail
- Client side, there must not be any type of buffering (such as a proxy), or you may get incorrect results
## How to use
-See example.html, it's not rocket science.
+See the examples, it's really simple.
## License
Copyright (C) 2016 Federico Dossena
diff --git a/example.html b/example.html
deleted file mode 100644
index 507b898..0000000
--- a/example.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-Speedtest
-
-
-
-Speedtest
-Download
-Wait...
-Upload
-Wait...
-Latency
-Wait...
-
-
-
\ No newline at end of file
diff --git a/example1.html b/example1.html
new file mode 100644
index 0000000..a8c2842
--- /dev/null
+++ b/example1.html
@@ -0,0 +1,26 @@
+
+
+
+Speedtest
+
+
+Speedtest
+Download
+
+Upload
+
+Latency
+
+
+
+
\ No newline at end of file
diff --git a/example2.html b/example2.html
new file mode 100644
index 0000000..be1a907
--- /dev/null
+++ b/example2.html
@@ -0,0 +1,70 @@
+
+
+
+Speedtest
+
+
+
+Speedtest
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example3.html b/example3.html
new file mode 100644
index 0000000..9ee0a41
--- /dev/null
+++ b/example3.html
@@ -0,0 +1,97 @@
+
+
+
+Speedtest
+
+
+
+
+Speedtest
+
+Run speedtest
+
+
\ No newline at end of file
diff --git a/speedtest.js b/speedtest.js
deleted file mode 100644
index ce93259..0000000
--- a/speedtest.js
+++ /dev/null
@@ -1,123 +0,0 @@
-function DownloadTester(serverURL,done,update,err){
- this.xhr=new XMLHttpRequest();
- this.firstTick=true;
- this.prevLoaded=0;
- this.startT=new Date().getTime();
- this.prevT=new Date().getTime();
- this.speed=0.0;
- if(done)this.onDone=done; if(update)this.onUpdate=update; if(err)this.onFail=err;
- this.xhr.onprogress=function(event){
- var instspd=(event.loaded-this.prevLoaded)/((new Date().getTime()-this.prevT)/1000.0);
- if(isNaN(instspd)||!isFinite(instspd)) return;
- if(this.firstTick){
- this.speed=instspd;
- this.firstTick=false;
- }else{
- this.speed=this.speed*0.9+instspd*0.1;
- }
- this.prevLoaded=event.loaded;
- this.prevT=new Date().getTime();
- this.onUpdate();
- if(((this.prevT-this.startT)/1000.0)>15){try{this.xhr.abort();}catch(e){} this.onDone();}
- }.bind(this);
- this.xhr.onload=function(){
- this.onUpdate();
- this.onDone();
- }.bind(this);
- this.xhr.onerror=function(){
- this.onUpdate();
- this.onFail();
- }.bind(this);
- this.xhr.open("GET", serverURL+"?random="+Math.random(),true);
- this.xhr.send();
-}
-DownloadTester.prototype={
- constructor:DownloadTester,
- onDone:function(){},
- onFail:function(){},
- onUpdate:function(){},
- getValue:function(){return ((this.speed*8)/1048576.0).toFixed(2);},
- cancel:function(){try{this.xhr.abort();}catch(e){}}
-}
-
-function UploadTester(serverURL,done,update,err){
- this.xhr=new XMLHttpRequest();
- this.firstTick=true;
- this.prevLoaded=0;
- this.startT=new Date().getTime();
- this.prevT=new Date().getTime();
- this.speed=0.0;
- if(done)this.onDone=done; if(update)this.onUpdate=update; if(err)this.onFail=err;
- this.xhr.upload.onprogress=function(event){
- var instspd=(event.loaded-this.prevLoaded)/((new Date().getTime()-this.prevT)/1000.0);
- if(isNaN(instspd)||!isFinite(instspd)) return;
- if(this.firstTick){
- this.firstTick=false;
- }else{
- this.speed=this.speed*0.7+instspd*0.3;
- }
- this.prevLoaded=event.loaded;
- this.prevT=new Date().getTime();
- this.onUpdate();
- if(((this.prevT-this.startT)/1000.0)>15){try{this.xhr.abort();}catch(e){} this.onDone();}
- }.bind(this);
- this.xhr.onload=function(){
- this.onUpdate();
- this.onDone();
- }.bind(this);
- this.xhr.onerror=function(){
- this.onUpdate();
- this.onFail();
- }.bind(this);
- this.xhr.open("POST", serverURL+"?random="+Math.random(),true);
- this.xhr.send(new ArrayBuffer(10485760));
-}
-UploadTester.prototype={
- constructor:UploadTester,
- onDone:function(){},
- onFail:function(){},
- onUpdate:function(){},
- getValue:function(){return ((this.speed*8)/1048576.0).toFixed(2);},
- cancel:function(){try{this.xhr.abort();}catch(e){}}
-}
-
-function PingTester(serverURL,done,update,err){
- this.xhr=null;
- this.prevT=null;
- this.ping=0.0;
- this.i=0;
- this.pingURL=serverURL;
- if(done)this.onDone=done;
- if(update)this.onUpdate=update;
- if(err)this.onFail=err;
- this.doPing=function(){
- this.prevT=new Date().getTime();
- this.xhr=new XMLHttpRequest();
- this.xhr.onload=function(){
- if(this.i==0){
- this.prevT=new Date().getTime();
- }else{
- var instspd=new Date().getTime()-this.prevT;
- if(this.i==1) this.ping=instspd; else this.ping=this.ping*0.9+instspd*0.1;
- }
- this.onUpdate();
- this.i++;
- if(this.i<50) this.doPing(); else this.onDone();
- }.bind(this);
- this.xhr.onerror=function(){
- this.onUpdate();
- this.onFail();
- }.bind(this);
- this.xhr.open("GET", this.pingURL+"?random="+Math.random(),true);
- this.xhr.send();
- }.bind(this);
- this.doPing();
-}
-PingTester.prototype={
- constructor:PingTester,
- onDone:function(){},
- onFail:function(){},
- onUpdate:function(){},
- getValue:function(){return this.ping.toFixed(2);},
- cancel:function(){this.i=9999; if(this.xhr) try{xhr.abort();}catch(e){}}
-}
\ No newline at end of file
diff --git a/speedtest_worker.js b/speedtest_worker.js
new file mode 100644
index 0000000..3ccea6e
--- /dev/null
+++ b/speedtest_worker.js
@@ -0,0 +1,104 @@
+var testStatus=0,dlStatus="",ulStatus="",pingStatus="";
+var xhr=null;
+this.addEventListener('message', function(e){
+ var params=e.data.split(" ");
+ if(params[0]=="status"){
+ postMessage(testStatus+";"+dlStatus+";"+ulStatus+";"+pingStatus);
+ }
+ if(params[0]=="start"){
+ if(testStatus==0){
+ testStatus=1;
+ var dlUrl=params[1]?params[1]:"garbage.php", ulUrl=params[2]?params[2]:"empty.dat", pingUrl=params[3]?params[3]:"empty.dat";
+ dlTest(dlUrl,function(){testStatus=2;ulTest(ulUrl,function(){testStatus=3;pingTest(pingUrl,function(){testStatus=4;});});});
+ }
+ }
+ if(params[0]=="abort"){
+ try{if(xhr)xhr.abort();}catch(e){}
+ testStatus=5;dlStatus="";ulStatus="";pingStatus="";
+ }
+});
+
+function dlTest(serverURL,done){
+ var firstTick=true,startT=new Date().getTime(), prevT=new Date().getTime(),prevLoaded=0,speed=0.0;
+ xhr=new XMLHttpRequest();
+ xhr.onprogress=function(event){
+ var instspd=(event.loaded-prevLoaded)/((new Date().getTime()-prevT)/1000.0);
+ if(isNaN(instspd)||!isFinite(instspd)) return;
+ if(firstTick){
+ speed=instspd;
+ firstTick=false;
+ }else{
+ speed=speed*0.9+instspd*0.1;
+ }
+ prevLoaded=event.loaded;
+ prevT=new Date().getTime();
+ dlStatus=((speed*8)/1048576.0).toFixed(2);
+ if(((prevT-startT)/1000.0)>15){try{xhr.abort();}catch(e){} xhr=null; done();}
+ }.bind(this);
+ xhr.onload=function(){
+ dlStatus=((speed*8)/1048576.0).toFixed(2);
+ xhr=null;
+ done();
+ }.bind(this);
+ xhr.onerror=function(){
+ dlStatus="Fail";
+ xhr=null;
+ done();
+ }.bind(this);
+ xhr.open("GET", serverURL+"?random="+Math.random(),true);
+ xhr.send();
+}
+
+function ulTest(serverURL,done){
+ var firstTick=true,startT=new Date().getTime(), prevT=new Date().getTime(),prevLoaded=0,speed=0.0;
+ xhr=new XMLHttpRequest();
+ xhr.upload.onprogress=function(event){
+ var instspd=(event.loaded-prevLoaded)/((new Date().getTime()-prevT)/1000.0);
+ if(isNaN(instspd)||!isFinite(instspd)) return;
+ if(firstTick){
+ firstTick=false;
+ }else{
+ speed=speed*0.7+instspd*0.3;
+ }
+ prevLoaded=event.loaded;
+ prevT=new Date().getTime();
+ ulStatus=((speed*8)/1048576.0).toFixed(2);
+ if(((prevT-startT)/1000.0)>15){try{xhr.abort();}catch(e){} xhr=null; done();}
+ }.bind(this);
+ xhr.onload=function(){
+ ulStatus=((speed*8)/1048576.0).toFixed(2);
+ done();
+ }.bind(this);
+ xhr.onerror=function(){
+ ulStatus="Fail";
+ done();
+ }.bind(this);
+ xhr.open("POST", serverURL+"?random="+Math.random(),true);
+ xhr.send(new ArrayBuffer(10485760));
+}
+
+function pingTest(pingUrl,done){
+ var prevT=null,ping=0.0,i=0;
+ var doPing=function(){
+ prevT=new Date().getTime();
+ xhr=new XMLHttpRequest();
+ xhr.onload=function(){
+ if(i==0){
+ prevT=new Date().getTime();
+ }else{
+ var instspd=new Date().getTime()-prevT;
+ if(i==1) ping=instspd; else ping=ping*0.9+instspd*0.1;
+ }
+ pingStatus=ping.toFixed(2);
+ i++;
+ if(i<50) doPing(); else done();
+ }.bind(this);
+ xhr.onerror=function(){
+ pingStatus="Fail";
+ done();
+ }.bind(this);
+ xhr.open("GET", pingUrl+"?random="+Math.random(),true);
+ xhr.send();
+ }.bind(this);
+ doPing();
+}