Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion frontend/javascript/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,34 @@ function populateDropdown(servers) {
});
}

// Sort servers by country, then by city within the same country.
// Name formats: "City, Country", "City, Country (qualifier)", "City, Country, Provider", "Country"
const parseServerName = (name) => {
const parts = (name || "").split(",").map((s) => s.trim());
let country, city;
if (parts.length >= 3) {
// "City, Country, Provider" — use second part as country
country = parts[1];
city = parts[0];
} else if (parts.length === 2) {
country = parts[1];
city = parts[0];
} else {
country = parts[0];
city = "";
}
// Strip parenthetical qualifiers for sorting: "Germany (1) (Hetzner)" → "Germany"
country = country.replace(/\s*\([^)]*\)\s*/g, "").trim();
return { country, city };
};
const sorted = [...servers].sort((a, b) => {
const pa = parseServerName(a.name);
const pb = parseServerName(b.name);
return pa.country.localeCompare(pb.country) || pa.city.localeCompare(pb.city);
});

// Populate the list to choose from
servers.forEach((server) => {
sorted.forEach((server) => {
const item = document.createElement("li");
const link = document.createElement("a");
link.href = "#";
Expand Down
38 changes: 33 additions & 5 deletions index-classic.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,41 @@
s.selectServer(function (server) {
if (server != null) { //at least 1 server is available
I("loading").className = "hidden"; //hide loading message
//sort servers by country, then by city
//name formats: "City, Country", "City, Country (qualifier)", "City, Country, Provider", "Country"
function parseServerName(name) {
var parts = (name || "").split(",");
for (var p = 0; p < parts.length; p++) parts[p] = parts[p].trim();
var country, city;
if (parts.length >= 3) {
country = parts[1];
city = parts[0];
} else if (parts.length === 2) {
country = parts[1];
city = parts[0];
} else {
country = parts[0];
city = "";
}
country = country.replace(/\s*\([^)]*\)\s*/g, "").trim();
return { country: country, city: city };
}
var indexed = [];
for (var j = 0; j < SPEEDTEST_SERVERS.length; j++) {
indexed.push({ idx: j, server: SPEEDTEST_SERVERS[j] });
}
indexed.sort(function (a, b) {
var pa = parseServerName(a.server.name);
var pb = parseServerName(b.server.name);
return pa.country.localeCompare(pb.country) || pa.city.localeCompare(pb.city);
});
//populate server list for manual selection
for (var i = 0; i < SPEEDTEST_SERVERS.length; i++) {
if (SPEEDTEST_SERVERS[i].pingT == -1) continue;
for (var i = 0; i < indexed.length; i++) {
if (indexed[i].server.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;
option.value = indexed[i].idx;
option.textContent = indexed[i].server.name;
if (indexed[i].server === server) option.selected = true;
I("server").appendChild(option);
}
//show test UI
Expand Down