|
@@ -5,345 +5,346 @@
|
|
|
GNU LGPLv3 License
|
|
GNU LGPLv3 License
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
-//data reported to main thread
|
|
|
|
|
-var testStatus=0, //0=not started, 1=download test, 2=ping+jitter test, 3=upload test, 4=finished, 5=abort/error
|
|
|
|
|
- dlStatus="", //download speed in megabit/s with 2 decimal digits
|
|
|
|
|
- ulStatus="", //upload speed in megabit/s with 2 decimal digits
|
|
|
|
|
- pingStatus="", //ping in milliseconds with 2 decimal digits
|
|
|
|
|
- jitterStatus="", //jitter in milliseconds with 2 decimal digits
|
|
|
|
|
- clientIp=""; //client's IP address as reported by getIP.php
|
|
|
|
|
|
|
+// data reported to main thread
|
|
|
|
|
+var testStatus = 0 // 0=not started, 1=download test, 2=ping+jitter test, 3=upload test, 4=finished, 5=abort/error
|
|
|
|
|
+var dlStatus = '' // download speed in megabit/s with 2 decimal digits
|
|
|
|
|
+var ulStatus = '' // upload speed in megabit/s with 2 decimal digits
|
|
|
|
|
+var pingStatus = '' // ping in milliseconds with 2 decimal digits
|
|
|
|
|
+var jitterStatus = '' // jitter in milliseconds with 2 decimal digits
|
|
|
|
|
+var clientIp = '' // client's IP address as reported by getIP.php
|
|
|
|
|
|
|
|
-//test settings. can be overridden by sending specific values with the start command
|
|
|
|
|
-var settings={
|
|
|
|
|
- time_ul:15, //duration of upload test in seconds
|
|
|
|
|
- time_dl:15, //duration of download test in seconds
|
|
|
|
|
- count_ping:35, //number of pings to perform in ping test
|
|
|
|
|
- url_dl:"garbage.php", //path to a large file or garbage.php, used for download test. must be relative to this js file
|
|
|
|
|
- url_ul:"empty.dat", //path to an empty file, used for upload test. must be relative to this js file
|
|
|
|
|
- url_ping:"empty.dat", //path to an empty file, used for ping test. must be relative to this js file
|
|
|
|
|
- url_getIp:"getIP.php", //path to getIP.php relative to this js file, or a similar thing that outputs the client's ip
|
|
|
|
|
- xhr_dlMultistream:10, //number of download streams to use (can be different if enable_quirks is active)
|
|
|
|
|
- xhr_ulMultistream:3, //number of upload streams to use (can be different if enable_quirks is active)
|
|
|
|
|
- xhr_dlUseBlob:false, //if set to true, it reduces ram usage but uses the hard drive (useful with large garbagePhp_chunkSize and/or high xhr_dlMultistream)
|
|
|
|
|
- garbagePhp_chunkSize:20, //size of chunks sent by garbage.php (can be different if enable_quirks is active)
|
|
|
|
|
- enable_quirks:true, //enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers, unless they are already being overridden with the start command
|
|
|
|
|
- allow_fetchAPI:false, //enables Fetch API. currently disabled because it leaks memory like no tomorrow
|
|
|
|
|
- force_fetchAPI:false //when Fetch API is enabled, it will force usage on every browser that supports it
|
|
|
|
|
-};
|
|
|
|
|
|
|
+// test settings. can be overridden by sending specific values with the start command
|
|
|
|
|
+var settings = {
|
|
|
|
|
+ time_ul: 15, // duration of upload test in seconds
|
|
|
|
|
+ time_dl: 15, // duration of download test in seconds
|
|
|
|
|
+ count_ping: 35, // number of pings to perform in ping test
|
|
|
|
|
+ url_dl: 'garbage.php', // path to a large file or garbage.php, used for download test. must be relative to this js file
|
|
|
|
|
+ url_ul: 'empty.dat', // path to an empty file, used for upload test. must be relative to this js file
|
|
|
|
|
+ url_ping: 'empty.dat', // path to an empty file, used for ping test. must be relative to this js file
|
|
|
|
|
+ url_getIp: 'getIP.php', // path to getIP.php relative to this js file, or a similar thing that outputs the client's ip
|
|
|
|
|
+ xhr_dlMultistream: 10, // number of download streams to use (can be different if enable_quirks is active)
|
|
|
|
|
+ xhr_ulMultistream: 3, // number of upload streams to use (can be different if enable_quirks is active)
|
|
|
|
|
+ xhr_dlUseBlob: false, // if set to true, it reduces ram usage but uses the hard drive (useful with large garbagePhp_chunkSize and/or high xhr_dlMultistream)
|
|
|
|
|
+ garbagePhp_chunkSize: 20, // size of chunks sent by garbage.php (can be different if enable_quirks is active)
|
|
|
|
|
+ enable_quirks: true, // enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers, unless they are already being overridden with the start command
|
|
|
|
|
+ allow_fetchAPI: false, // enables Fetch API. currently disabled because it leaks memory like no tomorrow
|
|
|
|
|
+ force_fetchAPI: false // when Fetch API is enabled, it will force usage on every browser that supports it
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+var xhr = null // array of currently active xhr requests
|
|
|
|
|
+var interval = null // timer used in tests
|
|
|
|
|
|
|
|
-var xhr=null, //array of currently active xhr requests
|
|
|
|
|
- interval=null; //timer used in tests
|
|
|
|
|
-
|
|
|
|
|
/*
|
|
/*
|
|
|
when set to true (automatically) the download test will use the fetch api instead of xhr.
|
|
when set to true (automatically) the download test will use the fetch api instead of xhr.
|
|
|
fetch api is used if
|
|
fetch api is used if
|
|
|
-allow_fetchAPI is true AND
|
|
-allow_fetchAPI is true AND
|
|
|
-(we're on chrome that supports fetch api AND enable_quirks is true) OR (we're on any browser that supports fetch api AND force_fetchAPI is true)
|
|
-(we're on chrome that supports fetch api AND enable_quirks is true) OR (we're on any browser that supports fetch api AND force_fetchAPI is true)
|
|
|
*/
|
|
*/
|
|
|
-var useFetchAPI=false;
|
|
|
|
|
-
|
|
|
|
|
|
|
+var useFetchAPI = false
|
|
|
|
|
+
|
|
|
/*
|
|
/*
|
|
|
listener for commands from main thread to this worker.
|
|
listener for commands from main thread to this worker.
|
|
|
commands:
|
|
commands:
|
|
|
-status: returns the current status as a string of values spearated by a semicolon (;) in this order: testStatus;dlStatus;ulStatus;pingStatus;clientIp;jitterStatus
|
|
-status: returns the current status as a string of values spearated by a semicolon (;) in this order: testStatus;dlStatus;ulStatus;pingStatus;clientIp;jitterStatus
|
|
|
-abort: aborts the current test
|
|
-abort: aborts the current test
|
|
|
- -start: starts the test. optionally, settings can be passed as JSON.
|
|
|
|
|
|
|
+ -start: starts the test. optionally, settings can be passed as JSON.
|
|
|
example: start {"time_ul":"10", "time_dl":"10", "count_ping":"50"}
|
|
example: start {"time_ul":"10", "time_dl":"10", "count_ping":"50"}
|
|
|
*/
|
|
*/
|
|
|
-this.addEventListener('message', function(e){
|
|
|
|
|
- var params=e.data.split(" ");
|
|
|
|
|
- if(params[0]=="status"){ //return status
|
|
|
|
|
- postMessage(testStatus+";"+dlStatus+";"+ulStatus+";"+pingStatus+";"+clientIp+";"+jitterStatus);
|
|
|
|
|
- }
|
|
|
|
|
- if(params[0]=="start"&&testStatus==0){ //start new test
|
|
|
|
|
- testStatus=1;
|
|
|
|
|
- try{
|
|
|
|
|
- //parse settings, if present
|
|
|
|
|
- var s=JSON.parse(e.data.substring(5));
|
|
|
|
|
- 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
|
|
|
|
|
- if(typeof s.url_getIp != "undefined") settings.url_getIp=s.url_getIp; //url to getIP.php
|
|
|
|
|
- if(typeof s.time_dl != "undefined") settings.time_dl=s.time_dl; //duration of download test
|
|
|
|
|
- if(typeof s.time_ul != "undefined") settings.time_ul=s.time_ul; //duration of upload test
|
|
|
|
|
- if(typeof s.enable_quirks != "undefined") settings.enable_quirks=s.enable_quirks; //enable quirks or not
|
|
|
|
|
- if(typeof s.allow_fetchAPI != "undefined") settings.allow_fetchAPI=s.allow_fetchAPI; //allows fetch api to be used if supported
|
|
|
|
|
- //quirks for specific browsers. more may be added in future releases
|
|
|
|
|
- if(settings.enable_quirks){
|
|
|
|
|
- var ua=navigator.userAgent;
|
|
|
|
|
- if(/Firefox.(\d+\.\d+)/i.test(ua)){
|
|
|
|
|
- //ff more precise with 1 upload stream
|
|
|
|
|
- settings.xhr_ulMultistream=1;
|
|
|
|
|
- }
|
|
|
|
|
- if(/Edge.(\d+\.\d+)/i.test(ua)){
|
|
|
|
|
- //edge more precise with 3 download streams
|
|
|
|
|
- settings.xhr_dlMultistream=3;
|
|
|
|
|
- }
|
|
|
|
|
- if((/Safari.(\d+)/i.test(ua))&&!(/Chrome.(\d+)/i.test(ua))){
|
|
|
|
|
- //safari more precise with 10 upload streams and 5mb chunks for download test
|
|
|
|
|
- settings.xhr_ulMultistream=10;
|
|
|
|
|
- settings.garbagePhp_chunkSize=5;
|
|
|
|
|
- }
|
|
|
|
|
- if(/Chrome.(\d+)/i.test(ua)&&(!!self.fetch)){
|
|
|
|
|
- //chrome can't handle large xhr very well, use fetch api if available and allowed
|
|
|
|
|
- if(settings.allow_fetchAPI) useFetchAPI=true;
|
|
|
|
|
- //chrome more precise with 5 streams
|
|
|
|
|
- settings.xhr_dlMultistream=5;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if(typeof s.count_ping != "undefined") settings.count_ping=s.count_ping; //number of pings for ping test
|
|
|
|
|
- if(typeof s.xhr_dlMultistream != "undefined") settings.xhr_dlMultistream=s.xhr_dlMultistream; //number of download streams
|
|
|
|
|
- if(typeof s.xhr_ulMultistream != "undefined") settings.xhr_ulMultistream=s.xhr_ulMultistream; //number of upload streams
|
|
|
|
|
- if(typeof s.xhr_dlUseBlob != "undefined") settings.xhr_dlUseBlob=s.xhr_dlUseBlob; //use blob for download test
|
|
|
|
|
- if(typeof s.garbagePhp_chunkSize != "undefined") settings.garbagePhp_chunkSize=s.garbagePhp_chunkSize; //size of garbage.php chunks
|
|
|
|
|
- if(typeof s.force_fetchAPI != "undefined") settings.force_fetchAPI=s.force_fetchAPI; //use fetch api on all browsers that support it if enabled
|
|
|
|
|
- if(settings.allow_fetchAPI&&settings.force_fetchAPI&&(!!self.fetch)) useFetchAPI=true;
|
|
|
|
|
- }catch(e){}
|
|
|
|
|
- //run the tests
|
|
|
|
|
- console.log(settings);
|
|
|
|
|
- console.log("Fetch API: "+useFetchAPI);
|
|
|
|
|
- getIp(function(){dlTest(function(){testStatus=2;pingTest(function(){testStatus=3;ulTest(function(){testStatus=4;});});})});
|
|
|
|
|
- }
|
|
|
|
|
- if(params[0]=="abort"){ //abort command
|
|
|
|
|
- clearRequests(); //stop all xhr activity
|
|
|
|
|
- if(interval)clearInterval(interval); //clear timer if present
|
|
|
|
|
- testStatus=5;dlStatus="";ulStatus="";pingStatus="";jitterStatus=""; //set test as aborted
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-//stops all XHR activity, aggressively
|
|
|
|
|
-function clearRequests(){
|
|
|
|
|
- if(xhr){
|
|
|
|
|
- for(var i=0;i<xhr.length;i++){
|
|
|
|
|
- if(useFetchAPI)try{xhr[i].cancelRequested=true;}catch(e){}
|
|
|
|
|
- try{xhr[i].onprogress=null; xhr[i].onload=null; xhr[i].onerror=null;}catch(e){}
|
|
|
|
|
- try{xhr[i].upload.onprogress=null; xhr[i].upload.onload=null; xhr[i].upload.onerror=null;}catch(e){}
|
|
|
|
|
- try{xhr[i].abort();}catch(e){}
|
|
|
|
|
- try{delete(xhr[i]);}catch(e){}
|
|
|
|
|
- }
|
|
|
|
|
- xhr=null;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+this.addEventListener('message', function (e) {
|
|
|
|
|
+ var params = e.data.split(' ')
|
|
|
|
|
+ if (params[0] === 'status') { // return status
|
|
|
|
|
+ postMessage(testStatus + ';' + dlStatus + ';' + ulStatus + ';' + pingStatus + ';' + clientIp + ';' + jitterStatus)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (params[0] === 'start' && testStatus === 0) { // start new test
|
|
|
|
|
+ testStatus = 1
|
|
|
|
|
+ try {
|
|
|
|
|
+ // parse settings, if present
|
|
|
|
|
+ var s = JSON.parse(e.data.substring(5))
|
|
|
|
|
+ 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
|
|
|
|
|
+ if (typeof s.url_getIp !== 'undefined') settings.url_getIp = s.url_getIp // url to getIP.php
|
|
|
|
|
+ if (typeof s.time_dl !== 'undefined') settings.time_dl = s.time_dl // duration of download test
|
|
|
|
|
+ if (typeof s.time_ul !== 'undefined') settings.time_ul = s.time_ul // duration of upload test
|
|
|
|
|
+ if (typeof s.enable_quirks !== 'undefined') settings.enable_quirks = s.enable_quirks // enable quirks or not
|
|
|
|
|
+ if (typeof s.allow_fetchAPI !== 'undefined') settings.allow_fetchAPI = s.allow_fetchAPI // allows fetch api to be used if supported
|
|
|
|
|
+ // quirks for specific browsers. more may be added in future releases
|
|
|
|
|
+ if (settings.enable_quirks) {
|
|
|
|
|
+ var ua = navigator.userAgent
|
|
|
|
|
+ if (/Firefox.(\d+\.\d+)/i.test(ua)) {
|
|
|
|
|
+ // ff more precise with 1 upload stream
|
|
|
|
|
+ settings.xhr_ulMultistream = 1
|
|
|
|
|
+ }
|
|
|
|
|
+ if (/Edge.(\d+\.\d+)/i.test(ua)) {
|
|
|
|
|
+ // edge more precise with 3 download streams
|
|
|
|
|
+ settings.xhr_dlMultistream = 3
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((/Safari.(\d+)/i.test(ua)) && !(/Chrome.(\d+)/i.test(ua))) {
|
|
|
|
|
+ // safari more precise with 10 upload streams and 5mb chunks for download test
|
|
|
|
|
+ settings.xhr_ulMultistream = 10
|
|
|
|
|
+ settings.garbagePhp_chunkSize = 5
|
|
|
|
|
+ }
|
|
|
|
|
+ if (/Chrome.(\d+)/i.test(ua) && (!!self.fetch)) {
|
|
|
|
|
+ // chrome can't handle large xhr very well, use fetch api if available and allowed
|
|
|
|
|
+ if (settings.allow_fetchAPI) useFetchAPI = true
|
|
|
|
|
+ // chrome more precise with 5 streams
|
|
|
|
|
+ settings.xhr_dlMultistream = 5
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof s.count_ping !== 'undefined') settings.count_ping = s.count_ping // number of pings for ping test
|
|
|
|
|
+ if (typeof s.xhr_dlMultistream !== 'undefined') settings.xhr_dlMultistream = s.xhr_dlMultistream // number of download streams
|
|
|
|
|
+ if (typeof s.xhr_ulMultistream !== 'undefined') settings.xhr_ulMultistream = s.xhr_ulMultistream // number of upload streams
|
|
|
|
|
+ if (typeof s.xhr_dlUseBlob !== 'undefined') settings.xhr_dlUseBlob = s.xhr_dlUseBlob // use blob for download test
|
|
|
|
|
+ if (typeof s.garbagePhp_chunkSize !== 'undefined') settings.garbagePhp_chunkSize = s.garbagePhp_chunkSize // size of garbage.php chunks
|
|
|
|
|
+ if (typeof s.force_fetchAPI !== 'undefined') settings.force_fetchAPI = s.force_fetchAPI // use fetch api on all browsers that support it if enabled
|
|
|
|
|
+ if (settings.allow_fetchAPI && settings.force_fetchAPI && (!!self.fetch)) useFetchAPI = true
|
|
|
|
|
+ } catch (e) { }
|
|
|
|
|
+ // run the tests
|
|
|
|
|
+ console.log(settings)
|
|
|
|
|
+ console.log('Fetch API: ' + useFetchAPI)
|
|
|
|
|
+ getIp(function () { dlTest(function () { testStatus = 2; pingTest(function () { testStatus = 3; ulTest(function () { testStatus = 4 }) }) }) })
|
|
|
|
|
+ }
|
|
|
|
|
+ if (params[0] === 'abort') { // abort command
|
|
|
|
|
+ clearRequests() // stop all xhr activity
|
|
|
|
|
+ if (interval) clearInterval(interval) // clear timer if present
|
|
|
|
|
+ testStatus = 5; dlStatus = ''; ulStatus = ''; pingStatus = ''; jitterStatus = '' // set test as aborted
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+// stops all XHR activity, aggressively
|
|
|
|
|
+function clearRequests () {
|
|
|
|
|
+ if (xhr) {
|
|
|
|
|
+ for (var i = 0; i < xhr.length; i++) {
|
|
|
|
|
+ if (useFetchAPI) try { xhr[i].cancelRequested = true } catch (e) { }
|
|
|
|
|
+ try { xhr[i].onprogress = null; xhr[i].onload = null; xhr[i].onerror = null } catch (e) { }
|
|
|
|
|
+ try { xhr[i].upload.onprogress = null; xhr[i].upload.onload = null; xhr[i].upload.onerror = null } catch (e) { }
|
|
|
|
|
+ try { xhr[i].abort() } catch (e) { }
|
|
|
|
|
+ try { delete (xhr[i]) } catch (e) { }
|
|
|
|
|
+ }
|
|
|
|
|
+ xhr = null
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-//gets client's IP using url_getIp, then calls the done function
|
|
|
|
|
-function getIp(done){
|
|
|
|
|
- xhr=new XMLHttpRequest();
|
|
|
|
|
- xhr.onload=function(){
|
|
|
|
|
- clientIp=xhr.responseText;
|
|
|
|
|
- done();
|
|
|
|
|
- }
|
|
|
|
|
- xhr.onerror=function(){
|
|
|
|
|
- done();
|
|
|
|
|
- }
|
|
|
|
|
- xhr.open("GET",settings.url_getIp+"?r="+Math.random(),true);
|
|
|
|
|
- xhr.send();
|
|
|
|
|
|
|
+// gets client's IP using url_getIp, then calls the done function
|
|
|
|
|
+function getIp (done) {
|
|
|
|
|
+ xhr = new XMLHttpRequest()
|
|
|
|
|
+ xhr.onload = function () {
|
|
|
|
|
+ clientIp = xhr.responseText
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ xhr.onerror = function () {
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ xhr.open('GET', settings.url_getIp + '?r=' + Math.random(), true)
|
|
|
|
|
+ xhr.send()
|
|
|
}
|
|
}
|
|
|
-//download test, calls done function when it's over
|
|
|
|
|
-var dlCalled=false; //used to prevent multiple accidental calls to dlTest
|
|
|
|
|
-function dlTest(done){
|
|
|
|
|
- if(dlCalled) return; else dlCalled=true; //dlTest already called?
|
|
|
|
|
- var totLoaded=0.0, //total number of loaded bytes
|
|
|
|
|
- startT=new Date().getTime(), //timestamp when test was started
|
|
|
|
|
- failed=false; //set to true if a stream fails
|
|
|
|
|
- xhr=[];
|
|
|
|
|
- //function to create a download stream. streams are slightly delayed so that they will not end at the same time
|
|
|
|
|
- var testStream=function(i,delay){
|
|
|
|
|
- setTimeout(function(){
|
|
|
|
|
- if(testStatus!=1) return; //delayed stream ended up starting after the end of the download test
|
|
|
|
|
- if(useFetchAPI){
|
|
|
|
|
- xhr[i]=fetch(settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize).then(function(response) {
|
|
|
|
|
- var reader = response.body.getReader();
|
|
|
|
|
- var consume=function() {
|
|
|
|
|
- return reader.read().then(function(result){
|
|
|
|
|
- if(result.done) testStream(i); else{
|
|
|
|
|
- totLoaded+=result.value.length;
|
|
|
|
|
- if(xhr[i].cancelRequested) reader.cancel();
|
|
|
|
|
- }
|
|
|
|
|
- return consume();
|
|
|
|
|
- }.bind(this));
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- return consume();
|
|
|
|
|
- }.bind(this));
|
|
|
|
|
- }else{
|
|
|
|
|
- var prevLoaded=0; //number of bytes loaded last time onprogress was called
|
|
|
|
|
- var x=new XMLHttpRequest();
|
|
|
|
|
- xhr[i]=x;
|
|
|
|
|
- xhr[i].onprogress=function(event){
|
|
|
|
|
- if(testStatus!=1){try{x.abort();}catch(e){}} //just in case this XHR is still running after the download test
|
|
|
|
|
- //progress event, add number of new loaded bytes to totLoaded
|
|
|
|
|
- var loadDiff=event.loaded<=0?0:(event.loaded-prevLoaded);
|
|
|
|
|
- if(isNaN(loadDiff)||!isFinite(loadDiff)||loadDiff<0) return; //just in case
|
|
|
|
|
- totLoaded+=loadDiff;
|
|
|
|
|
- prevLoaded=event.loaded;
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- xhr[i].onload=function(){
|
|
|
|
|
- //the large file has been loaded entirely, start again
|
|
|
|
|
- try{xhr[i].abort();}catch(e){} //reset the stream data to empty ram
|
|
|
|
|
- testStream(i,0);
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- xhr[i].onerror=function(){
|
|
|
|
|
- //error, abort
|
|
|
|
|
- failed=true;
|
|
|
|
|
- try{xhr[i].abort();}catch(e){}
|
|
|
|
|
- delete(xhr[i]);
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- //send xhr
|
|
|
|
|
- try{if(settings.xhr_dlUseBlob) xhr[i].responseType='blob'; else xhr[i].responseType='arraybuffer';}catch(e){}
|
|
|
|
|
- xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,true); //random string to prevent caching
|
|
|
|
|
- xhr[i].send();
|
|
|
|
|
- }
|
|
|
|
|
- }.bind(this),1+delay);
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- //open streams
|
|
|
|
|
- for(var i=0;i<settings.xhr_dlMultistream;i++){
|
|
|
|
|
- testStream(i,100*i);
|
|
|
|
|
- }
|
|
|
|
|
- //every 200ms, update dlStatus
|
|
|
|
|
- interval=setInterval(function(){
|
|
|
|
|
- var t=new Date().getTime()-startT;
|
|
|
|
|
- if(t<200) return;
|
|
|
|
|
- var speed=totLoaded/(t/1000.0);
|
|
|
|
|
- dlStatus=((speed*8)/925000.0).toFixed(2); //925000 instead of 1048576 to account for overhead
|
|
|
|
|
- if((t/1000.0)>settings.time_dl||failed){ //test is over, stop streams and timer
|
|
|
|
|
- if(failed||isNaN(dlStatus)) dlStatus="Fail";
|
|
|
|
|
- clearRequests();
|
|
|
|
|
- clearInterval(interval);
|
|
|
|
|
- done();
|
|
|
|
|
- }
|
|
|
|
|
- }.bind(this),200);
|
|
|
|
|
|
|
+// download test, calls done function when it's over
|
|
|
|
|
+var dlCalled = false // used to prevent multiple accidental calls to dlTest
|
|
|
|
|
+function dlTest (done) {
|
|
|
|
|
+ if (dlCalled) return; else dlCalled = true // dlTest already called?
|
|
|
|
|
+ var totLoaded = 0.0, // total number of loaded bytes
|
|
|
|
|
+ startT = new Date().getTime(), // timestamp when test was started
|
|
|
|
|
+ failed = false // set to true if a stream fails
|
|
|
|
|
+ xhr = []
|
|
|
|
|
+ // function to create a download stream. streams are slightly delayed so that they will not end at the same time
|
|
|
|
|
+ var testStream = function (i, delay) {
|
|
|
|
|
+ setTimeout(function () {
|
|
|
|
|
+ if (testStatus !== 1) return // delayed stream ended up starting after the end of the download test
|
|
|
|
|
+ if (useFetchAPI) {
|
|
|
|
|
+ xhr[i] = fetch(settings.url_dl + '?r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize).then(function (response) {
|
|
|
|
|
+ var reader = response.body.getReader()
|
|
|
|
|
+ var consume = function () {
|
|
|
|
|
+ return reader.read().then(function (result) {
|
|
|
|
|
+ if (result.done) testStream(i); else {
|
|
|
|
|
+ totLoaded += result.value.length
|
|
|
|
|
+ if (xhr[i].cancelRequested) reader.cancel()
|
|
|
|
|
+ }
|
|
|
|
|
+ return consume()
|
|
|
|
|
+ }.bind(this))
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ return consume()
|
|
|
|
|
+ }.bind(this))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ var prevLoaded = 0 // number of bytes loaded last time onprogress was called
|
|
|
|
|
+ var x = new XMLHttpRequest()
|
|
|
|
|
+ xhr[i] = x
|
|
|
|
|
+ xhr[i].onprogress = function (event) {
|
|
|
|
|
+ if (testStatus !== 1) { try { x.abort() } catch (e) { } } // just in case this XHR is still running after the download test
|
|
|
|
|
+ // progress event, add number of new loaded bytes to totLoaded
|
|
|
|
|
+ var loadDiff = event.loaded <= 0 ? 0 : (event.loaded - prevLoaded)
|
|
|
|
|
+ if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return // just in case
|
|
|
|
|
+ totLoaded += loadDiff
|
|
|
|
|
+ prevLoaded = event.loaded
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ xhr[i].onload = function () {
|
|
|
|
|
+ // the large file has been loaded entirely, start again
|
|
|
|
|
+ try { xhr[i].abort() } catch (e) { } // reset the stream data to empty ram
|
|
|
|
|
+ testStream(i, 0)
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ xhr[i].onerror = function () {
|
|
|
|
|
+ // error, abort
|
|
|
|
|
+ failed = true
|
|
|
|
|
+ try { xhr[i].abort() } catch (e) { }
|
|
|
|
|
+ delete (xhr[i])
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ // send xhr
|
|
|
|
|
+ try { if (settings.xhr_dlUseBlob) xhr[i].responseType = 'blob'; else xhr[i].responseType = 'arraybuffer' } catch (e) { }
|
|
|
|
|
+ xhr[i].open('GET', settings.url_dl + '?r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize, true) // random string to prevent caching
|
|
|
|
|
+ xhr[i].send()
|
|
|
|
|
+ }
|
|
|
|
|
+ }.bind(this), 1 + delay)
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ // open streams
|
|
|
|
|
+ for (var i = 0; i < settings.xhr_dlMultistream; i++) {
|
|
|
|
|
+ testStream(i, 100 * i)
|
|
|
|
|
+ }
|
|
|
|
|
+ // every 200ms, update dlStatus
|
|
|
|
|
+ interval = setInterval(function () {
|
|
|
|
|
+ var t = new Date().getTime() - startT
|
|
|
|
|
+ if (t < 200) return
|
|
|
|
|
+ var speed = totLoaded / (t / 1000.0)
|
|
|
|
|
+ dlStatus = ((speed * 8) / 925000.0).toFixed(2) // 925000 instead of 1048576 to account for overhead
|
|
|
|
|
+ if ((t / 1000.0) > settings.time_dl || failed) { // test is over, stop streams and timer
|
|
|
|
|
+ if (failed || isNaN(dlStatus)) dlStatus = 'Fail'
|
|
|
|
|
+ clearRequests()
|
|
|
|
|
+ clearInterval(interval)
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ }.bind(this), 200)
|
|
|
}
|
|
}
|
|
|
-//upload test, calls done function whent it's over
|
|
|
|
|
-//garbage data for upload test
|
|
|
|
|
-var r=new ArrayBuffer(1048576);
|
|
|
|
|
-try{r=new Float32Array(r);for(var i=0;i<r.length;i++)r[i]=Math.random();}catch(e){}
|
|
|
|
|
-var req=[],reqsmall=[];
|
|
|
|
|
-for(var i=0;i<20;i++) req.push(r);
|
|
|
|
|
-req=new Blob(req);
|
|
|
|
|
-r=new ArrayBuffer(262144);
|
|
|
|
|
-try{r=new Float32Array(r);for(var i=0;i<r.length;i++)r[i]=Math.random();}catch(e){}
|
|
|
|
|
-reqsmall.push(r);
|
|
|
|
|
-reqsmall=new Blob(reqsmall);
|
|
|
|
|
-var ulCalled=false; //used to prevent multiple accidental calls to ulTest
|
|
|
|
|
-function ulTest(done){
|
|
|
|
|
- if(ulCalled) return; else ulCalled=true; //ulTest already called?
|
|
|
|
|
- var totLoaded=0.0, //total number of transmitted bytes
|
|
|
|
|
- startT=new Date().getTime(), //timestamp when test was started
|
|
|
|
|
- failed=false; //set to true if a stream fails
|
|
|
|
|
- xhr=[];
|
|
|
|
|
- //function to create an upload stream. streams are slightly delayed so that they will not end at the same time
|
|
|
|
|
- var testStream=function(i,delay){
|
|
|
|
|
- setTimeout(function(){
|
|
|
|
|
- if(testStatus!=3) return; //delayed stream ended up starting after the end of the upload test
|
|
|
|
|
- var prevLoaded=0; //number of bytes transmitted last time onprogress was called
|
|
|
|
|
- var x=new XMLHttpRequest();
|
|
|
|
|
- xhr[i]=x;
|
|
|
|
|
- var ie11workaround;
|
|
|
|
|
- try{
|
|
|
|
|
- xhr[i].upload.onprogress;
|
|
|
|
|
- ie11workaround=false;
|
|
|
|
|
- }catch(e){
|
|
|
|
|
- ie11workaround=true;
|
|
|
|
|
- }
|
|
|
|
|
- if(ie11workaround){
|
|
|
|
|
- //IE11 workarond: xhr.upload does not work properly, therefore we send a bunch of small 256k requests and use the onload event as progress. This is not precise, especially on fast connections
|
|
|
|
|
- xhr[i].onload=function(){
|
|
|
|
|
- totLoaded+=262144;
|
|
|
|
|
- testStream(i,0);
|
|
|
|
|
- }
|
|
|
|
|
- xhr[i].onerror=function(){
|
|
|
|
|
- //error, abort
|
|
|
|
|
- failed=true;
|
|
|
|
|
- try{xhr[i].abort();}catch(e){}
|
|
|
|
|
- delete(xhr[i]);
|
|
|
|
|
- }
|
|
|
|
|
- xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),true); //random string to prevent caching
|
|
|
|
|
- xhr[i].setRequestHeader('Content-Encoding','identity'); //disable compression (some browsers may refuse it, but data is incompressible anyway)
|
|
|
|
|
- xhr[i].send(reqsmall);
|
|
|
|
|
- }else{
|
|
|
|
|
- //REGULAR version, no workaround
|
|
|
|
|
- xhr[i].upload.onprogress=function(event){
|
|
|
|
|
- if(testStatus!=3){try{x.abort();}catch(e){}} //just in case this XHR is still running after the upload test
|
|
|
|
|
- //progress event, add number of new loaded bytes to totLoaded
|
|
|
|
|
- var loadDiff=event.loaded<=0?0:(event.loaded-prevLoaded);
|
|
|
|
|
- if(isNaN(loadDiff)||!isFinite(loadDiff)||loadDiff<0) return; //just in case
|
|
|
|
|
- totLoaded+=loadDiff;
|
|
|
|
|
- prevLoaded=event.loaded;
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- xhr[i].upload.onload=function(){
|
|
|
|
|
- //this stream sent all the garbage data, start again
|
|
|
|
|
- testStream(i,0);
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- xhr[i].upload.onerror=function(){
|
|
|
|
|
- //error, abort
|
|
|
|
|
- failed=true;
|
|
|
|
|
- try{xhr[i].abort();}catch(e){}
|
|
|
|
|
- delete(xhr[i]);
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- //send xhr
|
|
|
|
|
- xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),true); //random string to prevent caching
|
|
|
|
|
- xhr[i].setRequestHeader('Content-Encoding','identity'); //disable compression (some browsers may refuse it, but data is incompressible anyway)
|
|
|
|
|
- xhr[i].send(req);
|
|
|
|
|
- }
|
|
|
|
|
- }.bind(this),1);
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- //open streams
|
|
|
|
|
- for(var i=0;i<settings.xhr_ulMultistream;i++){
|
|
|
|
|
- testStream(i,100*i);
|
|
|
|
|
- }
|
|
|
|
|
- //every 200ms, update ulStatus
|
|
|
|
|
- interval=setInterval(function(){
|
|
|
|
|
- var t=new Date().getTime()-startT;
|
|
|
|
|
- if(t<200) return;
|
|
|
|
|
- var speed=totLoaded/(t/1000.0);
|
|
|
|
|
- ulStatus=((speed*8)/925000.0).toFixed(2); //925000 instead of 1048576 to account for overhead
|
|
|
|
|
- if((t/1000.0)>settings.time_ul||failed){ //test is over, stop streams and timer
|
|
|
|
|
- if(failed||isNaN(ulStatus)) ulStatus="Fail";
|
|
|
|
|
- clearRequests();
|
|
|
|
|
- clearInterval(interval);
|
|
|
|
|
- done();
|
|
|
|
|
- }
|
|
|
|
|
- }.bind(this),200);
|
|
|
|
|
|
|
+// upload test, calls done function whent it's over
|
|
|
|
|
+// garbage data for upload test
|
|
|
|
|
+var r = new ArrayBuffer(1048576)
|
|
|
|
|
+try { r = new Float32Array(r); for (var i = 0; i < r.length; i++)r[i] = Math.random() } catch (e) { }
|
|
|
|
|
+var req = []
|
|
|
|
|
+var reqsmall = []
|
|
|
|
|
+for (var i = 0; i < 20; i++) req.push(r)
|
|
|
|
|
+req = new Blob(req)
|
|
|
|
|
+r = new ArrayBuffer(262144)
|
|
|
|
|
+try { r = new Float32Array(r); for (var i = 0; i < r.length; i++)r[i] = Math.random() } catch (e) { }
|
|
|
|
|
+reqsmall.push(r)
|
|
|
|
|
+reqsmall = new Blob(reqsmall)
|
|
|
|
|
+var ulCalled = false // used to prevent multiple accidental calls to ulTest
|
|
|
|
|
+function ulTest (done) {
|
|
|
|
|
+ if (ulCalled) return; else ulCalled = true // ulTest already called?
|
|
|
|
|
+ var totLoaded = 0.0 // total number of transmitted bytes
|
|
|
|
|
+ var startT = new Date().getTime() // timestamp when test was started
|
|
|
|
|
+ var failed = false // set to true if a stream fails
|
|
|
|
|
+ xhr = []
|
|
|
|
|
+ // function to create an upload stream. streams are slightly delayed so that they will not end at the same time
|
|
|
|
|
+ var testStream = function (i, delay) {
|
|
|
|
|
+ setTimeout(function () {
|
|
|
|
|
+ if (testStatus !== 3) return // delayed stream ended up starting after the end of the upload test
|
|
|
|
|
+ var prevLoaded = 0 // number of bytes transmitted last time onprogress was called
|
|
|
|
|
+ var x = new XMLHttpRequest()
|
|
|
|
|
+ xhr[i] = x
|
|
|
|
|
+ var ie11workaround
|
|
|
|
|
+ try {
|
|
|
|
|
+ xhr[i].upload.onprogress
|
|
|
|
|
+ ie11workaround = false
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ ie11workaround = true
|
|
|
|
|
+ }
|
|
|
|
|
+ if (ie11workaround) {
|
|
|
|
|
+ // IE11 workarond: xhr.upload does not work properly, therefore we send a bunch of small 256k requests and use the onload event as progress. This is not precise, especially on fast connections
|
|
|
|
|
+ xhr[i].onload = function () {
|
|
|
|
|
+ totLoaded += 262144
|
|
|
|
|
+ testStream(i, 0)
|
|
|
|
|
+ }
|
|
|
|
|
+ xhr[i].onerror = function () {
|
|
|
|
|
+ // error, abort
|
|
|
|
|
+ failed = true
|
|
|
|
|
+ try { xhr[i].abort() } catch (e) { }
|
|
|
|
|
+ delete (xhr[i])
|
|
|
|
|
+ }
|
|
|
|
|
+ xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching
|
|
|
|
|
+ xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway)
|
|
|
|
|
+ xhr[i].send(reqsmall)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // REGULAR version, no workaround
|
|
|
|
|
+ xhr[i].upload.onprogress = function (event) {
|
|
|
|
|
+ if (testStatus !== 3) { try { x.abort() } catch (e) { } } // just in case this XHR is still running after the upload test
|
|
|
|
|
+ // progress event, add number of new loaded bytes to totLoaded
|
|
|
|
|
+ var loadDiff = event.loaded <= 0 ? 0 : (event.loaded - prevLoaded)
|
|
|
|
|
+ if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return // just in case
|
|
|
|
|
+ totLoaded += loadDiff
|
|
|
|
|
+ prevLoaded = event.loaded
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ xhr[i].upload.onload = function () {
|
|
|
|
|
+ // this stream sent all the garbage data, start again
|
|
|
|
|
+ testStream(i, 0)
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ xhr[i].upload.onerror = function () {
|
|
|
|
|
+ // error, abort
|
|
|
|
|
+ failed = true
|
|
|
|
|
+ try { xhr[i].abort() } catch (e) { }
|
|
|
|
|
+ delete (xhr[i])
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ // send xhr
|
|
|
|
|
+ xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching
|
|
|
|
|
+ xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway)
|
|
|
|
|
+ xhr[i].send(req)
|
|
|
|
|
+ }
|
|
|
|
|
+ }.bind(this), 1)
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ // open streams
|
|
|
|
|
+ for (var i = 0; i < settings.xhr_ulMultistream; i++) {
|
|
|
|
|
+ testStream(i, 100 * i)
|
|
|
|
|
+ }
|
|
|
|
|
+ // every 200ms, update ulStatus
|
|
|
|
|
+ interval = setInterval(function () {
|
|
|
|
|
+ var t = new Date().getTime() - startT
|
|
|
|
|
+ if (t < 200) return
|
|
|
|
|
+ var speed = totLoaded / (t / 1000.0)
|
|
|
|
|
+ ulStatus = ((speed * 8) / 925000.0).toFixed(2) // 925000 instead of 1048576 to account for overhead
|
|
|
|
|
+ if ((t / 1000.0) > settings.time_ul || failed) { // test is over, stop streams and timer
|
|
|
|
|
+ if (failed || isNaN(ulStatus)) ulStatus = 'Fail'
|
|
|
|
|
+ clearRequests()
|
|
|
|
|
+ clearInterval(interval)
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ }.bind(this), 200)
|
|
|
}
|
|
}
|
|
|
-//ping+jitter test, function done is called when it's over
|
|
|
|
|
-var ptCalled=false; //used to prevent multiple accidental calls to pingTest
|
|
|
|
|
-function pingTest(done){
|
|
|
|
|
- if(ptCalled) return; else ptCalled=true; //pingTest already called?
|
|
|
|
|
- var prevT=null, //last time a pong was received
|
|
|
|
|
- ping=0.0, //current ping value
|
|
|
|
|
- jitter=0.0, //current jitter value
|
|
|
|
|
- i=0, //counter of pongs received
|
|
|
|
|
- prevInstspd=0; //last ping time, used for jitter calculation
|
|
|
|
|
- xhr=[];
|
|
|
|
|
- //ping function
|
|
|
|
|
- var doPing=function(){
|
|
|
|
|
- prevT=new Date().getTime();
|
|
|
|
|
- xhr[0]=new XMLHttpRequest();
|
|
|
|
|
- xhr[0].onload=function(){
|
|
|
|
|
- //pong
|
|
|
|
|
- if(i==0){
|
|
|
|
|
- prevT=new Date().getTime(); //first pong
|
|
|
|
|
- }else{
|
|
|
|
|
- var instspd=(new Date().getTime()-prevT)/2;
|
|
|
|
|
- var instjitter=Math.abs(instspd-prevInstspd);
|
|
|
|
|
- if(i==1)ping=instspd; /*first ping, can't tell jiutter yet*/ else{
|
|
|
|
|
- ping=ping*0.9+instspd*0.1; //ping, weighted average
|
|
|
|
|
- jitter=instjitter>jitter?(jitter*0.2+instjitter*0.8):(jitter*0.9+instjitter*0.1); //update jitter, weighted average. spikes in ping values are given more weight.
|
|
|
|
|
- }
|
|
|
|
|
- prevInstspd=instspd;
|
|
|
|
|
- }
|
|
|
|
|
- pingStatus=ping.toFixed(2);
|
|
|
|
|
- jitterStatus=jitter.toFixed(2);
|
|
|
|
|
- i++;
|
|
|
|
|
- if(i<settings.count_ping) doPing(); else done(); //more pings to do?
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- xhr[0].onerror=function(){
|
|
|
|
|
- //a ping failed, cancel test
|
|
|
|
|
- pingStatus="Fail";
|
|
|
|
|
- jitterStatus="Fail";
|
|
|
|
|
- clearRequests();
|
|
|
|
|
- done();
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- //sent xhr
|
|
|
|
|
- xhr[0].open("GET",settings.url_ping+"?r="+Math.random(),true); //random string to prevent caching
|
|
|
|
|
- xhr[0].send();
|
|
|
|
|
- }.bind(this);
|
|
|
|
|
- doPing(); //start first ping
|
|
|
|
|
|
|
+// ping+jitter test, function done is called when it's over
|
|
|
|
|
+var ptCalled = false // used to prevent multiple accidental calls to pingTest
|
|
|
|
|
+function pingTest (done) {
|
|
|
|
|
+ if (ptCalled) return; else ptCalled = true // pingTest already called?
|
|
|
|
|
+ var prevT = null // last time a pong was received
|
|
|
|
|
+ var ping = 0.0 // current ping value
|
|
|
|
|
+ var jitter = 0.0 // current jitter value
|
|
|
|
|
+ var i = 0 // counter of pongs received
|
|
|
|
|
+ var prevInstspd = 0 // last ping time, used for jitter calculation
|
|
|
|
|
+ xhr = []
|
|
|
|
|
+ // ping function
|
|
|
|
|
+ var doPing = function () {
|
|
|
|
|
+ prevT = new Date().getTime()
|
|
|
|
|
+ xhr[0] = new XMLHttpRequest()
|
|
|
|
|
+ xhr[0].onload = function () {
|
|
|
|
|
+ // pong
|
|
|
|
|
+ if (i === 0) {
|
|
|
|
|
+ prevT = new Date().getTime() // first pong
|
|
|
|
|
+ } else {
|
|
|
|
|
+ var instspd = (new Date().getTime() - prevT) / 2
|
|
|
|
|
+ var instjitter = Math.abs(instspd - prevInstspd)
|
|
|
|
|
+ if (i === 1) ping = instspd; /* first ping, can't tell jiutter yet*/ else {
|
|
|
|
|
+ ping = ping * 0.9 + instspd * 0.1 // ping, weighted average
|
|
|
|
|
+ jitter = instjitter > jitter ? (jitter * 0.2 + instjitter * 0.8) : (jitter * 0.9 + instjitter * 0.1) // update jitter, weighted average. spikes in ping values are given more weight.
|
|
|
|
|
+ }
|
|
|
|
|
+ prevInstspd = instspd
|
|
|
|
|
+ }
|
|
|
|
|
+ pingStatus = ping.toFixed(2)
|
|
|
|
|
+ jitterStatus = jitter.toFixed(2)
|
|
|
|
|
+ i++
|
|
|
|
|
+ if (i < settings.count_ping) doPing(); else done() // more pings to do?
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ xhr[0].onerror = function () {
|
|
|
|
|
+ // a ping failed, cancel test
|
|
|
|
|
+ pingStatus = 'Fail'
|
|
|
|
|
+ jitterStatus = 'Fail'
|
|
|
|
|
+ clearRequests()
|
|
|
|
|
+ done()
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ // sent xhr
|
|
|
|
|
+ xhr[0].open('GET', settings.url_ping + '?r=' + Math.random(), true) // random string to prevent caching
|
|
|
|
|
+ xhr[0].send()
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ doPing() // start first ping
|
|
|
}
|
|
}
|