135 lines
No EOL
3.8 KiB
HTML
135 lines
No EOL
3.8 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>MonkeyType Profile Data</title>
|
|
<style>
|
|
body {
|
|
background-color: #1e1e1e;
|
|
color: #f0f0f0;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
th,
|
|
td {
|
|
padding: 12px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
|
|
th {
|
|
background-color: #333;
|
|
color: #f0f0f0;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<h1>MonkeyType Profile Data</h1>
|
|
<table id="profileTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Added At</th>
|
|
<th>Compl./started</th>
|
|
<th>Time Typing</th>
|
|
<th>Best 15s</th>
|
|
<th>Best 30s</th>
|
|
<th>Best 60s</th>
|
|
<th>Best 120s</th>
|
|
<th>Best 10w</th>
|
|
<th>Best 25w</th>
|
|
<th>Best 50w</th>
|
|
<th>Best 100w</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
|
|
<script>
|
|
|
|
const users = (new URLSearchParams(window.location.search)).get('users');
|
|
|
|
// no users passed => ask for them and reload
|
|
if (users == null || users == '') {
|
|
var names = prompt('Please provide user names, delimited by \',\'');
|
|
window.location.replace(location.protocol + '//' + location.host + location.pathname + '?users=' + names);
|
|
}
|
|
|
|
const urls = users.split(",").map(user => 'https://api.monkeytype.com/users/' + user.replace(' ', '') + '/profile');
|
|
|
|
const table = document.querySelector('#profileTable tbody');
|
|
|
|
for (url of urls) {
|
|
fetch(url)
|
|
.then(response => {
|
|
if(response.status == 429) {
|
|
throw new Error('Rate limiting triggered, please wait a few minutes');
|
|
}
|
|
else if(!response.ok) {
|
|
throw new Error('Code ' + response.status);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
const { name, addedAt, typingStats, personalBests } = data.data;
|
|
const { completedTests, startedTests, timeTyping } = typingStats;
|
|
const { time, words } = personalBests;
|
|
|
|
function getMaxWpmAcc(stats) {
|
|
var max_acc = 0.0; var max_wpm = 0.0;
|
|
for ({ wpm, acc } of stats) {
|
|
if (wpm > max_wpm) {
|
|
max_wpm = wpm;
|
|
max_acc = acc;
|
|
}
|
|
}
|
|
|
|
max_wpm = Math.round(max_wpm);
|
|
// set max_wpm to fixed-length of 3
|
|
max_wpm = ' '.repeat(3 - max_wpm.toString().length) + max_wpm;
|
|
return '<strong>' + max_wpm + '</strong> ' + max_acc.toFixed(1) + '%';
|
|
}
|
|
|
|
const best15s = getMaxWpmAcc(time['15']);
|
|
const best30s = getMaxWpmAcc(time['30']);
|
|
const best60s = getMaxWpmAcc(time['60']);
|
|
const best120s = getMaxWpmAcc(time['120']);
|
|
const best10w = getMaxWpmAcc(words['10']);
|
|
const best25w = getMaxWpmAcc(words['25']);
|
|
const best50w = getMaxWpmAcc(words['50']);
|
|
const best100w = getMaxWpmAcc(words['100']);
|
|
|
|
const row = `
|
|
<tr>
|
|
<td>${name}</td>
|
|
<td>${new Date(addedAt).toLocaleDateString()}</td>
|
|
<td>${completedTests}/${startedTests}</td>
|
|
<td>${new Date(timeTyping * 1000).toISOString().substr(11, 8)}</td>
|
|
<td>${best15s}</td>
|
|
<td>${best30s}</td>
|
|
<td>${best60s}</td>
|
|
<td>${best120s}</td>
|
|
<td>${best10w}</td>
|
|
<td>${best25w}</td>
|
|
<td>${best50w}</td>
|
|
<td>${best100w}</td>
|
|
</tr>
|
|
`;
|
|
|
|
table.insertAdjacentHTML('beforeend', row);
|
|
})
|
|
.catch(error => { document.body.insertAdjacentHTML('beforeend', '<h4>' + error + '</h4>') });
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html> |