| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- <!DOCTYPE html>
- <html>
- <head>
- <link rel="shortcut icon" href="favicon.ico">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
- <meta charset="UTF-8" />
- <script type="text/javascript" src="speedtest.js"></script>
- <script type="text/javascript">
- function I(i) { return document.getElementById(i); }
- //LIST OF TEST SERVERS. Leave empty if you're doing a standalone installation. See documentation for details
- var SPEEDTEST_SERVERS = [
- /*{ //this server doesn't actually exist, remove it
- name:"Example Server 1", //user friendly name for the server
- server:"//test1.mydomain.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
- dlURL:"backend/garbage.php", //path to download test on this server (garbage.php or replacement)
- ulURL:"backend/empty.php", //path to upload test on this server (empty.php or replacement)
- pingURL:"backend/empty.php", //path to ping/jitter test on this server (empty.php or replacement)
- getIpURL:"backend/getIP.php" //path to getIP on this server (getIP.php or replacement)
- },
- { //this server doesn't actually exist, remove it
- name:"Example Server 2", //user friendly name for the server
- server:"//test2.example.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
- dlURL:"garbage.php", //path to download test on this server (garbage.php or replacement)
- ulURL:"empty.php", //path to upload test on this server (empty.php or replacement)
- pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
- getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
- }*/
- //add other servers here, comma separated
- ];
- //INITIALIZE SPEEDTEST
- var s = new Speedtest(); //create speed test object
- s.setParameter("telemetry_level", "basic"); //enable basic telemetry (for results sharing)
- //SERVER AUTO SELECTION
- function initServers() {
- var showStandaloneUI = function () {
- I("loading").className = "hidden";
- I("serverArea").style.display = "none";
- I("testWrapper").className = "visible";
- initUI();
- }
- var noServersAvailable = function () {
- I("message").innerHTML = "No servers available";
- }
- var runServerSelect = function () {
- s.selectServer(function (server) {
- if (server != null) { //at least 1 server is available
- I("loading").className = "hidden"; //hide loading message
- //populate server list for manual selection
- for (var i = 0; i < SPEEDTEST_SERVERS.length; i++) {
- if (SPEEDTEST_SERVERS[i].pingT == -1) continue;
- var option = document.createElement("option");
- option.value = i;
- option.textContent = SPEEDTEST_SERVERS[i].name;
- if (SPEEDTEST_SERVERS[i] === server) option.selected = true;
- I("server").appendChild(option);
- }
- //show test UI
- I("testWrapper").className = "visible";
- initUI();
- } else { //no servers are available, the test cannot proceed
- noServersAvailable();
- }
- });
- }
- var loadServerList = function (serverListUrl, onFailure) {
- s.loadServerList(serverListUrl, function (servers) {
- if (servers == null || servers.length == 0) {
- onFailure();
- } else {
- SPEEDTEST_SERVERS = servers;
- s.addTestPoints(SPEEDTEST_SERVERS);
- runServerSelect();
- }
- });
- }
- if (typeof SPEEDTEST_SERVERS === "string") {
- //need to fetch list of servers from specified URL
- loadServerList(SPEEDTEST_SERVERS, noServersAvailable);
- } else if (SPEEDTEST_SERVERS.length == 0) {
- //standalone installation by default, but use server-list.json if present
- loadServerList("server-list.json", showStandaloneUI);
- } else { //multiple servers
- //hardcoded server list
- s.addTestPoints(SPEEDTEST_SERVERS);
- runServerSelect();
- }
- }
- var meterBk = /Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent) ? "#EAEAEA" : "#80808040";
- var dlColor = "#6060AA",
- ulColor = "#616161";
- var progColor = meterBk;
- //CODE FOR GAUGES
- function drawMeter(c, amount, bk, fg, progress, prog) {
- var ctx = c.getContext("2d");
- var dp = window.devicePixelRatio || 1;
- var cw = c.clientWidth * dp, ch = c.clientHeight * dp;
- var sizScale = ch * 0.0055;
- if (c.width == cw && c.height == ch) {
- ctx.clearRect(0, 0, cw, ch);
- } else {
- c.width = cw;
- c.height = ch;
- }
- ctx.beginPath();
- ctx.strokeStyle = bk;
- ctx.lineWidth = 12 * sizScale;
- ctx.arc(c.width / 2, c.height - 58 * sizScale, c.height / 1.8 - ctx.lineWidth, -Math.PI * 1.1, Math.PI * 0.1);
- ctx.stroke();
- ctx.beginPath();
- ctx.strokeStyle = fg;
- ctx.lineWidth = 12 * sizScale;
- ctx.arc(c.width / 2, c.height - 58 * sizScale, c.height / 1.8 - ctx.lineWidth, -Math.PI * 1.1, amount * Math.PI * 1.2 - Math.PI * 1.1);
- ctx.stroke();
- if (typeof progress !== "undefined") {
- ctx.fillStyle = prog;
- ctx.fillRect(c.width * 0.3, c.height - 16 * sizScale, c.width * 0.4 * progress, 4 * sizScale);
- }
- }
- function mbpsToAmount(s) {
- return 1 - (1 / (Math.pow(1.3, Math.sqrt(s))));
- }
- function format(d) {
- d = Number(d);
- if (d < 10) return d.toFixed(2);
- if (d < 100) return d.toFixed(1);
- return d.toFixed(0);
- }
- //UI CODE
- var uiData = null;
- function startStop() {
- if (s.getState() == 3) {
- //speed test is running, abort
- s.abort();
- data = null;
- I("startStopBtn").className = "";
- I("server").disabled = false;
- initUI();
- } else {
- //test is not running, begin
- I("startStopBtn").className = "running";
- I("shareArea").style.display = "none";
- I("server").disabled = true;
- s.onupdate = function (data) {
- uiData = data;
- };
- s.onend = function (aborted) {
- I("startStopBtn").className = "";
- I("server").disabled = false;
- updateUI(true);
- if (!aborted) {
- //if testId is present, show sharing panel, otherwise do nothing
- try {
- var testId = uiData.testId;
- if (testId != null) {
- var shareURL = window.location.href.substring(0, window.location.href.lastIndexOf("/")) + "/results/?id=" + testId;
- I("resultsImg").src = shareURL;
- I("resultsURL").value = shareURL;
- I("testId").innerHTML = testId;
- I("shareArea").style.display = "";
- }
- } catch (e) { }
- }
- };
- s.start();
- }
- }
- //this function reads the data sent back by the test and updates the UI
- function updateUI(forced) {
- if (!forced && s.getState() != 3) return;
- if (uiData == null) return;
- var status = uiData.testState;
- I("ip").textContent = uiData.clientIp;
- I("dlText").textContent = (status == 1 && uiData.dlStatus == 0) ? "..." : format(uiData.dlStatus);
- drawMeter(I("dlMeter"), mbpsToAmount(Number(uiData.dlStatus * (status == 1 ? oscillate() : 1))), meterBk, dlColor, Number(uiData.dlProgress), progColor);
- I("ulText").textContent = (status == 3 && uiData.ulStatus == 0) ? "..." : format(uiData.ulStatus);
- drawMeter(I("ulMeter"), mbpsToAmount(Number(uiData.ulStatus * (status == 3 ? oscillate() : 1))), meterBk, ulColor, Number(uiData.ulProgress), progColor);
- I("pingText").textContent = format(uiData.pingStatus);
- I("jitText").textContent = format(uiData.jitterStatus);
- }
- function oscillate() {
- return 1 + 0.02 * Math.sin(Date.now() / 100);
- }
- //update the UI every frame
- window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || (function (callback, element) { setTimeout(callback, 1000 / 60); });
- function frame() {
- requestAnimationFrame(frame);
- updateUI();
- }
- frame(); //start frame loop
- //function to (re)initialize UI
- function initUI() {
- drawMeter(I("dlMeter"), 0, meterBk, dlColor, 0);
- drawMeter(I("ulMeter"), 0, meterBk, ulColor, 0);
- I("dlText").textContent = "";
- I("ulText").textContent = "";
- I("pingText").textContent = "";
- I("jitText").textContent = "";
- I("ip").textContent = "";
- }
- </script>
- <style type="text/css">
- html,
- body {
- border: none;
- padding: 0;
- margin: 0;
- background: #FFFFFF;
- color: #202020;
- }
- body {
- text-align: center;
- font-family: "Roboto", sans-serif;
- }
- h1 {
- color: #404040;
- }
- #loading {
- background-color: #FFFFFF;
- color: #404040;
- text-align: center;
- }
- span.loadCircle {
- display: inline-block;
- width: 2em;
- height: 2em;
- vertical-align: middle;
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
- background-size: 2em 2em;
- margin-right: 0.5em;
- animation: spin 0.6s linear infinite;
- }
- @keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(359deg);
- }
- }
- #startStopBtn {
- display: inline-block;
- margin: 0 auto;
- color: #6060AA;
- background-color: rgba(0, 0, 0, 0);
- border: 0.15em solid #6060FF;
- border-radius: 0.3em;
- transition: all 0.3s;
- box-sizing: border-box;
- width: 8em;
- height: 3em;
- line-height: 2.7em;
- cursor: pointer;
- box-shadow: 0 0 0 rgba(0, 0, 0, 0.1), inset 0 0 0 rgba(0, 0, 0, 0.1);
- }
- #startStopBtn:hover {
- box-shadow: 0 0 2em rgba(0, 0, 0, 0.1), inset 0 0 1em rgba(0, 0, 0, 0.1);
- }
- #startStopBtn.running {
- background-color: #FF3030;
- border-color: #FF6060;
- color: #FFFFFF;
- }
- #startStopBtn:before {
- content: "Start";
- }
- #startStopBtn.running:before {
- content: "Abort";
- }
- #serverArea {
- margin-top: 1em;
- }
- #server {
- font-size: 1em;
- padding: 0.2em;
- }
- #test {
- margin-top: 2em;
- margin-bottom: 12em;
- }
- div.testArea {
- display: inline-block;
- width: 16em;
- height: 12.5em;
- position: relative;
- box-sizing: border-box;
- }
- div.testArea2 {
- display: inline-block;
- width: 14em;
- height: 7em;
- position: relative;
- box-sizing: border-box;
- text-align: center;
- }
- div.testArea div.testName {
- position: absolute;
- top: 0.1em;
- left: 0;
- width: 100%;
- font-size: 1.4em;
- z-index: 9;
- }
- div.testArea2 div.testName {
- display: block;
- text-align: center;
- font-size: 1.4em;
- }
- div.testArea div.meterText {
- position: absolute;
- bottom: 1.55em;
- left: 0;
- width: 100%;
- font-size: 2.5em;
- z-index: 9;
- }
- div.testArea2 div.meterText {
- display: inline-block;
- font-size: 2.5em;
- }
- div.meterText:empty:before {
- content: "0.00";
- }
- div.testArea div.unit {
- position: absolute;
- bottom: 2em;
- left: 0;
- width: 100%;
- z-index: 9;
- }
- div.testArea2 div.unit {
- display: inline-block;
- }
- div.testArea canvas {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 1;
- }
- div.testGroup {
- display: block;
- margin: 0 auto;
- }
- #shareArea {
- width: 95%;
- max-width: 40em;
- margin: 0 auto;
- margin-top: 2em;
- }
- #shareArea>* {
- display: block;
- width: 100%;
- height: auto;
- margin: 0.25em 0;
- }
- #privacyPolicy {
- position: fixed;
- top: 2em;
- bottom: 2em;
- left: 2em;
- right: 2em;
- overflow-y: auto;
- width: auto;
- height: auto;
- box-shadow: 0 0 3em 1em #000000;
- z-index: 999999;
- text-align: left;
- background-color: #FFFFFF;
- padding: 1em;
- }
- a.privacy {
- text-align: center;
- font-size: 0.8em;
- color: #808080;
- padding: 0 3em;
- }
- div.closePrivacyPolicy {
- width: 100%;
- text-align: center;
- }
- div.closePrivacyPolicy a.privacy {
- padding: 1em 3em;
- }
- @media all and (max-width:40em) {
- body {
- font-size: 0.8em;
- }
- }
- div.visible {
- animation: fadeIn 0.4s;
- display: block;
- }
- div.hidden {
- animation: fadeOut 0.4s;
- display: none;
- }
- @keyframes fadeIn {
- 0% {
- opacity: 0;
- }
- 100% {
- opacity: 1;
- }
- }
- @keyframes fadeOut {
- 0% {
- display: block;
- opacity: 1;
- }
- 100% {
- display: block;
- opacity: 0;
- }
- }
- @media all and (prefers-color-scheme: dark) {
- html,
- body,
- #loading {
- background: #202020;
- color: #F4F4F4;
- color-scheme: dark;
- }
- h1 {
- color: #E0E0E0;
- }
- a {
- color: #9090FF;
- }
- #privacyPolicy {
- background: #000000;
- }
- #resultsImg {
- filter: invert(1);
- }
- }
- </style>
- <title>LibreSpeed</title>
- </head>
- <body onload="initServers()">
- <h1>LibreSpeed</h1>
- <div id="loading" class="visible">
- <p id="message"><span class="loadCircle"></span>Selecting a server...</p>
- </div>
- <div id="testWrapper" class="hidden">
- <div id="startStopBtn" onclick="startStop()"></div><br />
- <a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
- <div id="serverArea">
- Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
- </div>
- <div id="test">
- <div class="testGroup">
- <div class="testArea2">
- <div class="testName">Ping</div>
- <div id="pingText" class="meterText" style="color:#AA6060"></div>
- <div class="unit">ms</div>
- </div>
- <div class="testArea2">
- <div class="testName">Jitter</div>
- <div id="jitText" class="meterText" style="color:#AA6060"></div>
- <div class="unit">ms</div>
- </div>
- </div>
- <div class="testGroup">
- <div class="testArea">
- <div class="testName">Download</div>
- <canvas id="dlMeter" class="meter"></canvas>
- <div id="dlText" class="meterText"></div>
- <div class="unit">Mbit/s</div>
- </div>
- <div class="testArea">
- <div class="testName">Upload</div>
- <canvas id="ulMeter" class="meter"></canvas>
- <div id="ulText" class="meterText"></div>
- <div class="unit">Mbit/s</div>
- </div>
- </div>
- <div id="ipArea">
- <span id="ip"></span>
- </div>
- <div id="shareArea" style="display:none">
- <h3>Share results</h3>
- <p>Test ID: <span id="testId"></span></p>
- <input type="text" value="" id="resultsURL" readonly="readonly"
- onclick="this.select();this.focus();this.select();document.execCommand('copy');alert('Link copied')" />
- <img src="" id="resultsImg" />
- </div>
- </div>
- <a href="index.html?design=new">Try the modern design</a><br>
- <a href="https://github.com/librespeed/speedtest">Source code</a>
- </div>
- <div id="privacyPolicy" style="display:none">
- <h2>Privacy Policy</h2>
- <p>This HTML5 speed test server is configured with telemetry enabled.</p>
- <h4>What data we collect</h4>
- <p>
- At the end of the test, the following data is collected and stored:
- <ul>
- <li>Test ID</li>
- <li>Time of testing</li>
- <li>Test results (download and upload speed, ping and jitter)</li>
- <li>IP address</li>
- <li>ISP information</li>
- <li>Approximate location (inferred from IP address, not GPS)</li>
- <li>User agent and browser locale</li>
- <li>Test log (contains no personal information)</li>
- </ul>
- </p>
- <h4>How we use the data</h4>
- <p>
- Data collected through this service is used to:
- <ul>
- <li>Allow sharing of test results (sharable image for forums, etc.)</li>
- <li>To improve the service offered to you (for instance, to detect problems on our side)</li>
- </ul>
- No personal information is disclosed to third parties.
- </p>
- <h4>Your consent</h4>
- <p>
- By starting the test, you consent to the terms of this privacy policy.
- </p>
- <h4>Data removal</h4>
- <p>
- If you want to have your information deleted, you need to provide either the ID of the test or your IP
- address. This is the only way to identify your data, without this information we won't be able to comply
- with your request.<br /><br />
- Contact this email address for all deletion requests:
- <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
- </p>
- <br /><br />
- <div class="closePrivacyPolicy">
- <a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a>
- </div>
- <br />
- </div>
- </body>
- </html>
|