14 |
PING ms
15 |
Get milliseconds(ms) of HTTP ping, from browser, to cloud, excluding DNS latency
16 |
17 |
18 |
19 | Source code and documentation at GitHub
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/pingms.js:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // JavaScript code of HTTP ping.
4 | // The full project is at https://github.com/pingms/pingms
5 | //
6 | //
7 | "use strict"
8 |
9 | //
10 | //
11 | // Test Data
12 | const data = {
13 | Vultr: [{
14 | name: "Frankfurt",
15 | url: "http://fra-de-ping.vultr.com/"
16 | }, {
17 | name: "Amsterdam",
18 | url: "http://ams-nl-ping.vultr.com/"
19 | }, {
20 | name: "Paris",
21 | url: "http://par-fr-ping.vultr.com/"
22 | }, {
23 | name: "London",
24 | url: "http://lon-gb-ping.vultr.com/"
25 | }, {
26 | name: "Singapore",
27 | url: "http://sgp-ping.vultr.com/"
28 | }, {
29 | name: "Tokyo",
30 | url: "http://hnd-jp-ping.vultr.com/"
31 | }, {
32 | name: "New Jersey",
33 | url: "http://nj-us-ping.vultr.com/"
34 | }, {
35 | name: "Chicago",
36 | url: "http://il-us-ping.vultr.com/"
37 | }, {
38 | name: "Seattle",
39 | url: "http://wa-us-ping.vultr.com/"
40 | }, {
41 | name: "Atlanta",
42 | url: "http://ga-us-ping.vultr.com/"
43 | }, {
44 | name: "Miami",
45 | url: "http://fl-us-ping.vultr.com/"
46 | }, {
47 | name: "Dallas",
48 | url: "http://tx-us-ping.vultr.com/"
49 | }, {
50 | name: "Silicon Valley",
51 | url: "http://sjo-ca-us-ping.vultr.com/"
52 | }, {
53 | name: "Los Angeles",
54 | url: "http://lax-ca-us-ping.vultr.com/"
55 | }, {
56 | name: "Sydney",
57 | url: "http://syd-au-ping.vultr.com/"
58 | }],
59 | Linode: [{
60 | name: "Newark",
61 | url: "http://speedtest.newark.linode.com/"
62 | }, {
63 | name: "Atlanta",
64 | url: "http://speedtest.atlanta.linode.com/"
65 | }, {
66 | name: "Dallas",
67 | url: "http://speedtest.dallas.linode.com/"
68 | }, {
69 | name: "Fremont",
70 | url: "http://speedtest.fremont.linode.com/"
71 | }, {
72 | name: "Frankfurt",
73 | url: "http://speedtest.frankfurt.linode.com/"
74 | }, {
75 | name: "London",
76 | url: "http://speedtest.london.linode.com/"
77 | }, {
78 | name: "Singapore",
79 | url: "http://speedtest.singapore.linode.com/"
80 | }, {
81 | name: "Tokyo 2",
82 | url: "http://speedtest.tokyo2.linode.com/"
83 | }],
84 | DigitalOcean: [{
85 | name: "NYC1",
86 | url: "http://speedtest-nyc1.digitalocean.com/"
87 | }, {
88 | name: "NYC2",
89 | url: "http://speedtest-nyc2.digitalocean.com/"
90 | }, {
91 | name: "NYC3",
92 | url: "http://speedtest-nyc3.digitalocean.com/"
93 | }, {
94 | name: "AMS2",
95 | url: "http://speedtest-ams2.digitalocean.com/"
96 | }, {
97 | name: "AMS3",
98 | url: "http://speedtest-ams3.digitalocean.com/"
99 | }, {
100 | name: "SFO1",
101 | url: "http://speedtest-sfo1.digitalocean.com/"
102 | }, {
103 | name: "SFO2",
104 | url: "http://speedtest-sfo2.digitalocean.com/"
105 | }, {
106 | name: "SGP1",
107 | url: "http://speedtest-sgp1.digitalocean.com/"
108 | }, {
109 | name: "LON1",
110 | url: "http://speedtest-lon1.digitalocean.com/"
111 | }, {
112 | name: "FRA1",
113 | url: "http://speedtest-fra1.digitalocean.com/"
114 | }, {
115 | name: "TOR1",
116 | url: "http://speedtest-tor1.digitalocean.com/"
117 | }, {
118 | name: "BLR1",
119 | url: "http://speedtest-blr1.digitalocean.com/"
120 | }],
121 | VirMach: [{
122 | name: "Frankfurt",
123 | url: "http://ffm.lg.virmach.com/"
124 | }, {
125 | name: "Amsterdam",
126 | url: "http://ams.lg.virmach.com/"
127 | }, {
128 | name: "Buffalo",
129 | url: "http://ny.lg.virmach.com/"
130 | }, {
131 | name: "Piscataway (NYC)",
132 | url: "http://nj.lg.virmach.com/"
133 | }, {
134 | name: "Dallas",
135 | url: "http://dal.lg.virmach.com/"
136 | }, {
137 | name: "Phoenix",
138 | url: "http://phx.lg.virmach.com/"
139 | }, {
140 | name: "Los Angeles",
141 | url: "http://la.lg.virmach.com/"
142 | }, {
143 | name: "DDoS-Protected Los Angeles",
144 | url: "http://filtered-la.lg.virmach.com/"
145 | }, {
146 | name: "Chicago",
147 | url: "http://chi.lg.virmach.com/"
148 | }, {
149 | name: "Seattle",
150 | url: "http://sea.lg.virmach.com/"
151 | }, {
152 | name: "Atlanta",
153 | url: "http://atl.lg.virmach.com/"
154 | }, {
155 | name: "San Jose",
156 | url: "http://sj.lg.virmach.com/"
157 | }]
158 | };
159 |
160 | let nextTick = window.requestAnimationFrame || window.setTimeout // The delay function
161 | let tasks = [] // Pending task
162 | let img = document.createElement("img") // Image for test
163 | //
164 | //
165 | // Prepare to run tests
166 | function prepare() {
167 | const resultArea = document.querySelector("#result");
168 | resultArea.appendChild(img);
169 | function simpleElement(tag, text) {
170 | const el = document.createElement(tag);
171 | el.textContent = text;
172 | return el;
173 | }
174 | Object.entries(data).forEach(function (pair) {
175 | const key = pair[0];
176 | const array = pair[1];
177 | const subtasks = [];
178 | const section = document.createElement("section");
179 | section.appendChild(simpleElement("h3", key));
180 | section.style.setProperty("--nums", array.length);
181 | array.forEach(function (item, i) {
182 | const bodyline = document.createElement("div");
183 | let result, name;
184 | bodyline.appendChild(result = simpleElement("span", "Pending"));
185 | result.className = "result";
186 | bodyline.appendChild(name = simpleElement("span", item.name));
187 | name.className = "name";
188 | section.appendChild(bodyline);
189 | subtasks.push({
190 | line: bodyline,
191 | url: item.url,
192 | result: result
193 | });
194 | bodyline.style.setProperty("--index", i);
195 | });
196 | resultArea.appendChild(section);
197 | tasks.push(subtasks);
198 | })
199 | }
200 | //
201 | //
202 | // Load image and set callback
203 | function loadImg(src, callback) {
204 | img.src = src + Math.random();
205 | img.onerror = callback;
206 | img.onload = callback;
207 | }
208 | //
209 | //
210 | // Start Ping
211 | function handleTasks() {
212 | if (tasks.length == 0) {
213 | img.remove();
214 | return;
215 | }
216 | const currentSubTasks = tasks.shift();
217 | const subResults = [];
218 | let startTime = 0;
219 | let maxDelay = 1;
220 | nextTick(function handleSubTasks() {
221 | subResults.sort(function(a, b) {
222 | return a.delay - b.delay;
223 | });
224 | subResults.forEach(function (key, index) {
225 | key.line.style.setProperty("--index", index);
226 | });
227 | if (currentSubTasks.length == 0) {
228 | return nextTick(handleTasks);
229 | }
230 | const task = currentSubTasks.shift();
231 | task.result.textContent = "Wait DNS";
232 | // First time to load image (skip DNS querying)
233 | loadImg(task.url, function() {
234 | startTime = new Date().getTime();
235 | let finished = false;
236 | function updateDelay() {
237 | const now = new Date().getTime();
238 | const delay = now - startTime;
239 | if (delay > maxDelay) {
240 | maxDelay = delay;
241 | task.line.parentElement.style.setProperty("--max-delay", delay);
242 | }
243 | task.result.textContent = (delay) + "ms";
244 | task.line.style.setProperty("--delay", delay);
245 | return delay;
246 | }
247 | // Update delay realtime
248 | nextTick(function rev() {
249 | if (!finished) {
250 | updateDelay();
251 | nextTick(rev);
252 | }
253 | });
254 | // Second time to load image (measure latency)
255 | loadImg(task.url, function() {
256 | finished = true;
257 | subResults.push({
258 | line: task.line,
259 | delay: updateDelay()
260 | });
261 | nextTick(handleSubTasks);
262 | });
263 | });
264 | });
265 | }
266 | //
267 | //
268 | // This function is called by "body onload".
269 | function main() {
270 | prepare();
271 | nextTick(handleTasks);
272 | }
273 | //
274 | //
275 | // END OF FILE
276 | // The full project is at https://github.com/pingms/pingms
277 | //
278 | //
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body {
6 | background: #4CAF50;
7 | color: #ffffff;
8 | font-size: 14px;
9 | font-family: Tahoma, Helvetica, Arial, sans-serif;
10 | margin: 8px;
11 | --item-height: 16px;
12 | --section-header-height: 30px;
13 | }
14 |
15 | p {
16 | display: flex;
17 | }
18 |
19 | a:link, a:visited, a:hover, a:active {
20 | color: LightBlue;
21 | }
22 |
23 | .content {
24 | width: 600px;
25 | max-width: calc(100vw - 16px);
26 | margin: 0 auto;
27 | }
28 |
29 | @media (min-width: 800px) {
30 | #result {
31 | column-count: 2;
32 | column-gap: 20px;
33 | column-rule-style: solid;
34 | column-rule-width: 1px;
35 | column-rule-color: white;
36 | }
37 | }
38 |
39 | section {
40 | overflow: hidden;
41 | break-inside: avoid-column;
42 | -webkit-column-break-inside: avoid;
43 | top: 0;
44 | position: relative;
45 | --max-delay: 1;
46 | transition: all ease 1s;
47 | height: calc(var(--nums) * (var(--item-height) + 2px) + var(--section-header-height));
48 | }
49 |
50 | h3 {
51 | font-size: 20px;
52 | font-weight: 900;
53 | height: var(--section-header-height);
54 | margin: 0;
55 | padding: 0;
56 | }
57 |
58 | section>div {
59 | width: 100%;
60 | position: absolute;
61 | height: 16px;
62 | margin: 1px 0;
63 | transition: all ease .5s;
64 | display: flex;
65 | --delay: 0;
66 | transform: translateY(calc(var(--index) * (var(--item-height) + 2px)));
67 | z-index: calc(var(--nums) - var(--index) + 1);
68 | }
69 |
70 | section>div::after {
71 | content: "";
72 | position: absolute;
73 | left: 70px;
74 | right: 0;
75 | top: 0;
76 | bottom: 0;
77 | --base-color: rgb(calc(var(--delay) / var(--max-delay) * 200), calc(255 - var(--delay) / var(--max-delay) * 128), 0);
78 | --percent: calc(var(--delay) / var(--max-delay) * 100%);
79 | background: linear-gradient(to right, var(--base-color) var(--percent), transparent var(--percent));
80 | z-index: -1;
81 | transition: all ease 1s;
82 | }
83 |
84 | .result {
85 | display: inline-block;
86 | width: 70px;
87 | font-family: 'Lucida Console', Monaco, monospace;
88 | font-size: 12px;
89 | text-align: right;
90 | padding-right: .5em;
91 | }
92 |
93 | .name {
94 | flex: 1;
95 | }
96 |
97 | img {
98 | position: absolute;
99 | opacity: 0;
100 | }
--------------------------------------------------------------------------------