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.
|
@ -1,29 +1,35 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Speedtest</title>
|
<title>Speedtest</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Speedtest</h1>
|
<h1>Speedtest</h1>
|
||||||
<h4>IP Address</h4>
|
|
||||||
<div id="ip"></div>
|
<h4>IP Address</h4>
|
||||||
<h4>Download</h4>
|
<p id="ip"></p>
|
||||||
<div id="download"></div>
|
|
||||||
<h4>Upload</h4>
|
<h4>Download</h4>
|
||||||
<div id="upload"></div>
|
<p id="download"></p>
|
||||||
<h4>Latency</h4>
|
|
||||||
<div id="ping"></div>
|
<h4>Upload</h4>
|
||||||
<script type="text/javascript">
|
<p id="upload"></p>
|
||||||
var w=new Worker("speedtest_worker.min.js"); //create new worker
|
|
||||||
setInterval(function(){w.postMessage("status");}.bind(this),100); //ask for status every 100ms
|
<h4>Latency</h4>
|
||||||
w.onmessage=function(event){ //when status is received, split the string and put the values in the appropriate fields
|
<p id="ping"></p>
|
||||||
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";
|
<script type="text/javascript">
|
||||||
document.getElementById("upload").innerHTML=data[2]+" Mbit/s";
|
var w = new Worker('speedtest_worker.min.js') // create new worker
|
||||||
document.getElementById("ping").innerHTML=data[3]+" ms, "+data[5]+" ms jitter";
|
setInterval(function () { w.postMessage('status') }, 100) // ask for status every 100ms
|
||||||
document.getElementById("ip").innerHTML=data[4];
|
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)
|
||||||
w.postMessage("start"); //start the speedtest (default params. keep garbage.php and empty.dat in the same directory as the js file)
|
document.getElementById('download').textContent = data[1] + ' Mbit/s'
|
||||||
</script>
|
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)
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
161
example2.html
161
example2.html
|
@ -1,78 +1,91 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Speedtest</title>
|
<title>Speedtest</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html,body{
|
html,
|
||||||
margin:0;
|
body {
|
||||||
padding:0;
|
margin: 0;
|
||||||
border:none;
|
padding: 0;
|
||||||
text-align:center;
|
border: none;
|
||||||
}
|
text-align: center;
|
||||||
div.test{
|
}
|
||||||
display:inline-block;
|
|
||||||
margin:1em;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
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>
|
div.test {
|
||||||
</head>
|
display: inline-block;
|
||||||
<body>
|
margin: 1em;
|
||||||
<h1>Speedtest</h1>
|
text-align: center;
|
||||||
<div class="test">
|
}
|
||||||
<div class="testName">Download</div>
|
|
||||||
<div class="meter"> <span id="download"></span> </div>
|
div.testName,
|
||||||
<div class="meterUnit">Mbit/s</div>
|
div.meterUnit {
|
||||||
</div>
|
font-size: 3vw;
|
||||||
<div class="test">
|
}
|
||||||
<div class="testName">Upload</div>
|
|
||||||
<div class="meter"> <span id="upload"></span> </div>
|
div.meter {
|
||||||
<div class="meterUnit">Mbit/s</div>
|
font-size: 6vw;
|
||||||
</div>
|
line-height: 1.5em;
|
||||||
<div class="test">
|
height: 1.5em !important;
|
||||||
<div class="testName">Latency</div>
|
}
|
||||||
<div class="meter"> <span id="ping"></span> </div>
|
|
||||||
<div class="meterUnit">ms</div>
|
.flash {
|
||||||
</div>
|
animation: flash 0.6s linear infinite;
|
||||||
<div class="test">
|
}
|
||||||
<div class="testName">Jitter</div>
|
|
||||||
<div class="meter"> <span id="jitter"></span> </div>
|
@keyframes flash {
|
||||||
<div class="meterUnit">ms</div>
|
0% { opacity: 0.6; }
|
||||||
</div>
|
50% { opacity: 1; }
|
||||||
<div id="ip"></div>
|
}
|
||||||
<script type="text/javascript">
|
</style>
|
||||||
var w=new Worker("speedtest_worker.min.js");
|
</head>
|
||||||
var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
|
|
||||||
w.onmessage=function(event){
|
<body>
|
||||||
var data=event.data.split(";");
|
<h1>Speedtest</h1>
|
||||||
var status=Number(data[0]);
|
<div class="test">
|
||||||
var dl=document.getElementById("download"),ul=document.getElementById("upload"),ping=document.getElementById("ping"),ip=document.getElementById("ip"),jitter=document.getElementById("jitter");
|
<div class="testName">Download</div>
|
||||||
dl.className=status==1?"flash":"";ping.className=status==2?"flash":"";jitter.className=ul.className=status==3?"flash":"";
|
<div class="meter"> <span id="download"></span> </div>
|
||||||
if(status>=4){
|
<div class="meterUnit">Mbit/s</div>
|
||||||
clearInterval(interval);
|
</div>
|
||||||
}
|
<div class="test">
|
||||||
dl.innerHTML=data[1];
|
<div class="testName">Upload</div>
|
||||||
ul.innerHTML=data[2];
|
<div class="meter"> <span id="upload"></span> </div>
|
||||||
ping.innerHTML=data[3];
|
<div class="meterUnit">Mbit/s</div>
|
||||||
jitter.innerHTML=data[5];
|
</div>
|
||||||
ip.innerHTML="Your IP: "+data[4];
|
<div class="test">
|
||||||
}.bind(this);
|
<div class="testName">Latency</div>
|
||||||
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
|
<div class="meter"> <span id="ping"></span> </div>
|
||||||
</script>
|
<div class="meterUnit">ms</div>
|
||||||
</body>
|
</div>
|
||||||
|
<div class="test">
|
||||||
|
<div class="testName">Jitter</div>
|
||||||
|
<div class="meter"> <span id="jitter"></span> </div>
|
||||||
|
<div class="meterUnit">ms</div>
|
||||||
|
</div>
|
||||||
|
<div id="ip"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
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')
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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>
|
</html>
|
237
example3.html
237
example3.html
|
@ -1,116 +1,133 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Speedtest</title>
|
<title>Speedtest</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html,body{
|
html,
|
||||||
margin:0;
|
body {
|
||||||
padding:0;
|
margin: 0;
|
||||||
border:none;
|
padding: 0;
|
||||||
text-align:center;
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.test {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 1em;
|
||||||
|
font-size: 2vw;
|
||||||
|
min-width: 20vw;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
padding: 0.3em 0.5em;
|
||||||
|
margin: 0.6em;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
div.test{
|
}
|
||||||
display:inline-block;
|
</style>
|
||||||
margin:1em;
|
<script type="text/javascript">
|
||||||
font-size:2vw;
|
var w = null
|
||||||
min-width:20vw;
|
function runTest() {
|
||||||
text-align:center;
|
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')
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if (status === 5) {
|
||||||
|
document.getElementById('testArea').style.display = 'none'
|
||||||
|
}
|
||||||
|
dl.textContent = data[1]
|
||||||
|
ul.textContent = data[2]
|
||||||
|
ping.textContent = data[3]
|
||||||
|
jitter.textContent = data[5]
|
||||||
|
ip.textContent = data[4]
|
||||||
}
|
}
|
||||||
div.testName,div.meterUnit{
|
w.postMessage('start')
|
||||||
font-size:1em;
|
}
|
||||||
}
|
function abortTest() {
|
||||||
div.meter{
|
if (w) w.postMessage('abort')
|
||||||
font-size:1.5em;
|
}
|
||||||
line-height:1.5em;
|
</script>
|
||||||
height:2em !important;
|
</head>
|
||||||
}
|
|
||||||
.flash{
|
<body>
|
||||||
animation:flash 0.6s linear infinite;
|
<h1>Speedtest</h1>
|
||||||
}
|
<div id="testArea" style="display:none">
|
||||||
@keyframes flash{
|
<div id="ip">Please wait...</div>
|
||||||
0%{opacity:0.6;}
|
<div class="test">
|
||||||
50%{opacity:1;}
|
<div class="testName">Download</div>
|
||||||
}
|
<div class="meter"> <span id="download"></span> </div>
|
||||||
a{
|
<div class="meterUnit">Mbit/s</div>
|
||||||
display:inline-block;
|
|
||||||
border:0.15em solid #000000;
|
|
||||||
padding:0.3em 0.5em;
|
|
||||||
margin:0.6em;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type="text/javascript">
|
|
||||||
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);
|
|
||||||
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":"";
|
|
||||||
if(status>=4){
|
|
||||||
clearInterval(interval);
|
|
||||||
document.getElementById("abortBtn").style.display="none";
|
|
||||||
document.getElementById("startBtn").style.display="";
|
|
||||||
w=null;
|
|
||||||
}
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
function abortTest(){
|
|
||||||
if(w)w.postMessage("abort");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Speedtest</h1>
|
|
||||||
<div id="testArea" style="display:none">
|
|
||||||
<div id="ip">Please wait...</div>
|
|
||||||
<div class="test">
|
|
||||||
<div class="testName">Download</div>
|
|
||||||
<div class="meter"> <span id="download"></span> </div>
|
|
||||||
<div class="meterUnit">Mbit/s</div>
|
|
||||||
</div>
|
|
||||||
<div class="test">
|
|
||||||
<div class="testName">Upload</div>
|
|
||||||
<div class="meter"> <span id="upload"></span> </div>
|
|
||||||
<div class="meterUnit">Mbit/s</div>
|
|
||||||
</div>
|
|
||||||
<div class="test">
|
|
||||||
<div class="testName">Latency</div>
|
|
||||||
<div class="meter"> <span id="ping"></span> </div>
|
|
||||||
<div class="meterUnit">ms</div>
|
|
||||||
</div>
|
|
||||||
<div class="test">
|
|
||||||
<div class="testName">Jitter</div>
|
|
||||||
<div class="meter"> <span id="jitter"></span> </div>
|
|
||||||
<div class="meterUnit">ms</div>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<a href="javascript:abortTest()" id="abortBtn">Abort</a>
|
|
||||||
</div>
|
</div>
|
||||||
<a href="javascript:runTest()" id="startBtn">Run speedtest</a>
|
<div class="test">
|
||||||
</body>
|
<div class="testName">Upload</div>
|
||||||
|
<div class="meter"> <span id="upload"></span> </div>
|
||||||
|
<div class="meterUnit">Mbit/s</div>
|
||||||
|
</div>
|
||||||
|
<div class="test">
|
||||||
|
<div class="testName">Latency</div>
|
||||||
|
<div class="meter"> <span id="ping"></span> </div>
|
||||||
|
<div class="meterUnit">ms</div>
|
||||||
|
</div>
|
||||||
|
<div class="test">
|
||||||
|
<div class="testName">Jitter</div>
|
||||||
|
<div class="meter"> <span id="jitter"></span> </div>
|
||||||
|
<div class="meterUnit">ms</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<a href="javascript:abortTest()" id="abortBtn">Abort</a>
|
||||||
|
</div>
|
||||||
|
<a href="javascript:runTest()" id="startBtn">Run speedtest</a>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
371
example4.html
371
example4.html
|
@ -1,196 +1,197 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Speedtest</title>
|
<title>Speedtest</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
}
|
||||||
|
|
||||||
html,body{
|
h1,
|
||||||
margin:0;
|
h2,
|
||||||
padding:0;
|
h3,
|
||||||
border:none;
|
h4,
|
||||||
text-align:center;
|
h5,
|
||||||
font-family: 'Open Sans';
|
h6 {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.meter {
|
||||||
|
display: inline-block;
|
||||||
|
height: 300px;
|
||||||
|
width: 400px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 6vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#testArea {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 10px 5px 0 2px;
|
||||||
|
padding: 16px 40px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 18px;
|
||||||
|
border: none;
|
||||||
|
background: #34aadc;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 700;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
#ip {
|
||||||
|
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
|
||||||
|
function runTest() {
|
||||||
|
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])
|
||||||
|
if (status >= 4) {
|
||||||
|
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').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')
|
||||||
|
}
|
||||||
|
|
||||||
h1,h2,h3,h4,h5,h6
|
document.addEventListener('DOMContentLoaded', function (event) {
|
||||||
{
|
ggdl = new JustGage({
|
||||||
font-family: 'Roboto', sans-serif;
|
id: 'ggdl',
|
||||||
font-weight: 700;
|
title: 'Download',
|
||||||
}
|
label: 'Mbit/s',
|
||||||
|
titleFontFamily: 'Open Sans',
|
||||||
|
valueFontFamily: 'Open Sans',
|
||||||
|
refreshAnimationTime: 300,
|
||||||
|
value: 0,
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
decimals: 2,
|
||||||
|
formatNumber: true,
|
||||||
|
humanFriendly: false,
|
||||||
|
levelColors: [
|
||||||
|
'#999999',
|
||||||
|
'#339933'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
div.meter{
|
ggul = new JustGage({
|
||||||
display:inline-block;
|
id: 'ggul',
|
||||||
height:300px;
|
title: 'Upload',
|
||||||
width:400px;
|
label: 'Mbit/s',
|
||||||
text-align: center;
|
titleFontFamily: 'Open Sans',
|
||||||
font-size:6vw;
|
valueFontFamily: 'Open Sans',
|
||||||
}
|
refreshAnimationTime: 300,
|
||||||
|
value: 0,
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
decimals: 2,
|
||||||
|
formatNumber: true,
|
||||||
|
humanFriendly: false,
|
||||||
|
levelColors: [
|
||||||
|
'#999999',
|
||||||
|
'#333399'
|
||||||
|
]
|
||||||
|
|
||||||
div#testArea
|
})
|
||||||
{
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ggping = new JustGage({
|
||||||
|
id: 'ggping',
|
||||||
|
title: 'Ping',
|
||||||
|
label: 'ms',
|
||||||
|
titleFontFamily: 'Open Sans',
|
||||||
|
valueFontFamily: 'Open Sans',
|
||||||
|
refreshAnimationTime: 300,
|
||||||
|
value: 0,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
decimals: 2,
|
||||||
|
formatNumber: true,
|
||||||
|
humanFriendly: false,
|
||||||
|
levelColors: [
|
||||||
|
'#999999',
|
||||||
|
'#993333'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
ggjitter = new JustGage({
|
||||||
|
id: 'ggjitter',
|
||||||
|
title: 'Jitter',
|
||||||
|
label: 'ms',
|
||||||
|
titleFontFamily: 'Open Sans',
|
||||||
|
valueFontFamily: 'Open Sans',
|
||||||
|
refreshAnimationTime: 300,
|
||||||
|
value: 0,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
decimals: 2,
|
||||||
|
formatNumber: true,
|
||||||
|
humanFriendly: false,
|
||||||
|
levelColors: [
|
||||||
|
'#999999',
|
||||||
|
'#993333'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
a {
|
function updateGauge(gauge, value) {
|
||||||
text-decoration: none;
|
// Alway use next power of 2 as maximum
|
||||||
}
|
var max = Math.max(Math.pow(2, Math.ceil(Math.log2(value))), gauge.config.max)
|
||||||
|
// Refresh the gauge
|
||||||
|
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>
|
||||||
|
|
||||||
.button {
|
<body>
|
||||||
display: inline-block;
|
<h1>Speed Test</h1>
|
||||||
margin: 10px 5px 0 2px;
|
<div id="testArea">
|
||||||
padding: 16px 40px;
|
<div class="meter" id="ggdl"></div>
|
||||||
border-radius: 5px;
|
<div class="meter" id="ggul"></div>
|
||||||
font-size: 18px;
|
<div class="meter" id="ggping"></div>
|
||||||
border: none;
|
<div class="meter" id="ggjitter"></div>
|
||||||
background: #34aadc;
|
</div>
|
||||||
color: white;
|
<div id="ip"></div>
|
||||||
cursor: pointer;
|
<div>
|
||||||
text-transform: uppercase;
|
<a href="javascript:runTest()" id="startBtn" class="button">Start</a>
|
||||||
font-weight: 700;
|
<a href="javascript:abortTest()" id="abortBtn" class="button" style="display:none;">Abort</a>
|
||||||
font-family: 'Roboto';
|
</div>
|
||||||
}
|
<p>Fonts: <a href="https://fonts.google.com/">Google Fonts</a> | Gauges: <a href="http://justgage.com/">justgage.com</a></p>
|
||||||
|
</body>
|
||||||
#ip{
|
|
||||||
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;
|
|
||||||
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.onmessage=function(event){
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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"}');
|
|
||||||
}
|
|
||||||
function abortTest(){
|
|
||||||
if(w)w.postMessage("abort");
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function(event) {
|
|
||||||
ggdl = new JustGage({
|
|
||||||
id: 'ggdl',
|
|
||||||
title: "Download",
|
|
||||||
label: "Mbit/s",
|
|
||||||
titleFontFamily : "Open Sans",
|
|
||||||
valueFontFamily : "Open Sans",
|
|
||||||
refreshAnimationTime: 300,
|
|
||||||
value: 0,
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
decimals : 2,
|
|
||||||
formatNumber: true,
|
|
||||||
humanFriendly : false,
|
|
||||||
levelColors: [
|
|
||||||
"#999999",
|
|
||||||
"#339933"
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
ggul = new JustGage({
|
|
||||||
id: 'ggul',
|
|
||||||
title: "Upload",
|
|
||||||
label: "Mbit/s",
|
|
||||||
titleFontFamily : "Open Sans",
|
|
||||||
valueFontFamily : "Open Sans",
|
|
||||||
refreshAnimationTime: 300,
|
|
||||||
value: 0,
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
decimals : 2,
|
|
||||||
formatNumber: true,
|
|
||||||
humanFriendly : false,
|
|
||||||
levelColors: [
|
|
||||||
"#999999",
|
|
||||||
"#333399"
|
|
||||||
]
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
ggping = new JustGage({
|
|
||||||
id: 'ggping',
|
|
||||||
title: "Ping",
|
|
||||||
label: "ms",
|
|
||||||
titleFontFamily : "Open Sans",
|
|
||||||
valueFontFamily : "Open Sans",
|
|
||||||
refreshAnimationTime: 300,
|
|
||||||
value: 0,
|
|
||||||
min: 0,
|
|
||||||
max: 100,
|
|
||||||
decimals : 2,
|
|
||||||
formatNumber: true,
|
|
||||||
humanFriendly : false,
|
|
||||||
levelColors: [
|
|
||||||
"#999999",
|
|
||||||
"#993333"
|
|
||||||
]
|
|
||||||
});
|
|
||||||
ggjitter = new JustGage({
|
|
||||||
id: 'ggjitter',
|
|
||||||
title: "Jitter",
|
|
||||||
label: "ms",
|
|
||||||
titleFontFamily : "Open Sans",
|
|
||||||
valueFontFamily : "Open Sans",
|
|
||||||
refreshAnimationTime: 300,
|
|
||||||
value: 0,
|
|
||||||
min: 0,
|
|
||||||
max: 100,
|
|
||||||
decimals : 2,
|
|
||||||
formatNumber: true,
|
|
||||||
humanFriendly : false,
|
|
||||||
levelColors: [
|
|
||||||
"#999999",
|
|
||||||
"#993333"
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
// Refresh the gauge
|
|
||||||
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">
|
|
||||||
<div class="meter" id="ggdl"></div>
|
|
||||||
<div class="meter" id="ggul"></div>
|
|
||||||
<div class="meter" id="ggping"></div>
|
|
||||||
<div class="meter" id="ggjitter"></div>
|
|
||||||
</div>
|
|
||||||
<div id="ip"></div>
|
|
||||||
<div>
|
|
||||||
<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>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
486
example5.html
486
example5.html
|
@ -1,250 +1,256 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Speedtest</title>
|
<title>Speedtest</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html,body{
|
html,
|
||||||
margin:0;
|
body {
|
||||||
padding:0;
|
margin: 0;
|
||||||
border:none;
|
padding: 0;
|
||||||
text-align:center;
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#startBtn {
|
||||||
|
display: inline-block;
|
||||||
|
border: 0.15em solid #000000;
|
||||||
|
padding: 0.3em 0.5em;
|
||||||
|
margin: 0.6em;
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ip {
|
||||||
|
margin: 0.8em 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chart1Area,
|
||||||
|
#chart2Area {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 30em;
|
||||||
|
height: 10em;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<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
|
||||||
|
function runTest() {
|
||||||
|
var chart1ctx = document.getElementById('chart1Area').getContext('2d')
|
||||||
|
var chart2ctx = document.getElementById('chart2Area').getContext('2d')
|
||||||
|
var dlDataset = {
|
||||||
|
label: 'Download',
|
||||||
|
fill: false,
|
||||||
|
lineTension: 0.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',
|
||||||
|
pointBorderWidth: 1,
|
||||||
|
pointHoverRadius: 5,
|
||||||
|
pointHoverBackgroundColor: 'rgba(75,192,192,1)',
|
||||||
|
pointHoverBorderColor: 'rgba(220,220,220,1)',
|
||||||
|
pointHoverBorderWidth: 2,
|
||||||
|
pointRadius: 1,
|
||||||
|
pointHitRadius: 10,
|
||||||
|
data: [0],
|
||||||
|
spanGaps: false
|
||||||
}
|
}
|
||||||
#startBtn{
|
var ulDataset = {
|
||||||
display:inline-block;
|
label: 'Upload',
|
||||||
border:0.15em solid #000000;
|
fill: false,
|
||||||
padding:0.3em 0.5em;
|
lineTension: 0.1,
|
||||||
margin:0.6em;
|
backgroundColor: 'rgba(192,192,75,0.4)',
|
||||||
color:#000000;
|
borderColor: 'rgba(192,192,75,1)',
|
||||||
text-decoration:none;
|
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
|
||||||
}
|
}
|
||||||
#ip{
|
|
||||||
margin:0.8em 0;
|
|
||||||
font-size:1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chart1Area, #chart2Area{
|
var chart1Options = {
|
||||||
width:100%;
|
type: 'line',
|
||||||
max-width:30em;
|
data: {
|
||||||
height:10em;
|
datasets: [dlDataset, ulDataset]
|
||||||
display:block;
|
},
|
||||||
margin:0 auto;
|
options: {
|
||||||
}
|
responsive: true,
|
||||||
</style>
|
legend: {
|
||||||
<script src="example5_data/Chart.bundle.min.js"></script>
|
position: 'bottom'
|
||||||
<script type="text/javascript">
|
},
|
||||||
var w=null;
|
scales: {
|
||||||
function runTest(){
|
xAxes: [{
|
||||||
var chart1ctx=document.getElementById("chart1Area").getContext('2d'),
|
display: true,
|
||||||
chart2ctx=document.getElementById("chart2Area").getContext('2d');
|
scaleLabel: {
|
||||||
var dlDataset={
|
display: false
|
||||||
label: "Download",
|
},
|
||||||
fill: false,
|
ticks: {
|
||||||
lineTension: 0.1,
|
beginAtZero: true
|
||||||
backgroundColor: "rgba(75,192,192,0.4)",
|
}
|
||||||
borderColor: "rgba(75,192,192,1)",
|
}],
|
||||||
borderCapStyle: 'butt',
|
yAxes: [{
|
||||||
borderDash: [],
|
display: true,
|
||||||
borderDashOffset: 0.0,
|
scaleLabel: 'Speed',
|
||||||
borderJoinStyle: 'miter',
|
ticks: {
|
||||||
pointBorderColor: "rgba(75,192,192,1)",
|
beginAtZero: true
|
||||||
pointBackgroundColor: "#fff",
|
}
|
||||||
pointBorderWidth: 1,
|
}]
|
||||||
pointHoverRadius: 5,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
;
|
|
||||||
var chart1Options={
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [dlDataset,ulDataset]
|
|
||||||
},
|
|
||||||
options:{
|
|
||||||
responsive: true,
|
|
||||||
legend: {
|
|
||||||
position: 'bottom',
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
xAxes: [{
|
|
||||||
display: true,
|
|
||||||
scaleLabel: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
ticks:{
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
yAxes: [{
|
|
||||||
display: true,
|
|
||||||
scaleLabel: "Speed",
|
|
||||||
ticks:{
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chart2Options={
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [pingDataset,jitterDataset]
|
|
||||||
},
|
|
||||||
options:{
|
|
||||||
responsive: true,
|
|
||||||
legend: {
|
|
||||||
position: 'bottom',
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
xAxes: [{
|
|
||||||
display: true,
|
|
||||||
scaleLabel: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
ticks:{
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
yAxes: [{
|
|
||||||
display: true,
|
|
||||||
scaleLabel: "Latency",
|
|
||||||
ticks:{
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var chart1 = new Chart(chart1ctx, chart1Options),
|
|
||||||
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);
|
|
||||||
w.onmessage=function(event){
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
if(status==5){
|
}
|
||||||
document.getElementById("testArea").style.display="none";
|
}
|
||||||
|
var chart2Options = {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [pingDataset, jitterDataset]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
legend: {
|
||||||
|
position: 'bottom'
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: 'Latency',
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
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==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");
|
|
||||||
}
|
}
|
||||||
function abortTest(){
|
|
||||||
if(w)w.postMessage("abort");
|
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') }, 100)
|
||||||
|
w.onmessage = function (event) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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 === 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()
|
||||||
|
}
|
||||||
|
ip.textContent = data[4]
|
||||||
}
|
}
|
||||||
</script>
|
w.postMessage('start')
|
||||||
</head>
|
}
|
||||||
<body>
|
function abortTest() {
|
||||||
<h1>Speedtest</h1>
|
if (w) w.postMessage('abort')
|
||||||
<div id="testArea" style="display:none">
|
}
|
||||||
<div id="ip">Please wait...</div>
|
</script>
|
||||||
<h2>Speed</h2>
|
</head>
|
||||||
<canvas id="chart1Area"></canvas>
|
|
||||||
<h2>Latency</h2>
|
<body>
|
||||||
<canvas id="chart2Area"></canvas>
|
<h1>Speedtest</h1>
|
||||||
<br/>
|
<div id="testArea" style="display:none">
|
||||||
<a href="javascript:abortTest()" id="abortBtn">Abort</a>
|
<p id="ip">Please wait...</p>
|
||||||
</div>
|
|
||||||
<a href="javascript:runTest()" id="startBtn">Run speedtest</a>
|
<h2>Speed</h2>
|
||||||
<br/><br/>
|
<canvas id="chart1Area"></canvas>
|
||||||
Charts by <a href="http://www.chartjs.org/">Chart.js</a>
|
|
||||||
</body>
|
<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>
|
||||||
|
</body>
|
||||||
</html>
|
</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>
|
|
@ -5,34 +5,34 @@
|
||||||
GNU LGPLv3 License
|
GNU LGPLv3 License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//data reported to main thread
|
// 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 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
|
var dlStatus = '' // download speed in megabit/s with 2 decimal digits
|
||||||
ulStatus="", //upload speed in megabit/s with 2 decimal digits
|
var ulStatus = '' // upload speed in megabit/s with 2 decimal digits
|
||||||
pingStatus="", //ping in milliseconds with 2 decimal digits
|
var pingStatus = '' // ping in milliseconds with 2 decimal digits
|
||||||
jitterStatus="", //jitter in milliseconds with 2 decimal digits
|
var jitterStatus = '' // jitter in milliseconds with 2 decimal digits
|
||||||
clientIp=""; //client's IP address as reported by getIP.php
|
var clientIp = '' // client's IP address as reported by getIP.php
|
||||||
|
|
||||||
//test settings. can be overridden by sending specific values with the start command
|
// test settings. can be overridden by sending specific values with the start command
|
||||||
var settings={
|
var settings = {
|
||||||
time_ul:15, //duration of upload test in seconds
|
time_ul: 15, // duration of upload test in seconds
|
||||||
time_dl:15, //duration of download test in seconds
|
time_dl: 15, // duration of download test in seconds
|
||||||
count_ping:35, //number of pings to perform in ping test
|
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_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_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_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_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_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_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)
|
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)
|
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
|
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
|
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
|
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 xhr = null // array of currently active xhr requests
|
||||||
interval=null; //timer used in tests
|
var 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.
|
||||||
|
@ -40,7 +40,7 @@ var xhr=null, //array of currently active xhr requests
|
||||||
-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.
|
||||||
|
@ -50,300 +50,301 @@ var useFetchAPI=false;
|
||||||
-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){
|
this.addEventListener('message', function (e) {
|
||||||
var params=e.data.split(" ");
|
var params = e.data.split(' ')
|
||||||
if(params[0]=="status"){ //return status
|
if (params[0] === 'status') { // return status
|
||||||
postMessage(testStatus+";"+dlStatus+";"+ulStatus+";"+pingStatus+";"+clientIp+";"+jitterStatus);
|
postMessage(testStatus + ';' + dlStatus + ';' + ulStatus + ';' + pingStatus + ';' + clientIp + ';' + jitterStatus)
|
||||||
}
|
}
|
||||||
if(params[0]=="start"&&testStatus==0){ //start new test
|
if (params[0] === 'start' && testStatus === 0) { // start new test
|
||||||
testStatus=1;
|
testStatus = 1
|
||||||
try{
|
try {
|
||||||
//parse settings, if present
|
// parse settings, if present
|
||||||
var s=JSON.parse(e.data.substring(5));
|
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_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_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_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.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_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.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.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
|
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
|
// quirks for specific browsers. more may be added in future releases
|
||||||
if(settings.enable_quirks){
|
if (settings.enable_quirks) {
|
||||||
var ua=navigator.userAgent;
|
var ua = navigator.userAgent
|
||||||
if(/Firefox.(\d+\.\d+)/i.test(ua)){
|
if (/Firefox.(\d+\.\d+)/i.test(ua)) {
|
||||||
//ff more precise with 1 upload stream
|
// ff more precise with 1 upload stream
|
||||||
settings.xhr_ulMultistream=1;
|
settings.xhr_ulMultistream = 1
|
||||||
}
|
}
|
||||||
if(/Edge.(\d+\.\d+)/i.test(ua)){
|
if (/Edge.(\d+\.\d+)/i.test(ua)) {
|
||||||
//edge more precise with 3 download streams
|
// 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))){
|
if ((/Safari.(\d+)/i.test(ua)) && !(/Chrome.(\d+)/i.test(ua))) {
|
||||||
//safari more precise with 10 upload streams and 5mb chunks for download test
|
// safari more precise with 10 upload streams and 5mb chunks for download test
|
||||||
settings.xhr_ulMultistream=10;
|
settings.xhr_ulMultistream = 10
|
||||||
settings.garbagePhp_chunkSize=5;
|
settings.garbagePhp_chunkSize = 5
|
||||||
}
|
}
|
||||||
if(/Chrome.(\d+)/i.test(ua)&&(!!self.fetch)){
|
if (/Chrome.(\d+)/i.test(ua) && (!!self.fetch)) {
|
||||||
//chrome can't handle large xhr very well, use fetch api if available and allowed
|
// 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
|
// 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.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_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_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.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.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 (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 (settings.allow_fetchAPI && settings.force_fetchAPI && (!!self.fetch)) useFetchAPI = true
|
||||||
}catch(e){}
|
} catch (e) { }
|
||||||
//run the tests
|
// run the tests
|
||||||
console.log(settings);
|
console.log(settings)
|
||||||
console.log("Fetch API: "+useFetchAPI);
|
console.log('Fetch API: ' + useFetchAPI)
|
||||||
getIp(function(){dlTest(function(){testStatus=2;pingTest(function(){testStatus=3;ulTest(function(){testStatus=4;});});})});
|
getIp(function () { dlTest(function () { testStatus = 2; pingTest(function () { testStatus = 3; ulTest(function () { testStatus = 4 }) }) }) })
|
||||||
}
|
}
|
||||||
if(params[0]=="abort"){ //abort command
|
if (params[0] === 'abort') { // abort command
|
||||||
clearRequests(); //stop all xhr activity
|
clearRequests() // stop all xhr activity
|
||||||
if(interval)clearInterval(interval); //clear timer if present
|
if (interval) clearInterval(interval) // clear timer if present
|
||||||
testStatus=5;dlStatus="";ulStatus="";pingStatus="";jitterStatus=""; //set test as aborted
|
testStatus = 5; dlStatus = ''; ulStatus = ''; pingStatus = ''; jitterStatus = '' // set test as aborted
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
//stops all XHR activity, aggressively
|
// stops all XHR activity, aggressively
|
||||||
function clearRequests(){
|
function clearRequests () {
|
||||||
if(xhr){
|
if (xhr) {
|
||||||
for(var i=0;i<xhr.length;i++){
|
for (var i = 0; i < xhr.length; i++) {
|
||||||
if(useFetchAPI)try{xhr[i].cancelRequested=true;}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].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].upload.onprogress = null; xhr[i].upload.onload = null; xhr[i].upload.onerror = null } catch (e) { }
|
||||||
try{xhr[i].abort();}catch(e){}
|
try { xhr[i].abort() } catch (e) { }
|
||||||
try{delete(xhr[i]);}catch(e){}
|
try { delete (xhr[i]) } catch (e) { }
|
||||||
}
|
}
|
||||||
xhr=null;
|
xhr = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//gets client's IP using url_getIp, then calls the done function
|
// gets client's IP using url_getIp, then calls the done function
|
||||||
function getIp(done){
|
function getIp (done) {
|
||||||
xhr=new XMLHttpRequest();
|
xhr = new XMLHttpRequest()
|
||||||
xhr.onload=function(){
|
xhr.onload = function () {
|
||||||
clientIp=xhr.responseText;
|
clientIp = xhr.responseText
|
||||||
done();
|
done()
|
||||||
}
|
}
|
||||||
xhr.onerror=function(){
|
xhr.onerror = function () {
|
||||||
done();
|
done()
|
||||||
}
|
}
|
||||||
xhr.open("GET",settings.url_getIp+"?r="+Math.random(),true);
|
xhr.open('GET', settings.url_getIp + '?r=' + Math.random(), true)
|
||||||
xhr.send();
|
xhr.send()
|
||||||
}
|
}
|
||||||
//download test, calls done function when it's over
|
// 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){
|
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
|
var totLoaded = 0.0, // total number of loaded bytes
|
||||||
startT=new Date().getTime(), //timestamp when test was started
|
startT = new Date().getTime(), // timestamp when test was started
|
||||||
failed=false; //set to true if a stream fails
|
failed = false // set to true if a stream fails
|
||||||
xhr=[];
|
xhr = []
|
||||||
//function to create a download stream. streams are slightly delayed so that they will not end at the same time
|
// 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){
|
var testStream = function (i, delay) {
|
||||||
setTimeout(function(){
|
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){
|
if (useFetchAPI) {
|
||||||
xhr[i]=fetch(settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize).then(function(response) {
|
xhr[i] = fetch(settings.url_dl + '?r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize).then(function (response) {
|
||||||
var reader = response.body.getReader();
|
var reader = response.body.getReader()
|
||||||
var consume=function() {
|
var consume = function () {
|
||||||
return reader.read().then(function(result){
|
return reader.read().then(function (result) {
|
||||||
if(result.done) testStream(i); else{
|
if (result.done) testStream(i); else {
|
||||||
totLoaded+=result.value.length;
|
totLoaded += result.value.length
|
||||||
if(xhr[i].cancelRequested) reader.cancel();
|
if (xhr[i].cancelRequested) reader.cancel()
|
||||||
}
|
}
|
||||||
return consume();
|
return consume()
|
||||||
}.bind(this));
|
}.bind(this))
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
return consume();
|
return consume()
|
||||||
}.bind(this));
|
}.bind(this))
|
||||||
}else{
|
} else {
|
||||||
var prevLoaded=0; //number of bytes loaded last time onprogress was called
|
var prevLoaded = 0 // number of bytes loaded last time onprogress was called
|
||||||
var x=new XMLHttpRequest();
|
var x = new XMLHttpRequest()
|
||||||
xhr[i]=x;
|
xhr[i] = x
|
||||||
xhr[i].onprogress=function(event){
|
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
|
// progress event, add number of new loaded bytes to totLoaded
|
||||||
var loadDiff=event.loaded<=0?0:(event.loaded-prevLoaded);
|
var loadDiff = event.loaded <= 0 ? 0 : (event.loaded - prevLoaded)
|
||||||
if(isNaN(loadDiff)||!isFinite(loadDiff)||loadDiff<0) return; //just in case
|
if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return // just in case
|
||||||
totLoaded+=loadDiff;
|
totLoaded += loadDiff
|
||||||
prevLoaded=event.loaded;
|
prevLoaded = event.loaded
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
xhr[i].onload=function(){
|
xhr[i].onload = function () {
|
||||||
//the large file has been loaded entirely, start again
|
// the large file has been loaded entirely, start again
|
||||||
try{xhr[i].abort();}catch(e){} //reset the stream data to empty ram
|
try { xhr[i].abort() } catch (e) { } // reset the stream data to empty ram
|
||||||
testStream(i,0);
|
testStream(i, 0)
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
xhr[i].onerror=function(){
|
xhr[i].onerror = function () {
|
||||||
//error, abort
|
// error, abort
|
||||||
failed=true;
|
failed = true
|
||||||
try{xhr[i].abort();}catch(e){}
|
try { xhr[i].abort() } catch (e) { }
|
||||||
delete(xhr[i]);
|
delete (xhr[i])
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
//send xhr
|
// send xhr
|
||||||
try{if(settings.xhr_dlUseBlob) xhr[i].responseType='blob'; else xhr[i].responseType='arraybuffer';}catch(e){}
|
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].open('GET', settings.url_dl + '?r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize, true) // random string to prevent caching
|
||||||
xhr[i].send();
|
xhr[i].send()
|
||||||
}
|
}
|
||||||
}.bind(this),1+delay);
|
}.bind(this), 1 + delay)
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
//open streams
|
// open streams
|
||||||
for(var i=0;i<settings.xhr_dlMultistream;i++){
|
for (var i = 0; i < settings.xhr_dlMultistream; i++) {
|
||||||
testStream(i,100*i);
|
testStream(i, 100 * i)
|
||||||
}
|
}
|
||||||
//every 200ms, update dlStatus
|
// every 200ms, update dlStatus
|
||||||
interval=setInterval(function(){
|
interval = setInterval(function () {
|
||||||
var t=new Date().getTime()-startT;
|
var t = new Date().getTime() - startT
|
||||||
if(t<200) return;
|
if (t < 200) return
|
||||||
var speed=totLoaded/(t/1000.0);
|
var speed = totLoaded / (t / 1000.0)
|
||||||
dlStatus=((speed*8)/925000.0).toFixed(2); //925000 instead of 1048576 to account for overhead
|
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 ((t / 1000.0) > settings.time_dl || failed) { // test is over, stop streams and timer
|
||||||
if(failed||isNaN(dlStatus)) dlStatus="Fail";
|
if (failed || isNaN(dlStatus)) dlStatus = 'Fail'
|
||||||
clearRequests();
|
clearRequests()
|
||||||
clearInterval(interval);
|
clearInterval(interval)
|
||||||
done();
|
done()
|
||||||
}
|
}
|
||||||
}.bind(this),200);
|
}.bind(this), 200)
|
||||||
}
|
}
|
||||||
//upload test, calls done function whent it's over
|
// upload test, calls done function whent it's over
|
||||||
//garbage data for upload test
|
// garbage data for upload test
|
||||||
var r=new ArrayBuffer(1048576);
|
var r = new ArrayBuffer(1048576)
|
||||||
try{r=new Float32Array(r);for(var i=0;i<r.length;i++)r[i]=Math.random();}catch(e){}
|
try { r = new Float32Array(r); for (var i = 0; i < r.length; i++)r[i] = Math.random() } catch (e) { }
|
||||||
var req=[],reqsmall=[];
|
var req = []
|
||||||
for(var i=0;i<20;i++) req.push(r);
|
var reqsmall = []
|
||||||
req=new Blob(req);
|
for (var i = 0; i < 20; i++) req.push(r)
|
||||||
r=new ArrayBuffer(262144);
|
req = new Blob(req)
|
||||||
try{r=new Float32Array(r);for(var i=0;i<r.length;i++)r[i]=Math.random();}catch(e){}
|
r = new ArrayBuffer(262144)
|
||||||
reqsmall.push(r);
|
try { r = new Float32Array(r); for (var i = 0; i < r.length; i++)r[i] = Math.random() } catch (e) { }
|
||||||
reqsmall=new Blob(reqsmall);
|
reqsmall.push(r)
|
||||||
var ulCalled=false; //used to prevent multiple accidental calls to ulTest
|
reqsmall = new Blob(reqsmall)
|
||||||
function ulTest(done){
|
var ulCalled = false // used to prevent multiple accidental calls to ulTest
|
||||||
if(ulCalled) return; else ulCalled=true; //ulTest already called?
|
function ulTest (done) {
|
||||||
var totLoaded=0.0, //total number of transmitted bytes
|
if (ulCalled) return; else ulCalled = true // ulTest already called?
|
||||||
startT=new Date().getTime(), //timestamp when test was started
|
var totLoaded = 0.0 // total number of transmitted bytes
|
||||||
failed=false; //set to true if a stream fails
|
var startT = new Date().getTime() // timestamp when test was started
|
||||||
xhr=[];
|
var failed = false // set to true if a stream fails
|
||||||
//function to create an upload stream. streams are slightly delayed so that they will not end at the same time
|
xhr = []
|
||||||
var testStream=function(i,delay){
|
// function to create an upload stream. streams are slightly delayed so that they will not end at the same time
|
||||||
setTimeout(function(){
|
var testStream = function (i, delay) {
|
||||||
if(testStatus!=3) return; //delayed stream ended up starting after the end of the upload test
|
setTimeout(function () {
|
||||||
var prevLoaded=0; //number of bytes transmitted last time onprogress was called
|
if (testStatus !== 3) return // delayed stream ended up starting after the end of the upload test
|
||||||
var x=new XMLHttpRequest();
|
var prevLoaded = 0 // number of bytes transmitted last time onprogress was called
|
||||||
xhr[i]=x;
|
var x = new XMLHttpRequest()
|
||||||
var ie11workaround;
|
xhr[i] = x
|
||||||
try{
|
var ie11workaround
|
||||||
xhr[i].upload.onprogress;
|
try {
|
||||||
ie11workaround=false;
|
xhr[i].upload.onprogress
|
||||||
}catch(e){
|
ie11workaround = false
|
||||||
ie11workaround=true;
|
} 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
|
if (ie11workaround) {
|
||||||
xhr[i].onload=function(){
|
// 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
|
||||||
totLoaded+=262144;
|
xhr[i].onload = function () {
|
||||||
testStream(i,0);
|
totLoaded += 262144
|
||||||
}
|
testStream(i, 0)
|
||||||
xhr[i].onerror=function(){
|
}
|
||||||
//error, abort
|
xhr[i].onerror = function () {
|
||||||
failed=true;
|
// error, abort
|
||||||
try{xhr[i].abort();}catch(e){}
|
failed = true
|
||||||
delete(xhr[i]);
|
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].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching
|
||||||
xhr[i].send(reqsmall);
|
xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway)
|
||||||
}else{
|
xhr[i].send(reqsmall)
|
||||||
//REGULAR version, no workaround
|
} else {
|
||||||
xhr[i].upload.onprogress=function(event){
|
// REGULAR version, no workaround
|
||||||
if(testStatus!=3){try{x.abort();}catch(e){}} //just in case this XHR is still running after the upload test
|
xhr[i].upload.onprogress = function (event) {
|
||||||
//progress event, add number of new loaded bytes to totLoaded
|
if (testStatus !== 3) { try { x.abort() } catch (e) { } } // just in case this XHR is still running after the upload test
|
||||||
var loadDiff=event.loaded<=0?0:(event.loaded-prevLoaded);
|
// progress event, add number of new loaded bytes to totLoaded
|
||||||
if(isNaN(loadDiff)||!isFinite(loadDiff)||loadDiff<0) return; //just in case
|
var loadDiff = event.loaded <= 0 ? 0 : (event.loaded - prevLoaded)
|
||||||
totLoaded+=loadDiff;
|
if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return // just in case
|
||||||
prevLoaded=event.loaded;
|
totLoaded += loadDiff
|
||||||
}.bind(this);
|
prevLoaded = event.loaded
|
||||||
xhr[i].upload.onload=function(){
|
}.bind(this)
|
||||||
//this stream sent all the garbage data, start again
|
xhr[i].upload.onload = function () {
|
||||||
testStream(i,0);
|
// this stream sent all the garbage data, start again
|
||||||
}.bind(this);
|
testStream(i, 0)
|
||||||
xhr[i].upload.onerror=function(){
|
}.bind(this)
|
||||||
//error, abort
|
xhr[i].upload.onerror = function () {
|
||||||
failed=true;
|
// error, abort
|
||||||
try{xhr[i].abort();}catch(e){}
|
failed = true
|
||||||
delete(xhr[i]);
|
try { xhr[i].abort() } catch (e) { }
|
||||||
}.bind(this);
|
delete (xhr[i])
|
||||||
//send xhr
|
}.bind(this)
|
||||||
xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),true); //random string to prevent caching
|
// send xhr
|
||||||
xhr[i].setRequestHeader('Content-Encoding','identity'); //disable compression (some browsers may refuse it, but data is incompressible anyway)
|
xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching
|
||||||
xhr[i].send(req);
|
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)
|
||||||
//open streams
|
}.bind(this)
|
||||||
for(var i=0;i<settings.xhr_ulMultistream;i++){
|
// open streams
|
||||||
testStream(i,100*i);
|
for (var i = 0; i < settings.xhr_ulMultistream; i++) {
|
||||||
}
|
testStream(i, 100 * i)
|
||||||
//every 200ms, update ulStatus
|
}
|
||||||
interval=setInterval(function(){
|
// every 200ms, update ulStatus
|
||||||
var t=new Date().getTime()-startT;
|
interval = setInterval(function () {
|
||||||
if(t<200) return;
|
var t = new Date().getTime() - startT
|
||||||
var speed=totLoaded/(t/1000.0);
|
if (t < 200) return
|
||||||
ulStatus=((speed*8)/925000.0).toFixed(2); //925000 instead of 1048576 to account for overhead
|
var speed = totLoaded / (t / 1000.0)
|
||||||
if((t/1000.0)>settings.time_ul||failed){ //test is over, stop streams and timer
|
ulStatus = ((speed * 8) / 925000.0).toFixed(2) // 925000 instead of 1048576 to account for overhead
|
||||||
if(failed||isNaN(ulStatus)) ulStatus="Fail";
|
if ((t / 1000.0) > settings.time_ul || failed) { // test is over, stop streams and timer
|
||||||
clearRequests();
|
if (failed || isNaN(ulStatus)) ulStatus = 'Fail'
|
||||||
clearInterval(interval);
|
clearRequests()
|
||||||
done();
|
clearInterval(interval)
|
||||||
}
|
done()
|
||||||
}.bind(this),200);
|
}
|
||||||
|
}.bind(this), 200)
|
||||||
}
|
}
|
||||||
//ping+jitter test, function done is called when it's over
|
// 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){
|
function pingTest (done) {
|
||||||
if(ptCalled) return; else ptCalled=true; //pingTest already called?
|
if (ptCalled) return; else ptCalled = true // pingTest already called?
|
||||||
var prevT=null, //last time a pong was received
|
var prevT = null // last time a pong was received
|
||||||
ping=0.0, //current ping value
|
var ping = 0.0 // current ping value
|
||||||
jitter=0.0, //current jitter value
|
var jitter = 0.0 // current jitter value
|
||||||
i=0, //counter of pongs received
|
var i = 0 // counter of pongs received
|
||||||
prevInstspd=0; //last ping time, used for jitter calculation
|
var prevInstspd = 0 // last ping time, used for jitter calculation
|
||||||
xhr=[];
|
xhr = []
|
||||||
//ping function
|
// ping function
|
||||||
var doPing=function(){
|
var doPing = function () {
|
||||||
prevT=new Date().getTime();
|
prevT = new Date().getTime()
|
||||||
xhr[0]=new XMLHttpRequest();
|
xhr[0] = new XMLHttpRequest()
|
||||||
xhr[0].onload=function(){
|
xhr[0].onload = function () {
|
||||||
//pong
|
// pong
|
||||||
if(i==0){
|
if (i === 0) {
|
||||||
prevT=new Date().getTime(); //first pong
|
prevT = new Date().getTime() // first pong
|
||||||
}else{
|
} else {
|
||||||
var instspd=(new Date().getTime()-prevT)/2;
|
var instspd = (new Date().getTime() - prevT) / 2
|
||||||
var instjitter=Math.abs(instspd-prevInstspd);
|
var instjitter = Math.abs(instspd - prevInstspd)
|
||||||
if(i==1)ping=instspd; /*first ping, can't tell jiutter yet*/ else{
|
if (i === 1) ping = instspd; /* first ping, can't tell jiutter yet*/ else {
|
||||||
ping=ping*0.9+instspd*0.1; //ping, weighted average
|
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.
|
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);
|
pingStatus = ping.toFixed(2)
|
||||||
jitterStatus=jitter.toFixed(2);
|
jitterStatus = jitter.toFixed(2)
|
||||||
i++;
|
i++
|
||||||
if(i<settings.count_ping) doPing(); else done(); //more pings to do?
|
if (i < settings.count_ping) doPing(); else done() // more pings to do?
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
xhr[0].onerror=function(){
|
xhr[0].onerror = function () {
|
||||||
//a ping failed, cancel test
|
// a ping failed, cancel test
|
||||||
pingStatus="Fail";
|
pingStatus = 'Fail'
|
||||||
jitterStatus="Fail";
|
jitterStatus = 'Fail'
|
||||||
clearRequests();
|
clearRequests()
|
||||||
done();
|
done()
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
//sent xhr
|
// sent xhr
|
||||||
xhr[0].open("GET",settings.url_ping+"?r="+Math.random(),true); //random string to prevent caching
|
xhr[0].open('GET', settings.url_ping + '?r=' + Math.random(), true) // random string to prevent caching
|
||||||
xhr[0].send();
|
xhr[0].send()
|
||||||
}.bind(this);
|
}.bind(this)
|
||||||
doPing(); //start first ping
|
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