Skip to content

Instantly share code, notes, and snippets.

@NotArchon
Created May 7, 2023 01:03
Show Gist options
  • Save NotArchon/fbfafc03f0e86c8a4f2a40fb3725fc5d to your computer and use it in GitHub Desktop.
Save NotArchon/fbfafc03f0e86c8a4f2a40fb3725fc5d to your computer and use it in GitHub Desktop.
class HiscoresCol {
name;
rowConsumer;
constructor(name, rowConsumer) {
this.name = name;
this.rowConsumer = rowConsumer;
}
}
class Hiscores {
page;
time;
gameMode;
xpMode;
accBuild;
category;
initiated;
selectAll(query) {
return document.querySelectorAll("#hiscores " + query);
}
select(query) {
return document.querySelector("#hiscores " + query);
}
selectNamed(query, name) {
return this.select(query + "[name='" + name + "']");
}
init() {
this.page = ${queryPage};
this.initCountdowns();
this.initDropdowns();
this.initCategories();
this.initiated = true;
this.fetch();
this.select(".rank-table").classList.remove("visually-hidden");
}
initCountdowns() {
this.time = '${queryTime}';
let weekResetMs = ${weekResetMs?long?c};
let monthResetMs = ${monthResetMs?long?c};
const updateTimers = () => {
this.select(".weekly-countdown").innerText = dhms(weekResetMs);
this.select(".monthly-countdown").innerText = dhms(monthResetMs);
};
updateTimers();
setInterval(() => {
weekResetMs = Math.max(0, weekResetMs - 1000);
monthResetMs = Math.max(0, monthResetMs - 1000);
updateTimers();
}, 1000);
let activeLink;
let activeCountdown;
const selectTime = (time, link) => {
this.time = time;
if (activeCountdown) {
activeCountdown.classList.add("visually-hidden");
activeCountdown = null;
}
if (time === "weekly" || time === "monthly") {
activeCountdown = this.select("." + time + "-countdown");
activeCountdown.classList.remove("visually-hidden");
}
if (activeLink)
activeLink.classList.remove("active");
activeLink = link;
activeLink.classList.add("active");
this.fetch();
};
const allTimeLink = this.select(".all-time-link");
overwriteClick(allTimeLink, e => selectTime("all", allTimeLink));
const weeklyLink = this.select(".weekly-link");
overwriteClick(weeklyLink, e => selectTime("weekly", weeklyLink));
const monthlyLink = this.select(".monthly-link");
overwriteClick(monthlyLink, e => selectTime("monthly", monthlyLink));
if (this.time === "weekly")
weeklyLink.click();
else if (this.time === "monthly")
monthlyLink.click();
else
allTimeLink.click();
}
initDropdowns() {
/* game mode */
this.gameMode = '${queryGameMode}';
const gameModes = JSON.parse(`${gameModesJson}`);
const gameModesSelect = this.selectNamed("select", "game-mode");
gameModesSelect.innerHTML = '<option value="all" selected>Game Mode - All</option>';
gameModes.forEach(m => {
gameModesSelect.innerHTML += '<option value=' + m.key + (this.gameMode === m.key ? " selected" : "") + '>'
+ 'Game Mode - ' + m.name + '</option>';
});
gameModesSelect.addEventListener('change', e => {
this.gameMode = e.target.value;
this.fetch();
});
/* xp mode */
this.xpMode = '${queryXpMode}';
const xpModes = JSON.parse(`${xpModesJson}`);
const xpModesSelect = this.selectNamed("select", "xp-mode");
xpModesSelect.innerHTML = '<option value="all" selected>XP Mode - All</option>';
xpModes.forEach(m => {
xpModesSelect.innerHTML += '<option value=' + m.key + (this.xpMode === m.key ? " selected" : "") + '>'
+ 'XP Mode - ' + m.name + '</option>';
});
xpModesSelect.addEventListener('change', e => {
this.xpMode = e.target.value;
this.fetch();
});
/* account build */
this.accBuild = '${queryAccBuild}';
const accBuilds = JSON.parse(`${accBuildsJson}`);
const accBuildsSelect = this.selectNamed("select", "acc-build");
accBuildsSelect.innerHTML = '<option value="all" selected>Build - All</option>';
accBuilds.forEach(m => {
accBuildsSelect.innerHTML += '<option value=' + m.key + (this.accBuild === m.key ? " selected" : "") + '>'
+ 'Build - ' + m.name + '</option>';
});
accBuildsSelect.addEventListener('change', e => {
this.accBuild = e.target.value;
this.fetch();
});
}
initCategories() {
this.category = '${queryCategory}';
const categories = this.selectAll("ul.hiscores-categories li a");
let active;
let iconClass;
categories.forEach(a => {
const i = a.href.indexOf("#");
if (i === -1) return;
const category = a.href.substring(i + 1);
overwriteClick(a, e => {
if (category === "votes-all") {
this.gameMode = "all";
this.selectNamed("select", "game-mode").selectedIndex = 0;
Object.values(this.selectNamed("select", "game-mode").options).forEach(op => op.disabled = true);
this.xpMode = "all";
this.selectNamed("select", "xp-mode").selectedIndex = 0;
Object.values(this.selectNamed("select", "xp-mode").options).forEach(op => op.disabled = true);
this.accBuild = "all";
this.selectNamed("select", "acc-build").selectedIndex = 0;
Object.values(this.selectNamed("select", "acc-build").options).forEach(op => op.disabled = true);
} else {
Object.values(this.selectNamed("select", "game-mode").options).forEach(op => op.disabled = false);
Object.values(this.selectNamed("select", "xp-mode").options).forEach(op => op.disabled = false);
Object.values(this.selectNamed("select", "acc-build").options).forEach(op => op.disabled = false);
}
if (active) {
this.page = 1;
active.classList.remove("active");
this.select(".card .highscores-current-view").scrollIntoView();
}
a.classList.add("active");
active = a;
this.select(".category-name-text").innerText = a.innerText;
const catNameElement = this.select(".category-name");
if (iconClass) {
catNameElement.classList.remove(iconClass);
iconClass = null;
}
iconClass = a.classList.item(0); // meh
catNameElement.classList.add(iconClass);
this.category = category;
this.fetch();
});
if (this.category === category) {
a.click();
}
});
}
fetch() {
if (!this.initiated)
return;
const page = this.page;
const time = this.time;
const gameMode = this.gameMode;
const xpMode = this.xpMode;
const accBuild = this.accBuild;
const category = this.category;
const data = new Map();
data.set("g", gameMode);
data.set("x", xpMode);
data.set("b", accBuild);
data.set("c", category);
data.set("p", page);
data.set("t", time);
const table = this.select(".rank-table");
table.style.opacity = 0.25;
sendRequest("POST", "/detectbots/hiscores/fetch", data, (success, message) => {
if (this.page !== page || this.time !== time
|| this.gameMode !== gameMode || this.xpMode !== xpMode || this.accBuild !== accBuild
|| this.category !== category)
return; // this is an old request that took some time to load most likely
this.populate(success, message);
table.style.opacity = 1;
});
}
populate(success, message) {
const header = this.select(".hiscores-header");
header.innerHTML = '';
const tbody = this.select("#leaderboard tbody");
if (success !== true) {
tbody.innerHTML = message;
return;
}
tbody.innerHTML = '';
const rows = JSON.parse(message);
const columns = [];
let dropdownsDisabled = false;
if (this.category.startsWith("skill-")) {
if (this.time === "all") {
columns.push(new HiscoresCol("Level", r => r.primary));
columns.push(new HiscoresCol("Experience", r => r.secondary.toLocaleString()));
} else {
columns.push(new HiscoresCol("XP Gained", r => r.secondary.toLocaleString()));
}
} else if (this.category === "votes-all") {
dropdownsDisabled = true;
columns.push(new HiscoresCol("Count", r => r.primary));
} else if (this.category === "wilderness-kd") {
columns.push(new HiscoresCol("Kills", r => r.primary));
columns.push(new HiscoresCol("Deaths", r => r.secondary));
columns.push(new HiscoresCol("KDR", r => r.secondary === 0 ? (r.primary + '.00') : (r.primary / r.secondary).toFixed(2)));
} else if (this.category === "task-counts") {
columns.push(new HiscoresCol("Completed", r => r.primary));
columns.push(new HiscoresCol("Points", r => r.secondary));
} else if (this.category === "login-streak") {
columns.push(new HiscoresCol("Longest", r => r.primary));
} else if (this.category === "barrows") {
columns.push(new HiscoresCol("Chests", r => r.primary));
} else if (this.category.startsWith("kc-")) {
columns.push(new HiscoresCol("Kills", r => r.primary));
} else { // assuming only one value
columns.push(new HiscoresCol("Count", r => r.primary));
}
header.innerHTML = '<th class="rank">Rank</th><th class="username">Name</th>';
columns.forEach(c => header.innerHTML += '<th>' + c.name + '</th>');
tbody.innerHTML = '';
rows.forEach(r => {
const rank = r.rank;
let rankClass;
if (rank === 1)
rankClass = "first";
else if (rank === 2)
rankClass = "second";
else if (rank === 3)
rankClass = "third";
rankClass = rankClass ? (" " + rankClass) : "";
let s = '<tr>';
s += '<td class="rank' + rankClass + '">' + rank + '</td>';
s += '<td class="username' + rankClass + '"><a href="/hiscores/player/Jane">'
+ r.name + '</a></td>';
columns.forEach(c => s += '<td class="hiscores-value">' + c.rowConsumer(r) + '</td>');
s += '</tr>';
tbody.innerHTML += s;
});
this.updatePagination();
}
updatePagination() {
const changePage = p => {
if(p > 0 && p !== this.page) {
this.page = p;
this.fetch();
}
};
const pageStart = 1 + (Math.floor((this.page - 1) / 3) * 3);
for(let i = 0; i < 3; i++) {
const pageItem = this.select(".page-item-" + (i + 1));
const pageLink = this.select(".page-item-" + (i + 1) + " a");
const p = pageStart + i;
if(p === this.page)
pageItem.classList.add("active");
else
pageItem.classList.remove("active");
pageLink.innerText = '' + p;
overwriteClick(pageItem, e => changePage(p));
}
const first = this.select(".first-page-btn");
const prev = this.select(".prev-page-btn");
const next = this.select(".next-page-btn");
if (this.page <= 1) {
prev.classList.add("disabled");
first.classList.add("disabled");
} else {
prev.classList.remove("disabled");
first.classList.remove("disabled");
overwriteClick(first, e => changePage(1));
overwriteClick(prev, e => changePage(this.page - 1));
}
overwriteClick(next, e => changePage(this.page + 1));
}
}
try {
new Hiscores().init();
} catch(err) {
if (devMode)
alert(err);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment