improvements and new example (#29)
Merge with #29. Using standard js code style (ugh...); rewritten documentation in markdown; Added example6
This commit is contained in:
parent
0dd0eeaa86
commit
3f06ab1381
12 changed files with 1365 additions and 976 deletions
18
.editorconfig
Normal file
18
.editorconfig
Normal file
|
@ -0,0 +1,18 @@
|
|||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
|
||||
[*.js]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
239
doc.md
Normal file
239
doc.md
Normal file
|
@ -0,0 +1,239 @@
|
|||
# HTML5 Speedtest
|
||||
|
||||
> by Federico Dossena
|
||||
> Version 4.2, April 26 2017
|
||||
> [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/)
|
||||
|
||||
|
||||
## Introduction
|
||||
In this document, we will introduce an XHR based HTML5 Speedtest and see how to use it.
|
||||
This test measures download speed, upload speed, ping and jitter.
|
||||
|
||||
First of all, the requirements to run this test:
|
||||
|
||||
* The browser have to support XHR Level 2 and Web Workers and Javascript must be enabled.
|
||||
* Internet Explorer 11
|
||||
* Microsoft Edge 12+
|
||||
* Mozilla Firefox 12+
|
||||
* Google Chrome / Chromium 31+
|
||||
* Apple Safari 7.1+
|
||||
* Opera 18+
|
||||
* Client side, the test can use up to 500 megabytes of RAM
|
||||
* Server side, you'll need a fast connection (at least 100 Mbps recommended), and the web server must accept large POST requests (up to 20 megabytes).
|
||||
Apache2 and PHP are recommended, but not mandatory.
|
||||
|
||||
If this looks good, let's proceed and see how to use the test.
|
||||
|
||||
|
||||
## Installation
|
||||
To install the test on your server, upload the following files:
|
||||
|
||||
* `speedtest_worker.min.js`
|
||||
* `garbage.php`
|
||||
* `getIP.php`
|
||||
* `empty.dat`
|
||||
|
||||
You may also want to upload one of the examples to test it.
|
||||
Later we'll see how to use the test without PHP.
|
||||
|
||||
__Important:__ keep all the files together; all paths are relative to the js file
|
||||
|
||||
|
||||
## Usage
|
||||
To run the test, you need to do 3 things:
|
||||
|
||||
* Create the worker
|
||||
* Write some code that handles the responses coming from the worker
|
||||
* Start the test
|
||||
|
||||
### Creating the worker
|
||||
```js
|
||||
var w = new Worker("speedtest_worker.min.js")
|
||||
```
|
||||
|
||||
__Important:__ use the minified version, it's smaller!
|
||||
|
||||
### Response handler
|
||||
First, we set up a timer that fetches the status of the worker continuously:
|
||||
```js
|
||||
var timer = setInterval(function () {
|
||||
w.postMessage('status')
|
||||
}, 100)
|
||||
```
|
||||
|
||||
Then we write a response handler that receives the status and updates the page. Later
|
||||
we'll see the details of the format of the response.
|
||||
|
||||
```js
|
||||
w.onmessage = function (event) {
|
||||
var data = event.data.split(';')
|
||||
var testState = data[0]
|
||||
var dlStatus = data[1]
|
||||
var ulStatus = data[2]
|
||||
var pingStatus = data[3]
|
||||
var jitterStatus = data[5]
|
||||
var clientIp = data[4]
|
||||
if (testState >= 4) {
|
||||
clearInterval(timer) // test is finished or aborted
|
||||
}
|
||||
// .. update your page here ..
|
||||
}
|
||||
```
|
||||
|
||||
#### Response format
|
||||
The response from the worker is composed of values separated by `;` (semicolon) in this
|
||||
format:
|
||||
|
||||
`testState;dlStatus;ulStatus;pingStatus;clientIp;jitterStatus`
|
||||
|
||||
* __testState__ is an integer 0-5
|
||||
* `0` = Test starting
|
||||
* `1` = Download test in progress
|
||||
* `2` = Ping + Jitter test in progress
|
||||
* `3` = Upload test in progress
|
||||
* `4` = Test finished
|
||||
* `5` = Test aborted
|
||||
* __dlStatus__ is either
|
||||
* Empty string (not started or aborted)
|
||||
* Download speed in Megabit/s as a number with 2 digits
|
||||
* The string "Fail" (test failed)
|
||||
* __ulStatus__ is either
|
||||
* Empty string (not started or aborted)
|
||||
* Upload speed in Megabit/s as a number with 2 digits
|
||||
* The string "Fail" (test failed)
|
||||
* __pingStatus__ is either
|
||||
* Empty string (not started or aborted)
|
||||
* Estimated ping in milliseconds as a number with 2 digits
|
||||
* The string "Fail" (test failed)
|
||||
* __clientIp__ is either
|
||||
* Empty string (not fetched yet or failed)
|
||||
* The client's IP address as a string
|
||||
* __jitterStatus__ is either
|
||||
* Empty string (not started or aborted)
|
||||
* Estimated jitter in milliseconds as a number with 2 digits (lower = stable connection)
|
||||
* The string "Fail" (test failed)
|
||||
|
||||
### Starting the test
|
||||
To start the test, send the start command to the worker:
|
||||
|
||||
```js
|
||||
w.postMessage('start')
|
||||
```
|
||||
|
||||
This starts the test with the default settings, which is usually the best choice. If you want, you can change these settings and pass them to the worker as JSON with like this:
|
||||
|
||||
```js
|
||||
w.postMessage('start {"param1": "value1", "param2": "value2", ...}')
|
||||
```
|
||||
|
||||
#### Test parameters
|
||||
* __time_dl__: How long the download test should be in seconds
|
||||
* Default: `15`
|
||||
* Recommended: `>=5`
|
||||
* __time_ul__: How long the upload test should be in seconds
|
||||
* Default: `15`
|
||||
* Recommended: `>=10`
|
||||
* __count_ping__: How many pings to perform in the ping test
|
||||
* Default: `35`
|
||||
* Recommended: `>=20`
|
||||
* __url_dl__: path to garbage.php or a large file to use for the download test
|
||||
* Default: `garbage.php`
|
||||
* __Important:__ path is relative to js file
|
||||
* __url_ul__: path to ab empty file or empty.dat to use for the upload test
|
||||
* Default: `empty.dat`
|
||||
* __Important:__ path is relative to js file
|
||||
* __url_ping__: path to an empty file or empty.dat to use for the ping test
|
||||
* Default: `empty.dat`
|
||||
* __Important:__ path is relative to js file
|
||||
* __url_getIp__: path to getIP.php or replacement
|
||||
* Default: `getIP.php`
|
||||
* __Important:__ path is relative to js file
|
||||
* __enable_quirks__: enables browser-specific optimizations. These optimizations override some of the default settings below. They do not override settings that are explicitly set.
|
||||
* Default: `true`
|
||||
* __garbagePhp_chunkSize__: size of chunks sent by garbage.php in megabytes
|
||||
* Default: `20`
|
||||
* Recommended: `>=10`
|
||||
* Default override: 5 on Safari if enable_quirks is true
|
||||
* __xhr_dlMultistream__: how many streams should be opened for the download test
|
||||
* Default: `10`
|
||||
* Recommended: `>=3`
|
||||
* Default override: 3 on Edge if enable_quirks is true
|
||||
* Default override: 5 on Chromium-based if enable_quirks is true
|
||||
* __xhr_ulMultistream__: how many streams should be opened for the upload test
|
||||
* Default: `3`
|
||||
* Recommended: `>=1`
|
||||
* Default override: 1 on Firefox if enable_quirks is true
|
||||
* Default override: 10 on Safari if enable_quirks is true
|
||||
* __allow_fetchAPI__: allow the use of Fetch API for the download test instead of regular XHR. Experimental, not recommended.
|
||||
* Default: `false`
|
||||
* __force_fetchAPI__: forces the use of Fetch API on all browsers that support it
|
||||
* Default: `false`
|
||||
Fetch API are used if the following conditions are met:
|
||||
* allow_fetchAPI is true
|
||||
* Chromium-based browser with support for Fetch API and enable_quirks is true
|
||||
OR force_fetchAPI is true and the browser supports Fetch API
|
||||
|
||||
### Aborting the test prematurely
|
||||
The test can be aborted at any time by sending an abort command to the worker:
|
||||
|
||||
```js
|
||||
w.postMessage('abort')
|
||||
```
|
||||
|
||||
This will terminate all network activity and stop the worker.
|
||||
|
||||
__Important:__ do not simply kill the worker while it's running, as it will leave pending XHR requests!
|
||||
|
||||
|
||||
## Using the test without PHP
|
||||
If your server does not support PHP, or you're using something newer like Node.js, you can still use this test by replacing `garbage.php` and `getIP.php`.
|
||||
|
||||
### Replacements
|
||||
|
||||
#### Replacement for `garbage.php`
|
||||
A replacement for `garbage.php` must generate incompressible garbage data.
|
||||
|
||||
A large file (10-100 Mbytes) is a possible replacement. You can get [one here](http://downloads.fdossena.com/geth.php?r=speedtest-bigfile).
|
||||
|
||||
If you're using Node.js or some other server, your replacement should accept the `ckSize` parameter (via GET) which tells it how many megabytes of garbage to generate.
|
||||
It is important here to turn off compression, and generate incompressible data.
|
||||
A symlink to `/dev/urandom` is also ok.
|
||||
|
||||
#### Replacement for `getIP.php`
|
||||
Your replacement must simply respond with the client's IP as plaintext. Nothing fancy.
|
||||
|
||||
### JS
|
||||
You need to start the test with your replacements like this:
|
||||
|
||||
```js
|
||||
w.postMessage('start {"url_dl": "newGarbageURL", "url_getIp": "newIpURL"}')
|
||||
```
|
||||
|
||||
|
||||
## Known bugs and limitations
|
||||
* __Chrome:__ high CPU usage from XHR requests with very fast connections (like gigabit).
|
||||
For this reason, the test may report inaccurate results if your CPU is too slow. (Does not affect most computers)
|
||||
* __IE11:__ the upload test is not precise on very fast connections
|
||||
* __Safari:__ works, but needs more testing and tweaking for very fast connections
|
||||
|
||||
## Making changes
|
||||
Since this is an open source project, you can modify it.
|
||||
|
||||
To make changes to the speedtest itself, edit `speedtest_worker.js`
|
||||
|
||||
To create the minified version, use UglifyJS like this:
|
||||
|
||||
```
|
||||
uglifyjs -c --screw-ie8 speedtest_worker.js > speedtest_worker.min.js
|
||||
```
|
||||
|
||||
Pull requests are much appreciated. If you don't use github (or git), simply contact me.
|
||||
|
||||
__Important:__ please add your name to modified versions to distinguish them from the main project.
|
||||
|
||||
|
||||
## License
|
||||
This software is under the GNU LGPL license, Version 3 or newer.
|
||||
|
||||
To put it short: you are free to use, study, modify, and redistribute this software and modified versions of it, for free or for money.
|
||||
You can also use it in proprietary software but all changes to this software must remain under the same GNU LGPL license.
|
BIN
doc.pdf
BIN
doc.pdf
Binary file not shown.
|
@ -3,27 +3,33 @@
|
|||
<head>
|
||||
<title>Speedtest</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Speedtest</h1>
|
||||
|
||||
<h4>IP Address</h4>
|
||||
<div id="ip"></div>
|
||||
<p id="ip"></p>
|
||||
|
||||
<h4>Download</h4>
|
||||
<div id="download"></div>
|
||||
<p id="download"></p>
|
||||
|
||||
<h4>Upload</h4>
|
||||
<div id="upload"></div>
|
||||
<p id="upload"></p>
|
||||
|
||||
<h4>Latency</h4>
|
||||
<div id="ping"></div>
|
||||
<p id="ping"></p>
|
||||
|
||||
<script type="text/javascript">
|
||||
var w=new Worker("speedtest_worker.min.js"); //create new worker
|
||||
setInterval(function(){w.postMessage("status");}.bind(this),100); //ask for status every 100ms
|
||||
var w = new Worker('speedtest_worker.min.js') // create new worker
|
||||
setInterval(function () { w.postMessage('status') }, 100) // ask for status every 100ms
|
||||
w.onmessage = function (event) { // when status is received, split the string and put the values in the appropriate fields
|
||||
var data=event.data.split(";"); //string format: status;download;upload;ping (speeds are in mbit/s) (status: 0=not started, 1=downloading, 2=uploading, 3=ping, 4=done, 5=aborted)
|
||||
document.getElementById("download").innerHTML=data[1]+" Mbit/s";
|
||||
document.getElementById("upload").innerHTML=data[2]+" Mbit/s";
|
||||
document.getElementById("ping").innerHTML=data[3]+" ms, "+data[5]+" ms jitter";
|
||||
document.getElementById("ip").innerHTML=data[4];
|
||||
var data = event.data.split(';') // string format: status;download;upload;ping (speeds are in mbit/s) (status: 0=not started, 1=downloading, 2=uploading, 3=ping, 4=done, 5=aborted)
|
||||
document.getElementById('download').textContent = data[1] + ' Mbit/s'
|
||||
document.getElementById('upload').textContent = data[2] + ' Mbit/s'
|
||||
document.getElementById('ping').textContent = data[3] + ' ms, ' + data[5] + ' ms jitter'
|
||||
document.getElementById('ip').textContent = data[4]
|
||||
}
|
||||
w.postMessage("start"); //start the speedtest (default params. keep garbage.php and empty.dat in the same directory as the js file)
|
||||
w.postMessage('start') // start the speedtest (default params. keep garbage.php and empty.dat in the same directory as the js file)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -3,35 +3,42 @@
|
|||
<head>
|
||||
<title>Speedtest</title>
|
||||
<style type="text/css">
|
||||
html,body{
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.test {
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
div.testName,div.meterUnit{
|
||||
|
||||
div.testName,
|
||||
div.meterUnit {
|
||||
font-size: 3vw;
|
||||
}
|
||||
|
||||
div.meter {
|
||||
font-size: 6vw;
|
||||
line-height: 1.5em;
|
||||
height: 1.5em !important;
|
||||
}
|
||||
|
||||
.flash {
|
||||
animation: flash 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0% { opacity: 0.6; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Speedtest</h1>
|
||||
<div class="test">
|
||||
|
@ -56,23 +63,29 @@
|
|||
</div>
|
||||
<div id="ip"></div>
|
||||
<script type="text/javascript">
|
||||
var w=new Worker("speedtest_worker.min.js");
|
||||
var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
|
||||
var w = new Worker('speedtest_worker.min.js')
|
||||
var interval = setInterval(function () { w.postMessage('status') }, 100)
|
||||
w.onmessage = function (event) {
|
||||
var data=event.data.split(";");
|
||||
var status=Number(data[0]);
|
||||
var dl=document.getElementById("download"),ul=document.getElementById("upload"),ping=document.getElementById("ping"),ip=document.getElementById("ip"),jitter=document.getElementById("jitter");
|
||||
dl.className=status==1?"flash":"";ping.className=status==2?"flash":"";jitter.className=ul.className=status==3?"flash":"";
|
||||
var data = event.data.split(';')
|
||||
var status = Number(data[0])
|
||||
var dl = document.getElementById('download')
|
||||
var ul = document.getElementById('upload')
|
||||
var ping = document.getElementById('ping')
|
||||
var ip = document.getElementById('ip')
|
||||
var jitter = document.getElementById('jitter')
|
||||
dl.className = status === 1 ? 'flash' : ''
|
||||
ping.className = status === 2 ? 'flash' : ''
|
||||
jitter.className = ul.className = status === 3 ? 'flash' : ''
|
||||
if (status >= 4) {
|
||||
clearInterval(interval);
|
||||
clearInterval(interval)
|
||||
}
|
||||
dl.innerHTML=data[1];
|
||||
ul.innerHTML=data[2];
|
||||
ping.innerHTML=data[3];
|
||||
jitter.innerHTML=data[5];
|
||||
ip.innerHTML="Your IP: "+data[4];
|
||||
}.bind(this);
|
||||
w.postMessage('start {"url_dl":"garbage.php","url_ul":"empty.dat","url_ping":"empty.dat","time_dl":"10","time_ul":"15","count_ping":"30"}'); //start with custom parameters. paths are relative to js file. you can omit parameters that you don't want to change
|
||||
dl.textContent = data[1]
|
||||
ul.textContent = data[2]
|
||||
ping.textContent = data[3]
|
||||
jitter.textContent = data[5]
|
||||
ip.textContent = 'Your IP: ' + data[4]
|
||||
}
|
||||
w.postMessage('start {"url_dl": "garbage.php", "url_ul": "empty.dat", "url_ping": "empty.dat", "time_dl": "10", "time_ul": "15", "count_ping": "30"}') // start with custom parameters. paths are relative to js file. you can omit parameters that you don't want to change
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -3,12 +3,14 @@
|
|||
<head>
|
||||
<title>Speedtest</title>
|
||||
<style type="text/css">
|
||||
html,body{
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.test {
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
|
@ -16,21 +18,27 @@
|
|||
min-width: 20vw;
|
||||
text-align: center;
|
||||
}
|
||||
div.testName,div.meterUnit{
|
||||
|
||||
div.testName,
|
||||
div.meterUnit {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.meter {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.5em;
|
||||
height: 2em !important;
|
||||
}
|
||||
|
||||
.flash {
|
||||
animation: flash 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0% { opacity: 0.6; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
border: 0.15em solid #000000;
|
||||
|
@ -39,10 +47,12 @@
|
|||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#ip {
|
||||
margin: 0.8em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
@media all and (max-width: 50em) {
|
||||
div.test {
|
||||
font-size: 2em;
|
||||
|
@ -50,40 +60,47 @@
|
|||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var w=null;
|
||||
var w = null
|
||||
function runTest() {
|
||||
document.getElementById("startBtn").style.display="none";
|
||||
document.getElementById("testArea").style.display="";
|
||||
document.getElementById("abortBtn").style.display="";
|
||||
w=new Worker("speedtest_worker.min.js");
|
||||
var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
|
||||
document.getElementById('startBtn').style.display = 'none'
|
||||
document.getElementById('testArea').style.display = ''
|
||||
document.getElementById('abortBtn').style.display = ''
|
||||
w = new Worker('speedtest_worker.min.js')
|
||||
var interval = setInterval(function () { w.postMessage('status') }, 100)
|
||||
w.onmessage = function (event) {
|
||||
var data=event.data.split(";");
|
||||
var status=Number(data[0]);
|
||||
var dl=document.getElementById("download"),ul=document.getElementById("upload"),ping=document.getElementById("ping"),ip=document.getElementById("ip"),jitter=document.getElementById("jitter");
|
||||
dl.className=status==1?"flash":"";ping.className=status==2?"flash":"";jitter.className=ul.className=status==3?"flash":"";
|
||||
var data = event.data.split(';')
|
||||
var status = Number(data[0])
|
||||
var dl = document.getElementById('download')
|
||||
var ul = document.getElementById('upload')
|
||||
var ping = document.getElementById('ping')
|
||||
var ip = document.getElementById('ip')
|
||||
var jitter = document.getElementById('jitter')
|
||||
dl.className = status === 1 ? 'flash' : ''
|
||||
ping.className = status === 2 ? 'flash' : ''
|
||||
jitter.className = ul.className = status === 3 ? 'flash' : ''
|
||||
if (status >= 4) {
|
||||
clearInterval(interval);
|
||||
document.getElementById("abortBtn").style.display="none";
|
||||
document.getElementById("startBtn").style.display="";
|
||||
w=null;
|
||||
clearInterval(interval)
|
||||
document.getElementById('abortBtn').style.display = 'none'
|
||||
document.getElementById('startBtn').style.display = ''
|
||||
w = null
|
||||
}
|
||||
if(status==5){
|
||||
document.getElementById("testArea").style.display="none";
|
||||
if (status === 5) {
|
||||
document.getElementById('testArea').style.display = 'none'
|
||||
}
|
||||
dl.innerHTML=data[1];
|
||||
ul.innerHTML=data[2];
|
||||
ping.innerHTML=data[3];
|
||||
jitter.innerHTML=data[5];
|
||||
ip.innerHTML=data[4];
|
||||
}.bind(this);
|
||||
w.postMessage("start");
|
||||
dl.textContent = data[1]
|
||||
ul.textContent = data[2]
|
||||
ping.textContent = data[3]
|
||||
jitter.textContent = data[5]
|
||||
ip.textContent = data[4]
|
||||
}
|
||||
w.postMessage('start')
|
||||
}
|
||||
function abortTest() {
|
||||
if(w)w.postMessage("abort");
|
||||
if (w) w.postMessage('abort')
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Speedtest</h1>
|
||||
<div id="testArea" style="display:none">
|
||||
|
|
127
example4.html
127
example4.html
|
@ -3,8 +3,8 @@
|
|||
<head>
|
||||
<title>Speedtest</title>
|
||||
<style type="text/css">
|
||||
|
||||
html,body{
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
@ -12,8 +12,12 @@
|
|||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6
|
||||
{
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
@ -26,14 +30,12 @@
|
|||
font-size: 6vw;
|
||||
}
|
||||
|
||||
div#testArea
|
||||
{
|
||||
div#testArea {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -57,46 +59,45 @@
|
|||
margin: 1em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/justgage/1.2.2/justgage.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var w=null;
|
||||
var ggdl,ggul,ggping;
|
||||
var w = null
|
||||
var ggdl, ggul, ggping
|
||||
function runTest() {
|
||||
w=new Worker("speedtest_worker.min.js");
|
||||
var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
|
||||
document.getElementById("abortBtn").style.display="";
|
||||
document.getElementById("startBtn").style.display="none";
|
||||
w = new Worker('speedtest_worker.min.js')
|
||||
var interval = setInterval(function () { w.postMessage('status') }, 100)
|
||||
document.getElementById('abortBtn').style.display = ''
|
||||
document.getElementById('startBtn').style.display = 'none'
|
||||
w.onmessage = function (event) {
|
||||
var data=event.data.split(";");
|
||||
var status=Number(data[0]);
|
||||
var data = event.data.split(';')
|
||||
var status = Number(data[0])
|
||||
if (status >= 4) {
|
||||
clearInterval(interval);
|
||||
document.getElementById("abortBtn").style.display="none";
|
||||
document.getElementById("startBtn").style.display="";
|
||||
w=null;
|
||||
clearInterval(interval)
|
||||
document.getElementById('abortBtn').style.display = 'none'
|
||||
document.getElementById('startBtn').style.display = ''
|
||||
w = null
|
||||
}
|
||||
updateGauge(ggdl, data[1]);
|
||||
updateGauge(ggul, data[2]);
|
||||
updateGauge(ggping, data[3]);
|
||||
document.getElementById("ip").innerHTML="Your IP: "+data[4];
|
||||
updateGauge(ggjitter, data[5]);
|
||||
}.bind(this);
|
||||
w.postMessage('start {"time_ul":"10", "time_dl":"10", "count_ping":"50", "url_dl":"garbage.php","url_ul":"empty.dat","url_ping":"empty.dat","url_getIp":"getIP.php"}');
|
||||
updateGauge(ggdl, data[1])
|
||||
updateGauge(ggul, data[2])
|
||||
updateGauge(ggping, data[3])
|
||||
document.getElementById('ip').textContent = 'Your IP: ' + data[4]
|
||||
updateGauge(ggjitter, data[5])
|
||||
}
|
||||
w.postMessage('start {"time_ul": "10", "time_dl": "10", "count_ping": "50", "url_dl": "garbage.php", "url_ul": "empty.dat", "url_ping": "empty.dat", "url_getIp": "getIP.php"}')
|
||||
}
|
||||
function abortTest() {
|
||||
if(w)w.postMessage("abort");
|
||||
if (w) w.postMessage('abort')
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
document.addEventListener('DOMContentLoaded', function (event) {
|
||||
ggdl = new JustGage({
|
||||
id: 'ggdl',
|
||||
title: "Download",
|
||||
label: "Mbit/s",
|
||||
titleFontFamily : "Open Sans",
|
||||
valueFontFamily : "Open Sans",
|
||||
title: 'Download',
|
||||
label: 'Mbit/s',
|
||||
titleFontFamily: 'Open Sans',
|
||||
valueFontFamily: 'Open Sans',
|
||||
refreshAnimationTime: 300,
|
||||
value: 0,
|
||||
min: 0,
|
||||
|
@ -105,17 +106,17 @@
|
|||
formatNumber: true,
|
||||
humanFriendly: false,
|
||||
levelColors: [
|
||||
"#999999",
|
||||
"#339933"
|
||||
'#999999',
|
||||
'#339933'
|
||||
]
|
||||
});
|
||||
})
|
||||
|
||||
ggul = new JustGage({
|
||||
id: 'ggul',
|
||||
title: "Upload",
|
||||
label: "Mbit/s",
|
||||
titleFontFamily : "Open Sans",
|
||||
valueFontFamily : "Open Sans",
|
||||
title: 'Upload',
|
||||
label: 'Mbit/s',
|
||||
titleFontFamily: 'Open Sans',
|
||||
valueFontFamily: 'Open Sans',
|
||||
refreshAnimationTime: 300,
|
||||
value: 0,
|
||||
min: 0,
|
||||
|
@ -124,18 +125,18 @@
|
|||
formatNumber: true,
|
||||
humanFriendly: false,
|
||||
levelColors: [
|
||||
"#999999",
|
||||
"#333399"
|
||||
'#999999',
|
||||
'#333399'
|
||||
]
|
||||
|
||||
});
|
||||
})
|
||||
|
||||
ggping = new JustGage({
|
||||
id: 'ggping',
|
||||
title: "Ping",
|
||||
label: "ms",
|
||||
titleFontFamily : "Open Sans",
|
||||
valueFontFamily : "Open Sans",
|
||||
title: 'Ping',
|
||||
label: 'ms',
|
||||
titleFontFamily: 'Open Sans',
|
||||
valueFontFamily: 'Open Sans',
|
||||
refreshAnimationTime: 300,
|
||||
value: 0,
|
||||
min: 0,
|
||||
|
@ -144,16 +145,16 @@
|
|||
formatNumber: true,
|
||||
humanFriendly: false,
|
||||
levelColors: [
|
||||
"#999999",
|
||||
"#993333"
|
||||
'#999999',
|
||||
'#993333'
|
||||
]
|
||||
});
|
||||
})
|
||||
ggjitter = new JustGage({
|
||||
id: 'ggjitter',
|
||||
title: "Jitter",
|
||||
label: "ms",
|
||||
titleFontFamily : "Open Sans",
|
||||
valueFontFamily : "Open Sans",
|
||||
title: 'Jitter',
|
||||
label: 'ms',
|
||||
titleFontFamily: 'Open Sans',
|
||||
valueFontFamily: 'Open Sans',
|
||||
refreshAnimationTime: 300,
|
||||
value: 0,
|
||||
min: 0,
|
||||
|
@ -162,22 +163,22 @@
|
|||
formatNumber: true,
|
||||
humanFriendly: false,
|
||||
levelColors: [
|
||||
"#999999",
|
||||
"#993333"
|
||||
'#999999',
|
||||
'#993333'
|
||||
]
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
function updateGauge(gauge, value)
|
||||
{
|
||||
function updateGauge(gauge, value) {
|
||||
// Alway use next power of 2 as maximum
|
||||
var max = Math.max(Math.pow(2, Math.ceil(Math.log2(value))), gauge.config.max);
|
||||
var max = Math.max(Math.pow(2, Math.ceil(Math.log2(value))), gauge.config.max)
|
||||
// Refresh the gauge
|
||||
gauge.refresh(value, max);
|
||||
gauge.refresh(value, max)
|
||||
}
|
||||
</script>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700|Roboto:400,500,700" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Speed Test</h1>
|
||||
<div id="testArea">
|
||||
|
@ -191,6 +192,6 @@
|
|||
<a href="javascript:runTest()" id="startBtn" class="button">Start</a>
|
||||
<a href="javascript:abortTest()" id="abortBtn" class="button" style="display:none;">Abort</a>
|
||||
</div>
|
||||
<p>Fonts: <a href="https://fonts.google.com/">Google Gonts</a> | Gauges: <a href="http://justgage.com/">justgage.com</a></p>
|
||||
<p>Fonts: <a href="https://fonts.google.com/">Google Fonts</a> | Gauges: <a href="http://justgage.com/">justgage.com</a></p>
|
||||
</body>
|
||||
</html>
|
258
example5.html
258
example5.html
|
@ -3,12 +3,14 @@
|
|||
<head>
|
||||
<title>Speedtest</title>
|
||||
<style type="text/css">
|
||||
html,body{
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#startBtn {
|
||||
display: inline-block;
|
||||
border: 0.15em solid #000000;
|
||||
|
@ -17,12 +19,14 @@
|
|||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#ip {
|
||||
margin: 0.8em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#chart1Area, #chart2Area{
|
||||
#chart1Area,
|
||||
#chart2Area {
|
||||
width: 100%;
|
||||
max-width: 30em;
|
||||
height: 10em;
|
||||
|
@ -30,101 +34,101 @@
|
|||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
<script src="example5_data/Chart.bundle.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var w=null;
|
||||
var w = null
|
||||
function runTest() {
|
||||
var chart1ctx=document.getElementById("chart1Area").getContext('2d'),
|
||||
chart2ctx=document.getElementById("chart2Area").getContext('2d');
|
||||
var chart1ctx = document.getElementById('chart1Area').getContext('2d')
|
||||
var chart2ctx = document.getElementById('chart2Area').getContext('2d')
|
||||
var dlDataset = {
|
||||
label: "Download",
|
||||
label: 'Download',
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: "rgba(75,192,192,0.4)",
|
||||
borderColor: "rgba(75,192,192,1)",
|
||||
backgroundColor: 'rgba(75,192,192,0.4)',
|
||||
borderColor: 'rgba(75,192,192,1)',
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: "rgba(75,192,192,1)",
|
||||
pointBackgroundColor: "#fff",
|
||||
pointBorderColor: 'rgba(75,192,192,1)',
|
||||
pointBackgroundColor: '#fff',
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(75,192,192,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointHoverBackgroundColor: 'rgba(75,192,192,1)',
|
||||
pointHoverBorderColor: 'rgba(220,220,220,1)',
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [0],
|
||||
spanGaps: false,
|
||||
},
|
||||
ulDataset={
|
||||
label: "Upload",
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: "rgba(192,192,75,0.4)",
|
||||
borderColor: "rgba(192,192,75,1)",
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: "rgba(192,192,75,1)",
|
||||
pointBackgroundColor: "#fff",
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(192,192,75,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [0],
|
||||
spanGaps: false,
|
||||
},
|
||||
pingDataset={
|
||||
label: "Ping",
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: "rgba(75,220,75,0.4)",
|
||||
borderColor: "rgba(75,220,75,1)",
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: "rgba(75,220,75,1)",
|
||||
pointBackgroundColor: "#fff",
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(75,220,75,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [],
|
||||
spanGaps: false,
|
||||
},
|
||||
jitterDataset={
|
||||
label: "Jitter",
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: "rgba(220,75,75,0.4)",
|
||||
borderColor: "rgba(220,75,75,1)",
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: "rgba(220,75,75,1)",
|
||||
pointBackgroundColor: "#fff",
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: "rgba(220,75,75,1)",
|
||||
pointHoverBorderColor: "rgba(220,220,220,1)",
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [],
|
||||
spanGaps: false,
|
||||
spanGaps: false
|
||||
}
|
||||
;
|
||||
var ulDataset = {
|
||||
label: 'Upload',
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: 'rgba(192,192,75,0.4)',
|
||||
borderColor: 'rgba(192,192,75,1)',
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: 'rgba(192,192,75,1)',
|
||||
pointBackgroundColor: '#fff',
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: 'rgba(192,192,75,1)',
|
||||
pointHoverBorderColor: 'rgba(220,220,220,1)',
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [0],
|
||||
spanGaps: false
|
||||
}
|
||||
var pingDataset = {
|
||||
label: 'Ping',
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: 'rgba(75,220,75,0.4)',
|
||||
borderColor: 'rgba(75,220,75,1)',
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: 'rgba(75,220,75,1)',
|
||||
pointBackgroundColor: '#fff',
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: 'rgba(75,220,75,1)',
|
||||
pointHoverBorderColor: 'rgba(220,220,220,1)',
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [],
|
||||
spanGaps: false
|
||||
}
|
||||
var jitterDataset = {
|
||||
label: 'Jitter',
|
||||
fill: false,
|
||||
lineTension: 0.1,
|
||||
backgroundColor: 'rgba(220,75,75,0.4)',
|
||||
borderColor: 'rgba(220,75,75,1)',
|
||||
borderCapStyle: 'butt',
|
||||
borderDash: [],
|
||||
borderDashOffset: 0.0,
|
||||
borderJoinStyle: 'miter',
|
||||
pointBorderColor: 'rgba(220,75,75,1)',
|
||||
pointBackgroundColor: '#fff',
|
||||
pointBorderWidth: 1,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: 'rgba(220,75,75,1)',
|
||||
pointHoverBorderColor: 'rgba(220,220,220,1)',
|
||||
pointHoverBorderWidth: 2,
|
||||
pointRadius: 1,
|
||||
pointHitRadius: 10,
|
||||
data: [],
|
||||
spanGaps: false
|
||||
}
|
||||
|
||||
var chart1Options = {
|
||||
type: 'line',
|
||||
data: {
|
||||
|
@ -133,13 +137,13 @@
|
|||
options: {
|
||||
responsive: true,
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
position: 'bottom'
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
|
@ -147,15 +151,15 @@
|
|||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: "Speed",
|
||||
scaleLabel: 'Speed',
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
chart2Options={
|
||||
}
|
||||
var chart2Options = {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [pingDataset, jitterDataset]
|
||||
|
@ -163,13 +167,13 @@
|
|||
options: {
|
||||
responsive: true,
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
position: 'bottom'
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
|
@ -177,74 +181,76 @@
|
|||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: "Latency",
|
||||
scaleLabel: 'Latency',
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var chart1 = new Chart(chart1ctx, chart1Options),
|
||||
chart2 = new Chart(chart2ctx, chart2Options);
|
||||
var chart1 = new Chart(chart1ctx, chart1Options)
|
||||
var chart2 = new Chart(chart2ctx, chart2Options)
|
||||
|
||||
document.getElementById("startBtn").style.display="none";
|
||||
document.getElementById("testArea").style.display="";
|
||||
document.getElementById("abortBtn").style.display="";
|
||||
w=new Worker("speedtest_worker.min.js");
|
||||
var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
|
||||
document.getElementById('startBtn').style.display = 'none'
|
||||
document.getElementById('testArea').style.display = ''
|
||||
document.getElementById('abortBtn').style.display = ''
|
||||
w = new Worker('speedtest_worker.min.js')
|
||||
var interval = setInterval(function () { w.postMessage('status') }, 100)
|
||||
w.onmessage = function (event) {
|
||||
var data=event.data.split(";");
|
||||
var status=Number(data[0]);
|
||||
var data = event.data.split(';')
|
||||
var status = Number(data[0])
|
||||
if (status >= 4) {
|
||||
clearInterval(interval);
|
||||
document.getElementById("abortBtn").style.display="none";
|
||||
document.getElementById("startBtn").style.display="";
|
||||
w=null;
|
||||
clearInterval(interval)
|
||||
document.getElementById('abortBtn').style.display = 'none'
|
||||
document.getElementById('startBtn').style.display = ''
|
||||
w = null
|
||||
}
|
||||
if(status==5){
|
||||
document.getElementById("testArea").style.display="none";
|
||||
if (status === 5) {
|
||||
document.getElementById('testArea').style.display = 'none'
|
||||
}
|
||||
if(status==1&&Number(data[1])>0){
|
||||
chart1.data.datasets[0].data.push(Number(data[1]));
|
||||
chart1.data.labels[chart1.data.datasets[0].data.length-1]="";
|
||||
chart1.update();
|
||||
if (status === 1 && Number(data[1]) > 0) {
|
||||
chart1.data.datasets[0].data.push(Number(data[1]))
|
||||
chart1.data.labels[chart1.data.datasets[0].data.length - 1] = ''
|
||||
chart1.update()
|
||||
}
|
||||
if(status==3&&Number(data[2])>0){
|
||||
chart1.data.datasets[1].data.push(Number(data[2]));
|
||||
chart1.data.labels[chart1.data.datasets[1].data.length-1]="";
|
||||
chart1.update();
|
||||
if (status === 3 && Number(data[2]) > 0) {
|
||||
chart1.data.datasets[1].data.push(Number(data[2]))
|
||||
chart1.data.labels[chart1.data.datasets[1].data.length - 1] = ''
|
||||
chart1.update()
|
||||
}
|
||||
if(status==2&&Number(data[3])>0){
|
||||
chart2.data.datasets[0].data.push(Number(data[3]));
|
||||
chart2.data.datasets[1].data.push(Number(data[5]));
|
||||
chart2.data.labels[chart2.data.datasets[0].data.length-1]="";
|
||||
chart2.data.labels[chart2.data.datasets[1].data.length-1]="";
|
||||
chart2.update();
|
||||
if (status === 2 && Number(data[3]) > 0) {
|
||||
chart2.data.datasets[0].data.push(Number(data[3]))
|
||||
chart2.data.datasets[1].data.push(Number(data[5]))
|
||||
chart2.data.labels[chart2.data.datasets[0].data.length - 1] = ''
|
||||
chart2.data.labels[chart2.data.datasets[1].data.length - 1] = ''
|
||||
chart2.update()
|
||||
}
|
||||
ip.innerHTML=data[4];
|
||||
}.bind(this);
|
||||
w.postMessage("start");
|
||||
ip.textContent = data[4]
|
||||
}
|
||||
w.postMessage('start')
|
||||
}
|
||||
function abortTest() {
|
||||
if(w)w.postMessage("abort");
|
||||
if (w) w.postMessage('abort')
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Speedtest</h1>
|
||||
<div id="testArea" style="display:none">
|
||||
<div id="ip">Please wait...</div>
|
||||
<p id="ip">Please wait...</p>
|
||||
|
||||
<h2>Speed</h2>
|
||||
<canvas id="chart1Area"></canvas>
|
||||
|
||||
<h2>Latency</h2>
|
||||
<canvas id="chart2Area"></canvas>
|
||||
<br/>
|
||||
<a href="javascript:abortTest()" id="abortBtn">Abort</a>
|
||||
</div>
|
||||
<a href="javascript:runTest()" id="startBtn">Run speedtest</a>
|
||||
<br/><br/>
|
||||
Charts by <a href="http://www.chartjs.org/">Chart.js</a>
|
||||
<br/><br/> Charts by <a href="http://www.chartjs.org/">Chart.js</a>
|
||||
</body>
|
||||
</html>
|
16
example5_data/Chart.bundle.min.js
vendored
16
example5_data/Chart.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
104
example6.html
Normal file
104
example6.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
|
||||
<title>Speedtest</title>
|
||||
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<style type="text/css">
|
||||
.st-block {
|
||||
text-align: center;
|
||||
}
|
||||
.st-btn {
|
||||
margin-top: -0.5rem;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
.st-value>span:empty::before {
|
||||
content: "0.00";
|
||||
color: #636c72;
|
||||
}
|
||||
#st-ip:empty::before {
|
||||
content: "___.___.___.___";
|
||||
color: #636c72;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="my-4">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 mb-3">
|
||||
<p class="h1">
|
||||
Speedtest
|
||||
<button id="st-start" class="btn btn-outline-primary st-btn" onclick="startTest()">Start</button>
|
||||
<button id="st-stop" class="btn btn-danger st-btn" onclick="stopTest()" hidden="true">Stop</button>
|
||||
</p>
|
||||
<p class="lead">
|
||||
Your IP: <span id="st-ip"></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 mb-3 st-block">
|
||||
<h3>Download</h3>
|
||||
<p class="display-4 st-value"><span id="st-download"></span></p>
|
||||
<p class="lead">Mbit/s</p>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 mb-3 st-block">
|
||||
<h3>Upload</h3>
|
||||
<p class="display-4 st-value"><span id="st-upload"></span></p>
|
||||
<p class="lead">Mbit/s</p>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 mb-3 st-block">
|
||||
<h3>Ping</h3>
|
||||
<p class="display-4 st-value"><span id="st-ping"></span></p>
|
||||
<p class="lead">ms</p>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 mb-3 st-block">
|
||||
<h3>Jitter</h3>
|
||||
<p class="display-4 st-value"><span id="st-jitter"></span></p>
|
||||
<p class="lead">ms</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var worker = null
|
||||
function startTest() {
|
||||
document.getElementById('st-start').hidden = true
|
||||
document.getElementById('st-stop').hidden = false
|
||||
worker = new Worker('speedtest_worker.min.js')
|
||||
var interval = setInterval(function () { worker.postMessage('status') }, 100)
|
||||
worker.onmessage = function (event) {
|
||||
var download = document.getElementById('st-download')
|
||||
var upload = document.getElementById('st-upload')
|
||||
var ping = document.getElementById('st-ping')
|
||||
var jitter = document.getElementById('st-jitter')
|
||||
var ip = document.getElementById('st-ip')
|
||||
|
||||
var data = event.data.split(';')
|
||||
var status = Number(data[0])
|
||||
if (status >= 4) {
|
||||
clearInterval(interval)
|
||||
document.getElementById('st-start').hidden = false
|
||||
document.getElementById('st-stop').hidden = true
|
||||
w = null
|
||||
}
|
||||
if (status === 5) {
|
||||
// speedtest cancelled, clear output data
|
||||
data = []
|
||||
}
|
||||
download.textContent = data[1]
|
||||
upload.textContent = data[2]
|
||||
ping.textContent = data[3]
|
||||
ip.textContent = data[4]
|
||||
jitter.textContent = data[5]
|
||||
}
|
||||
worker.postMessage('start')
|
||||
}
|
||||
function stopTest() {
|
||||
if (worker) worker.postMessage('abort')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -6,22 +6,22 @@
|
|||
*/
|
||||
|
||||
// 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
|
||||
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
|
||||
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)
|
||||
|
@ -29,10 +29,10 @@ var settings={
|
|||
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
|
||||
interval=null; //timer used in tests
|
||||
var xhr = null // array of currently active xhr requests
|
||||
var interval = null // timer used in tests
|
||||
|
||||
/*
|
||||
when set to true (automatically) the download test will use the fetch api instead of xhr.
|
||||
|
@ -40,7 +40,7 @@ var xhr=null, //array of currently active xhr requests
|
|||
-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)
|
||||
*/
|
||||
var useFetchAPI=false;
|
||||
var useFetchAPI = false
|
||||
|
||||
/*
|
||||
listener for commands from main thread to this worker.
|
||||
|
@ -51,299 +51,300 @@ var useFetchAPI=false;
|
|||
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);
|
||||
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;
|
||||
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
|
||||
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;
|
||||
var ua = navigator.userAgent
|
||||
if (/Firefox.(\d+\.\d+)/i.test(ua)) {
|
||||
// ff more precise with 1 upload stream
|
||||
settings.xhr_ulMultistream=1;
|
||||
settings.xhr_ulMultistream = 1
|
||||
}
|
||||
if (/Edge.(\d+\.\d+)/i.test(ua)) {
|
||||
// edge more precise with 3 download streams
|
||||
settings.xhr_dlMultistream=3;
|
||||
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;
|
||||
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;
|
||||
if (settings.allow_fetchAPI) useFetchAPI = true
|
||||
// chrome more precise with 5 streams
|
||||
settings.xhr_dlMultistream=5;
|
||||
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;
|
||||
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;});});})});
|
||||
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
|
||||
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){}
|
||||
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;
|
||||
xhr = null
|
||||
}
|
||||
}
|
||||
// gets client's IP using url_getIp, then calls the done function
|
||||
function getIp (done) {
|
||||
xhr=new XMLHttpRequest();
|
||||
xhr = new XMLHttpRequest()
|
||||
xhr.onload = function () {
|
||||
clientIp=xhr.responseText;
|
||||
done();
|
||||
clientIp = xhr.responseText
|
||||
done()
|
||||
}
|
||||
xhr.onerror = function () {
|
||||
done();
|
||||
done()
|
||||
}
|
||||
xhr.open("GET",settings.url_getIp+"?r="+Math.random(),true);
|
||||
xhr.send();
|
||||
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
|
||||
var dlCalled = false // used to prevent multiple accidental calls to dlTest
|
||||
function dlTest (done) {
|
||||
if(dlCalled) return; else dlCalled=true; //dlTest already called?
|
||||
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=[];
|
||||
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 (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();
|
||||
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();
|
||||
totLoaded += result.value.length
|
||||
if (xhr[i].cancelRequested) reader.cancel()
|
||||
}
|
||||
return consume();
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
return consume();
|
||||
}.bind(this));
|
||||
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;
|
||||
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
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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();
|
||||
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);
|
||||
}.bind(this), 1 + delay)
|
||||
}.bind(this)
|
||||
// open streams
|
||||
for (var i = 0; i < settings.xhr_dlMultistream; i++) {
|
||||
testStream(i,100*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
|
||||
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();
|
||||
if (failed || isNaN(dlStatus)) dlStatus = 'Fail'
|
||||
clearRequests()
|
||||
clearInterval(interval)
|
||||
done()
|
||||
}
|
||||
}.bind(this),200);
|
||||
}.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
|
||||
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
|
||||
startT=new Date().getTime(), //timestamp when test was started
|
||||
failed=false; //set to true if a stream fails
|
||||
xhr=[];
|
||||
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;
|
||||
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;
|
||||
xhr[i].upload.onprogress
|
||||
ie11workaround = false
|
||||
} catch (e) {
|
||||
ie11workaround=true;
|
||||
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);
|
||||
totLoaded += 262144
|
||||
testStream(i, 0)
|
||||
}
|
||||
xhr[i].onerror = function () {
|
||||
// error, abort
|
||||
failed=true;
|
||||
try{xhr[i].abort();}catch(e){}
|
||||
delete(xhr[i]);
|
||||
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);
|
||||
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
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}.bind(this), 1)
|
||||
}.bind(this)
|
||||
// open streams
|
||||
for (var i = 0; i < settings.xhr_ulMultistream; i++) {
|
||||
testStream(i,100*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
|
||||
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();
|
||||
if (failed || isNaN(ulStatus)) ulStatus = 'Fail'
|
||||
clearRequests()
|
||||
clearInterval(interval)
|
||||
done()
|
||||
}
|
||||
}.bind(this),200);
|
||||
}.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
|
||||
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=[];
|
||||
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();
|
||||
prevT = new Date().getTime()
|
||||
xhr[0] = new XMLHttpRequest()
|
||||
xhr[0].onload = function () {
|
||||
// pong
|
||||
if(i==0){
|
||||
prevT=new Date().getTime(); //first 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.
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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
|
||||
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
|
||||
}
|
||||
|
|
2
speedtest_worker.min.js
vendored
2
speedtest_worker.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue