example-multipleServers-full.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
  5. <meta charset="UTF-8" />
  6. <script type="text/javascript" src="speedtest.js"></script>
  7. <script type="text/javascript">
  8. function I(i){return document.getElementById(i);}
  9. //LIST OF TEST SERVERS. See documentation for details if needed
  10. var SPEEDTEST_SERVERS=[
  11. { //this is my demo server, remove it
  12. name:"Speedtest Demo Server 1", //user friendly name for the server
  13. server:"//mpotdemo.fdossena.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
  14. dlURL:"garbage.php", //path to download test on this server (garbage.php or replacement)
  15. ulURL:"empty.php", //path to upload test on this server (empty.php or replacement)
  16. pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
  17. getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
  18. },
  19. { //this is my demo server, remove it
  20. name:"Speedtest Demo Server 2",
  21. server:"//mpotdemo2.fdossena.com/",
  22. dlURL:"garbage.php",
  23. ulURL:"empty.php",
  24. pingURL:"empty.php",
  25. getIpURL:"getIP.php"
  26. }
  27. //add other servers here, comma separated
  28. ];
  29. //INITIALIZE SPEEDTEST
  30. var s=new Speedtest(); //create speedtest object
  31. s.setParameter("telemetry_level","basic"); //enable telemetry
  32. s.addTestPoints(SPEEDTEST_SERVERS); //add list of servers
  33. //SERVER AUTO SELECTION
  34. function initServers(){
  35. s.selectServer(function(server){
  36. if(server!=null){ //at least 1 server is available
  37. I("loading").className="hidden"; //hide loading message
  38. //populate server list for manual selection
  39. for(var i=0;i<SPEEDTEST_SERVERS.length;i++){
  40. if(SPEEDTEST_SERVERS[i].pingT==-1) continue;
  41. var option=document.createElement("option");
  42. option.value=i;
  43. option.textContent=SPEEDTEST_SERVERS[i].name;
  44. if(SPEEDTEST_SERVERS[i]===server) option.selected=true;
  45. I("server").appendChild(option);
  46. }
  47. //show test UI
  48. I("testWrapper").className="visible";
  49. initUI();
  50. }else{ //no servers are available, the test cannot proceed
  51. I("message").innerHTML="No servers available";
  52. }
  53. });
  54. }
  55. var meterBk=/Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent)?"#EAEAEA":"#80808040";
  56. var dlColor="#6060AA",
  57. ulColor="#309030",
  58. pingColor="#AA6060",
  59. jitColor="#AA6060";
  60. var progColor=meterBk;
  61. //CODE FOR GAUGES
  62. function drawMeter(c,amount,bk,fg,progress,prog){
  63. var ctx=c.getContext("2d");
  64. var dp=window.devicePixelRatio||1;
  65. var cw=c.clientWidth*dp, ch=c.clientHeight*dp;
  66. var sizScale=ch*0.0055;
  67. if(c.width==cw&&c.height==ch){
  68. ctx.clearRect(0,0,cw,ch);
  69. }else{
  70. c.width=cw;
  71. c.height=ch;
  72. }
  73. ctx.beginPath();
  74. ctx.strokeStyle=bk;
  75. ctx.lineWidth=12*sizScale;
  76. ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,Math.PI*0.1);
  77. ctx.stroke();
  78. ctx.beginPath();
  79. ctx.strokeStyle=fg;
  80. ctx.lineWidth=12*sizScale;
  81. 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);
  82. ctx.stroke();
  83. if(typeof progress !== "undefined"){
  84. ctx.fillStyle=prog;
  85. ctx.fillRect(c.width*0.3,c.height-16*sizScale,c.width*0.4*progress,4*sizScale);
  86. }
  87. }
  88. function mbpsToAmount(s){
  89. return 1-(1/(Math.pow(1.3,Math.sqrt(s))));
  90. }
  91. function msToAmount(s){
  92. return 1-(1/(Math.pow(1.08,Math.sqrt(s))));
  93. }
  94. //UI CODE
  95. var uiData=null;
  96. function startStop(){
  97. if(s.getState()==3){
  98. //speedtest is running, abort
  99. s.abort();
  100. data=null;
  101. I("startStopBtn").className="";
  102. I("server").disabled=false;
  103. initUI();
  104. }else{
  105. //test is not running, begin
  106. I("startStopBtn").className="running";
  107. I("shareArea").style.display="none";
  108. I("server").disabled=true;
  109. s.onupdate=function(data){
  110. uiData=data;
  111. };
  112. s.onend=function(aborted){
  113. I("startStopBtn").className="";
  114. I("server").disabled=false;
  115. updateUI(true);
  116. if(!aborted){
  117. //if testId is present, show sharing panel, otherwise do nothing
  118. try{
  119. var testId=uiData.testId;
  120. if(testId!=null){
  121. var shareURL=window.location.href.substring(0,window.location.href.lastIndexOf("/"))+"/results/?id="+testId;
  122. I("resultsImg").src=shareURL;
  123. I("resultsURL").value=shareURL;
  124. I("testId").innerHTML=testId;
  125. I("shareArea").style.display="";
  126. }
  127. }catch(e){}
  128. }
  129. };
  130. s.start();
  131. }
  132. }
  133. //this function reads the data sent back by the test and updates the UI
  134. function updateUI(forced){
  135. if(!forced&&s.getState()!=3) return;
  136. if(uiData==null) return;
  137. var status=uiData.testState;
  138. I("ip").textContent=uiData.clientIp;
  139. I("dlText").textContent=(status==1&&uiData.dlStatus==0)?"...":uiData.dlStatus;
  140. drawMeter(I("dlMeter"),mbpsToAmount(Number(uiData.dlStatus*(status==1?oscillate():1))),meterBk,dlColor,Number(uiData.dlProgress),progColor);
  141. I("ulText").textContent=(status==3&&uiData.ulStatus==0)?"...":uiData.ulStatus;
  142. drawMeter(I("ulMeter"),mbpsToAmount(Number(uiData.ulStatus*(status==3?oscillate():1))),meterBk,ulColor,Number(uiData.ulProgress),progColor);
  143. I("pingText").textContent=uiData.pingStatus;
  144. drawMeter(I("pingMeter"),msToAmount(Number(uiData.pingStatus*(status==2?oscillate():1))),meterBk,pingColor,Number(uiData.pingProgress),progColor);
  145. I("jitText").textContent=uiData.jitterStatus;
  146. drawMeter(I("jitMeter"),msToAmount(Number(uiData.jitterStatus*(status==2?oscillate():1))),meterBk,jitColor,Number(uiData.pingProgress),progColor);
  147. }
  148. function oscillate(){
  149. return 1+0.02*Math.sin(Date.now()/100);
  150. }
  151. //update the UI every frame
  152. window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||(function(callback,element){setTimeout(callback,1000/60);});
  153. function frame(){
  154. requestAnimationFrame(frame);
  155. updateUI();
  156. }
  157. frame(); //start frame loop
  158. //function to (re)initialize UI
  159. function initUI(){
  160. drawMeter(I("dlMeter"),0,meterBk,dlColor,0);
  161. drawMeter(I("ulMeter"),0,meterBk,ulColor,0);
  162. drawMeter(I("pingMeter"),0,meterBk,pingColor,0);
  163. drawMeter(I("jitMeter"),0,meterBk,jitColor,0);
  164. I("dlText").textContent="";
  165. I("ulText").textContent="";
  166. I("pingText").textContent="";
  167. I("jitText").textContent="";
  168. I("ip").textContent="";
  169. }
  170. </script>
  171. <style type="text/css">
  172. html,body{
  173. border:none; padding:0; margin:0;
  174. background:#FFFFFF;
  175. color:#202020;
  176. }
  177. body{
  178. text-align:center;
  179. font-family:"Roboto",sans-serif;
  180. }
  181. h1{
  182. color:#404040;
  183. }
  184. #loading{
  185. background-color:#FFFFFF;
  186. color:#404040;
  187. text-align:center;
  188. }
  189. span.loadCircle{
  190. display:inline-block;
  191. width:2em;
  192. height:2em;
  193. vertical-align:middle;
  194. background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
  195. background-size:2em 2em;
  196. margin-right:0.5em;
  197. animation: spin 0.6s linear infinite;
  198. }
  199. @keyframes spin{
  200. 0%{transform:rotate(0deg);}
  201. 100%{transform:rotate(359deg);}
  202. }
  203. #startStopBtn{
  204. display:inline-block;
  205. margin:0 auto;
  206. color:#6060AA;
  207. background-color:rgba(0,0,0,0);
  208. border:0.15em solid #6060FF;
  209. border-radius:0.3em;
  210. transition:all 0.3s;
  211. box-sizing:border-box;
  212. width:8em; height:3em;
  213. line-height:2.7em;
  214. cursor:pointer;
  215. box-shadow: 0 0 0 rgba(0,0,0,0.1), inset 0 0 0 rgba(0,0,0,0.1);
  216. }
  217. #startStopBtn:hover{
  218. box-shadow: 0 0 2em rgba(0,0,0,0.1), inset 0 0 1em rgba(0,0,0,0.1);
  219. }
  220. #startStopBtn.running{
  221. background-color:#FF3030;
  222. border-color:#FF6060;
  223. color:#FFFFFF;
  224. }
  225. #startStopBtn:before{
  226. content:"Start";
  227. }
  228. #startStopBtn.running:before{
  229. content:"Abort";
  230. }
  231. #serverArea{
  232. margin-top:1em;
  233. }
  234. #server{
  235. font-size:1em;
  236. padding:0.2em;
  237. }
  238. #test{
  239. margin-top:2em;
  240. margin-bottom:12em;
  241. }
  242. div.testArea{
  243. display:inline-block;
  244. width:16em;
  245. height:12.5em;
  246. position:relative;
  247. box-sizing:border-box;
  248. }
  249. div.testName{
  250. position:absolute;
  251. top:0.1em; left:0;
  252. width:100%;
  253. font-size:1.4em;
  254. z-index:9;
  255. }
  256. div.meterText{
  257. position:absolute;
  258. bottom:1.55em; left:0;
  259. width:100%;
  260. font-size:2.5em;
  261. z-index:9;
  262. }
  263. div.meterText:empty:before{
  264. content:"0.00";
  265. }
  266. div.unit{
  267. position:absolute;
  268. bottom:2em; left:0;
  269. width:100%;
  270. z-index:9;
  271. }
  272. div.testArea canvas{
  273. position:absolute;
  274. top:0; left:0; width:100%; height:100%;
  275. z-index:1;
  276. }
  277. div.testGroup{
  278. display:inline-block;
  279. }
  280. #shareArea{
  281. width:95%;
  282. max-width:40em;
  283. margin:0 auto;
  284. margin-top:2em;
  285. }
  286. #shareArea > *{
  287. display:block;
  288. width:100%;
  289. height:auto;
  290. margin: 0.25em 0;
  291. }
  292. div.visible{
  293. animation: fadeIn 0.4s;
  294. display:block;
  295. }
  296. div.hidden{
  297. animation: fadeOut 0.4s;
  298. display:none;
  299. }
  300. #privacyPolicy{
  301. position:fixed;
  302. top:2em;
  303. bottom:2em;
  304. left:2em;
  305. right:2em;
  306. overflow-y:auto;
  307. width:auto;
  308. height:auto;
  309. box-shadow:0 0 3em 1em #000000;
  310. z-index:999999;
  311. text-align:left;
  312. background-color:#FFFFFF;
  313. padding:1em;
  314. }
  315. a.privacy{
  316. text-align:center;
  317. font-size:0.8em;
  318. color:#808080;
  319. display:block;
  320. }
  321. @keyframes fadeIn{
  322. 0%{
  323. opacity:0;
  324. }
  325. 100%{
  326. opacity:1;
  327. }
  328. }
  329. @keyframes fadeOut{
  330. 0%{
  331. display:block;
  332. opacity:1;
  333. }
  334. 100%{
  335. display:block;
  336. opacity:0;
  337. }
  338. }
  339. @media all and (max-width:65em){
  340. body{
  341. font-size:1.5vw;
  342. }
  343. }
  344. @media all and (max-width:40em){
  345. body{
  346. font-size:0.8em;
  347. }
  348. div.testGroup{
  349. display:block;
  350. margin: 0 auto;
  351. }
  352. }
  353. </style>
  354. <title>HTML5 Speedtest</title>
  355. </head>
  356. <body onload="initServers()">
  357. <h1>HTML5 Speedtest</h1>
  358. <div id="loading" class="visible">
  359. <p id="message"><span class="loadCircle"></span>Selecting a server...</p>
  360. </div>
  361. <div id="testWrapper" class="hidden">
  362. <div id="startStopBtn" onclick="startStop()"></div>
  363. <a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
  364. <div id="serverArea">
  365. Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
  366. </div>
  367. <div id="test">
  368. <div class="testGroup">
  369. <div class="testArea">
  370. <div class="testName">Download</div>
  371. <canvas id="dlMeter" class="meter"></canvas>
  372. <div id="dlText" class="meterText"></div>
  373. <div class="unit">Mbps</div>
  374. </div>
  375. <div class="testArea">
  376. <div class="testName">Upload</div>
  377. <canvas id="ulMeter" class="meter"></canvas>
  378. <div id="ulText" class="meterText"></div>
  379. <div class="unit">Mbps</div>
  380. </div>
  381. </div>
  382. <div class="testGroup">
  383. <div class="testArea">
  384. <div class="testName">Ping</div>
  385. <canvas id="pingMeter" class="meter"></canvas>
  386. <div id="pingText" class="meterText"></div>
  387. <div class="unit">ms</div>
  388. </div>
  389. <div class="testArea">
  390. <div class="testName">Jitter</div>
  391. <canvas id="jitMeter" class="meter"></canvas>
  392. <div id="jitText" class="meterText"></div>
  393. <div class="unit">ms</div>
  394. </div>
  395. </div>
  396. <div id="ipArea">
  397. IP Address: <span id="ip"></span>
  398. </div>
  399. <div id="shareArea" style="display:none">
  400. <h3>Share results</h3>
  401. <p>Test ID: <span id="testId"></span></p>
  402. <input type="text" value="" id="resultsURL" readonly="readonly" onclick="this.select();this.focus();this.select();document.execCommand('copy');alert('Link copied')"/>
  403. <img src="" id="resultsImg" />
  404. </div>
  405. </div>
  406. <a href="https://github.com/adolfintel/speedtest">Source code</a>
  407. </div>
  408. <div id="privacyPolicy" style="display:none">
  409. <h2>Privacy Policy</h2>
  410. <p>This HTML5 Speedtest server is configured with telemetry enabled.</p>
  411. <h4>What data we collect</h4>
  412. <p>
  413. At the end of the test, the following data is collected and stored:
  414. <ul>
  415. <li>Test ID</li>
  416. <li>Time of testing</li>
  417. <li>Test results (download and upload speed, ping and jitter)</li>
  418. <li>IP address</li>
  419. <li>ISP information</li>
  420. <li>Approximate location (inferred from IP address, not GPS)</li>
  421. <li>User agent and browser locale</li>
  422. <li>Test log (contains no personal information)</li>
  423. </ul>
  424. </p>
  425. <h4>How we use the data</h4>
  426. <p>
  427. Data collected through this service is used to:
  428. <ul>
  429. <li>Allow sharing of test results (sharable image for forums, etc.)</li>
  430. <li>To improve the service offered to you (for instance, to detect problems on our side)</li>
  431. </ul>
  432. No personal information is disclosed to third parties.
  433. </p>
  434. <h4>Data removal</h4>
  435. <p>
  436. 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/>
  437. Contact this email address for all deletion requests: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
  438. </p>
  439. <br/><br/>
  440. <a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a><br/>
  441. </div>
  442. </body>
  443. </html>