diff --git a/doc.md b/doc.md
index 428405b5a..7c553429e 100644
--- a/doc.md
+++ b/doc.md
@@ -1,7 +1,7 @@
# HTML5 Speedtest
> by Federico Dossena
-> Version 4.3.2 Hotfix 1, October 12 2017
+> Version 4.4, October 14 2017
> [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/)
@@ -143,22 +143,26 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}')
* Recommended: `>=20`
* __url_dl__: path to garbage.php or a large file to use for the download test.
* Default: `garbage.php`
- * The string "-1" disables the test
* __Important:__ path is relative to js file
* __url_ul__: path to an empty file or empty.php to use for the upload test
* Default: `empty.php`
- * The string "-1" disables the test
* __Important:__ path is relative to js file
* __url_ping__: path to an empty file or empty.php to use for the ping test
* Default: `empty.php`
- * The string "-1" disables the test
* __Important:__ path is relative to js file
* __url_getIp__: path to getIP.php or replacement
* Default: `getIP.php`
- * The string "-1" disables the test
* __Important:__ path is relative to js file
#### Advanced test parameters
+* __test_order__: the order in which tests will be performed. Each character represents an operation:
+ * `I`: get IP
+ * `D`: download test
+ * `U`: upload test
+ * `P`: ping + jitter test
+ * `_`: delay 1 second
+ * Default test order: `ID_U_P`
+ * __Important:__ Tests can only be run once
* __enable_quirks__: enables browser-specific optimizations. These optimizations override some of the default settings. They do not override settings that are explicitly set.
* Default: `true`
* __garbagePhp_chunkSize__: size of chunks sent by garbage.php in megabytes
diff --git a/example7.html b/example7.html
index f0ed2da88..0e1d18489 100644
--- a/example7.html
+++ b/example7.html
@@ -102,14 +102,11 @@
Jitter
var dl_checkbox = document.getElementById("st-download-checkbox")
var ul_checkbox = document.getElementById("st-upload-checkbox")
var ping_checkbox = document.getElementById("st-ping-checkbox")
- var str_parameters = []
- if (!ip_checkbox.checked) {str_parameters.push('"url_getIp": "-1"')}
- if (!dl_checkbox.checked) {str_parameters.push('"url_dl": "-1"')}
- if (!ul_checkbox.checked) {str_parameters.push('"url_ul": "-1"')}
- if (!ping_checkbox.checked) {str_parameters.push('"url_ping": "-1"')}
+ var test_order=(ip_checkbox.checked?"I":"")+(dl_checkbox.checked?"D":"")+(ul_checkbox.checked?"U":"")+(ping_checkbox.checked?"P":"")
+
+ worker.postMessage('start {"test_order":"'+test_order+'" }')
- worker.postMessage('start {' + str_parameters.join(",") + '}')
}
function stopTest() {
if (worker) worker.postMessage('abort')
diff --git a/speedtest_worker.js b/speedtest_worker.js
index 2c0c72578..a17201b65 100644
--- a/speedtest_worker.js
+++ b/speedtest_worker.js
@@ -1,5 +1,5 @@
/*
- HTML5 Speedtest v4.3.2 Hotfix 1
+ HTML5 Speedtest v4.4
by Federico Dossena
https://github.com/adolfintel/speedtest/
GNU LGPLv3 License
@@ -19,6 +19,7 @@ function twarn(s){log+=Date.now()+' WARN: '+s+'\n'; console.warn(s)}
// test settings. can be overridden by sending specific values with the start command
var settings = {
+ test_order: "ID_U_P", //order in which tests will be performed as a string. D=Download, U=Upload, P=Ping+Jitter, I=IP, _=1 second delay
time_ul: 15, // duration of upload test in seconds
time_dl: 15, // duration of download test in seconds
time_ulGraceTime: 3, //time to wait in seconds before actually measuring ul speed (wait for buffers to fill)
@@ -41,6 +42,8 @@ var settings = {
var xhr = null // array of currently active xhr requests
var interval = null // timer used in tests
+var delayTimer = null // another timer used in test
+var test_pointer = 0 //pointer to the next test to run inside settings.test_order
/*
this function is used on URLs passed in the settings to determine whether we need a ? or an & as a separator
@@ -69,6 +72,7 @@ this.addEventListener('message', function (e) {
var ss = e.data.substring(5)
if (ss) s = JSON.parse(ss)
}catch(e){ twarn('Error parsing custom settings JSON. Please check your syntax') }
+ if (typeof s.test_order !== 'undefined') settings.test_order = s.test_order.toUpperCase() // test order
if (typeof s.url_dl !== 'undefined') settings.url_dl = s.url_dl // download url
if (typeof s.url_ul !== 'undefined') settings.url_ul = s.url_ul // upload url
if (typeof s.url_ping !== 'undefined') settings.url_ping = s.url_ping // ping url
@@ -110,11 +114,24 @@ this.addEventListener('message', function (e) {
} catch (e) { twarn('Possible error in custom test settings. Some settings may not be applied. Exception: '+e) }
// run the tests
tlog(JSON.stringify(settings))
- getIp(function () { dlTest(function () { testStatus = 2; pingTest(function () { testStatus = 3; ulTest(function () { testStatus = 4; sendTelemetry() }) }) }) })
+ test_pointer=0;
+ var runNextTest=function(){
+ if(test_pointer>=settings.test_order.length){testStatus=4; sendTelemetry(); return;}
+ switch(settings.test_order.charAt(test_pointer)){
+ case 'I':{test_pointer++; getIp(runNextTest);} break;
+ case 'D':{test_pointer++; testStatus=1; dlTest(runNextTest);} break;
+ case 'U':{test_pointer++; testStatus=3; ulTest(runNextTest);} break;
+ case 'P':{test_pointer++; testStatus=2; pingTest(runNextTest);} break;
+ case '_':{test_pointer++; delayTimer=setTimeout(runNextTest,1000);} break;
+ default: test_pointer++;
+ }
+ }
+ runNextTest()
}
if (params[0] === 'abort') { // abort command
tlog('manually aborted')
clearRequests() // stop all xhr activity
+ runNextTest=null;
if (interval) clearInterval(interval) // clear timer if present
if (settings.telemetry_level > 1) sendTelemetry()
testStatus = 5; dlStatus = ''; ulStatus = ''; pingStatus = ''; jitterStatus = '' // set test as aborted
@@ -134,9 +151,10 @@ function clearRequests () {
}
}
// gets client's IP using url_getIp, then calls the done function
+var ipCalled = false // used to prevent multiple accidental calls to getIp
function getIp (done) {
tlog('getIp')
- if (settings.url_getIp == "-1") {done(); return}
+ if (ipCalled) return; else ipCalled = true // getIp already called?
xhr = new XMLHttpRequest()
xhr.onload = function () {
tlog("IP: "+xhr.responseText)
@@ -155,7 +173,6 @@ var dlCalled = false // used to prevent multiple accidental calls to dlTest
function dlTest (done) {
tlog('dlTest')
if (dlCalled) return; else dlCalled = true // dlTest already called?
- if (settings.url_dl === '-1') {done(); return}
var totLoaded = 0.0, // total number of loaded bytes
startT = new Date().getTime(), // timestamp when test was started
graceTimeDone = false, //set to true after the grace time is past
@@ -244,7 +261,6 @@ var ulCalled = false // used to prevent multiple accidental calls to ulTest
function ulTest (done) {
tlog('ulTest')
if (ulCalled) return; else ulCalled = true // ulTest already called?
- if (settings.url_ul === '-1') {done(); return}
var totLoaded = 0.0, // total number of transmitted bytes
startT = new Date().getTime(), // timestamp when test was started
graceTimeDone = false, //set to true after the grace time is past
@@ -339,7 +355,7 @@ function ulTest (done) {
if (failed || isNaN(ulStatus)) ulStatus = 'Fail'
clearRequests()
clearInterval(interval)
- tlog('ulTest finished '+ulStatus)
+ tlog('ulTest finished '+ulStatus)
done()
}
}
@@ -350,7 +366,6 @@ var ptCalled = false // used to prevent multiple accidental calls to pingTest
function pingTest (done) {
tlog('pingTest')
if (ptCalled) return; else ptCalled = true // pingTest already called?
- if (settings.url_ping === '-1') {done(); return}
var prevT = null // last time a pong was received
var ping = 0.0 // current ping value
var jitter = 0.0 // current jitter value
diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js
index 85a1eff2d..3dd6734a8 100644
--- a/speedtest_worker.min.js
+++ b/speedtest_worker.min.js
@@ -1 +1 @@
-function tlog(s){log+=Date.now()+": "+s+"\n"}function twarn(s){log+=Date.now()+" WARN: "+s+"\n",console.warn(s)}function url_sep(url){return url.match(/\?/)?"&":"?"}function clearRequests(){if(tlog("stopping pending XHRs"),xhr){for(var i=0;isettings.time_dl&&dlStatus>0||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),tlog("dlTest finished "+dlStatus),done())):t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0))}.bind(this),200)}else done()}function ulTest(done){if(tlog("ulTest"),!ulCalled)if(ulCalled=!0,"-1"!==settings.url_ul){var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){tlog("ul test stream started "+i+" "+delay);var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;if(settings.forceIE11Workaround)ie11workaround=!0;else try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){tlog("ul stream progress event (ie11wa)"),totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){tlog("ul stream failed (ie11wa)"),0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(tlog("ul stream progress event "+i+" "+event.loaded),3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||loadDiff<0||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){tlog("ul stream finished "+i),testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){tlog("ul stream failed "+i),0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;isettings.time_ul&&ulStatus>0||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),tlog("ulTest finished "+ulStatus),done())):t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0))}.bind(this),200)}else done()}function pingTest(done){if(tlog("pingTest"),!ptCalled)if(ptCalled=!0,"-1"!==settings.url_ping){var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){tlog("ping"),prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(tlog("pong"),0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,tlog("PING: "+pingStatus+" JITTER: "+jitterStatus),i1?log:""),xhr.send(fd)}catch(ex){var postData="dl="+encodeURIComponent(dlStatus)+"&ul="+encodeURIComponent(ulStatus)+"&ping="+encodeURIComponent(pingStatus)+"&jitter="+encodeURIComponent(jitterStatus)+"&log="+encodeURIComponent(settings.telemetry_level>1?log:"");xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),xhr.send(postData)}}}var testStatus=0,dlStatus="",ulStatus="",pingStatus="",jitterStatus="",clientIp="",log="",settings={time_ul:15,time_dl:15,time_ulGraceTime:3,time_dlGraceTime:1.5,count_ping:35,url_dl:"garbage.php",url_ul:"empty.php",url_ping:"empty.php",url_getIp:"getIP.php",xhr_dlMultistream:10,xhr_ulMultistream:3,xhr_ignoreErrors:1,xhr_dlUseBlob:!1,garbagePhp_chunkSize:20,enable_quirks:!0,overheadCompensationFactor:1048576/925e3,telemetry_level:0,url_telemetry:"telemetry.php"},xhr=null,interval=null;this.addEventListener("message",function(e){var params=e.data.split(" ");if("status"===params[0]&&postMessage(testStatus+";"+dlStatus+";"+ulStatus+";"+pingStatus+";"+clientIp+";"+jitterStatus),"start"===params[0]&&0===testStatus){testStatus=1;try{var s={};try{var ss=e.data.substring(5);ss&&(s=JSON.parse(ss))}catch(e){twarn("Error parsing custom settings JSON. Please check your syntax")}if(void 0!==s.url_dl&&(settings.url_dl=s.url_dl),void 0!==s.url_ul&&(settings.url_ul=s.url_ul),void 0!==s.url_ping&&(settings.url_ping=s.url_ping),void 0!==s.url_getIp&&(settings.url_getIp=s.url_getIp),void 0!==s.time_dl&&(settings.time_dl=s.time_dl),void 0!==s.time_ul&&(settings.time_ul=s.time_ul),void 0!==s.enable_quirks&&(settings.enable_quirks=s.enable_quirks),settings.enable_quirks){var ua=navigator.userAgent;/Firefox.(\d+\.\d+)/i.test(ua)&&(settings.xhr_ulMultistream=1),/Edge.(\d+\.\d+)/i.test(ua)&&(settings.xhr_dlMultistream=3,(/Edge\/15.(\d+)/i.test(ua)||/Edge\/16.(\d+)/i.test(ua))&&(settings.forceIE11Workaround=!0)),/Chrome.(\d+)/i.test(ua)&&self.fetch&&(settings.xhr_dlMultistream=5)}void 0!==s.count_ping&&(settings.count_ping=s.count_ping),void 0!==s.xhr_dlMultistream&&(settings.xhr_dlMultistream=s.xhr_dlMultistream),void 0!==s.xhr_ulMultistream&&(settings.xhr_ulMultistream=s.xhr_ulMultistream),void 0!==s.xhr_ignoreErrors&&(settings.xhr_ignoreErrors=s.xhr_ignoreErrors),void 0!==s.xhr_dlUseBlob&&(settings.xhr_dlUseBlob=s.xhr_dlUseBlob),void 0!==s.garbagePhp_chunkSize&&(settings.garbagePhp_chunkSize=s.garbagePhp_chunkSize),void 0!==s.time_dlGraceTime&&(settings.time_dlGraceTime=s.time_dlGraceTime),void 0!==s.time_ulGraceTime&&(settings.time_ulGraceTime=s.time_ulGraceTime),void 0!==s.overheadCompensationFactor&&(settings.overheadCompensationFactor=s.overheadCompensationFactor),void 0!==s.telemetry_level&&(settings.telemetry_level="basic"===s.telemetry_level?1:"full"===s.telemetry_level?2:0),void 0!==s.url_telemetry&&(settings.url_telemetry=s.url_telemetry)}catch(e){twarn("Possible error in custom test settings. Some settings may not be applied. Exception: "+e)}tlog(JSON.stringify(settings)),getIp(function(){dlTest(function(){testStatus=2,pingTest(function(){testStatus=3,ulTest(function(){testStatus=4,sendTelemetry()})})})})}"abort"===params[0]&&(tlog("manually aborted"),clearRequests(),interval&&clearInterval(interval),settings.telemetry_level>1&&sendTelemetry(),testStatus=5,dlStatus="",ulStatus="",pingStatus="",jitterStatus="")});var dlCalled=!1,r=new ArrayBuffer(1048576);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){tlog("dl stream finished "+i);try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){tlog("dl stream failed "+i),0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl&&dlStatus>0||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),tlog("dlTest finished "+dlStatus),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(tlog("ulTest"),!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){tlog("ul test stream started "+i+" "+delay);var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;if(settings.forceIE11Workaround)ie11workaround=!0;else try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){tlog("ul stream progress event (ie11wa)"),totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){tlog("ul stream failed (ie11wa)"),0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(tlog("ul stream progress event "+i+" "+event.loaded),3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){tlog("ul stream finished "+i),testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){tlog("ul stream failed "+i),0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul&&ulStatus>0||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),tlog("ulTest finished "+ulStatus),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(tlog("pingTest"),!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){tlog("ping"),prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(tlog("pong"),0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,tlog("PING: "+pingStatus+" JITTER: "+jitterStatus),i1?log:""),xhr.send(fd)}catch(ex){var postData="dl="+encodeURIComponent(dlStatus)+"&ul="+encodeURIComponent(ulStatus)+"&ping="+encodeURIComponent(pingStatus)+"&jitter="+encodeURIComponent(jitterStatus)+"&log="+encodeURIComponent(settings.telemetry_level>1?log:"");xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),xhr.send(postData)}}}var testStatus=0,dlStatus="",ulStatus="",pingStatus="",jitterStatus="",clientIp="",log="",settings={test_order:"ID_U_P",time_ul:15,time_dl:15,time_ulGraceTime:3,time_dlGraceTime:1.5,count_ping:35,url_dl:"garbage.php",url_ul:"empty.php",url_ping:"empty.php",url_getIp:"getIP.php",xhr_dlMultistream:10,xhr_ulMultistream:3,xhr_ignoreErrors:1,xhr_dlUseBlob:!1,garbagePhp_chunkSize:20,enable_quirks:!0,overheadCompensationFactor:1048576/925e3,telemetry_level:0,url_telemetry:"telemetry.php"},xhr=null,interval=null,delayTimer=null,test_pointer=0;this.addEventListener("message",function(e){var params=e.data.split(" ");if("status"===params[0]&&postMessage(testStatus+";"+dlStatus+";"+ulStatus+";"+pingStatus+";"+clientIp+";"+jitterStatus),"start"===params[0]&&0===testStatus){testStatus=1;try{var s={};try{var ss=e.data.substring(5);ss&&(s=JSON.parse(ss))}catch(e){twarn("Error parsing custom settings JSON. Please check your syntax")}if("undefined"!=typeof s.test_order&&(settings.test_order=s.test_order.toUpperCase()),"undefined"!=typeof s.url_dl&&(settings.url_dl=s.url_dl),"undefined"!=typeof s.url_ul&&(settings.url_ul=s.url_ul),"undefined"!=typeof s.url_ping&&(settings.url_ping=s.url_ping),"undefined"!=typeof s.url_getIp&&(settings.url_getIp=s.url_getIp),"undefined"!=typeof s.time_dl&&(settings.time_dl=s.time_dl),"undefined"!=typeof s.time_ul&&(settings.time_ul=s.time_ul),"undefined"!=typeof s.enable_quirks&&(settings.enable_quirks=s.enable_quirks),settings.enable_quirks){var ua=navigator.userAgent;/Firefox.(\d+\.\d+)/i.test(ua)&&(settings.xhr_ulMultistream=1),/Edge.(\d+\.\d+)/i.test(ua)&&(settings.xhr_dlMultistream=3,(/Edge\/15.(\d+)/i.test(ua)||/Edge\/16.(\d+)/i.test(ua))&&(settings.forceIE11Workaround=!0)),/Chrome.(\d+)/i.test(ua)&&self.fetch&&(settings.xhr_dlMultistream=5)}"undefined"!=typeof s.count_ping&&(settings.count_ping=s.count_ping),"undefined"!=typeof s.xhr_dlMultistream&&(settings.xhr_dlMultistream=s.xhr_dlMultistream),"undefined"!=typeof s.xhr_ulMultistream&&(settings.xhr_ulMultistream=s.xhr_ulMultistream),"undefined"!=typeof s.xhr_ignoreErrors&&(settings.xhr_ignoreErrors=s.xhr_ignoreErrors),"undefined"!=typeof s.xhr_dlUseBlob&&(settings.xhr_dlUseBlob=s.xhr_dlUseBlob),"undefined"!=typeof s.garbagePhp_chunkSize&&(settings.garbagePhp_chunkSize=s.garbagePhp_chunkSize),"undefined"!=typeof s.time_dlGraceTime&&(settings.time_dlGraceTime=s.time_dlGraceTime),"undefined"!=typeof s.time_ulGraceTime&&(settings.time_ulGraceTime=s.time_ulGraceTime),"undefined"!=typeof s.overheadCompensationFactor&&(settings.overheadCompensationFactor=s.overheadCompensationFactor),"undefined"!=typeof s.telemetry_level&&(settings.telemetry_level="basic"===s.telemetry_level?1:"full"===s.telemetry_level?2:0),"undefined"!=typeof s.url_telemetry&&(settings.url_telemetry=s.url_telemetry)}catch(e){twarn("Possible error in custom test settings. Some settings may not be applied. Exception: "+e)}tlog(JSON.stringify(settings)),test_pointer=0;var runNextTest=function(){if(test_pointer>=settings.test_order.length)return testStatus=4,void sendTelemetry();switch(settings.test_order.charAt(test_pointer)){case"I":test_pointer++,getIp(runNextTest);break;case"D":test_pointer++,testStatus=1,dlTest(runNextTest);break;case"U":test_pointer++,testStatus=3,ulTest(runNextTest);break;case"P":test_pointer++,testStatus=2,pingTest(runNextTest);break;case"_":test_pointer++,delayTimer=setTimeout(runNextTest,1e3);break;default:test_pointer++}};runNextTest()}"abort"===params[0]&&(tlog("manually aborted"),clearRequests(),runNextTest=null,interval&&clearInterval(interval),settings.telemetry_level>1&&sendTelemetry(),testStatus=5,dlStatus="",ulStatus="",pingStatus="",jitterStatus="")});var ipCalled=!1,dlCalled=!1,r=new ArrayBuffer(1048576);try{r=new Float32Array(r);for(var i=0;ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i