HoldBoost Tweaks

fix holdboost styles and add feature tweaks

目前為 2024-03-09 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         HoldBoost Tweaks
// @namespace    http://tampermonkey.net/
// @version      0.2.9
// @description  fix holdboost styles and add feature tweaks
// @author       Californ1a
// @match        http://holdboost.com/*
// @match        https://holdboost.com/*
// @match        http://*.holdboost.com/*
// @match        https://*.holdboost.com/*
// ==/UserScript==

(async function() {
	const css = `
/* Levels */
#pills-tab {
  flex-wrap: nowrap;
  align-items: center;
}
.level-medal-times {
  flex: 1 1 5vw;
  max-width: 100%;
  min-width: 150px;
  margin: 0 max(0.5vw,18px) 1vw max(0.5vw,16px) !important;
  padding: 0.25vw !important;
}
#pills-sightings > div,
#pills-improvements > div,
#pills-info > div {
  max-height: calc(100vh - 27.7rem);
  overflow-y: auto;
}
.sticky-headers thead th {
  z-index: 1;
}
table tbody tr td.entry-img {
  vertical-align: middle;
}
body {
  background-position: center;
}

th.text-left {
  text-align: center !important;
}

#pills-tab .nav-item {
  flex: 1;
  align-self: stretch;
}
.nav-pills .nav-link {
  background-color: #00000033;
  margin: 0 0.2vw 0 0.2vw;
  height: 100%;
}
.nav-pills .nav-link:not(.active):hover {
  background-color: #00000044;
}
.nav-pills:first-child .nav-link {
  margin-left: 0;
}
.nav-pills:last-child .nav-link {
  margin-right: 0;
}
.nav-pills .nav-item:first-child a:not(#pills-recent-activity-tab) {
  /*line-height: 3.06rem;*/
  padding: 1.25rem;
}
thead th  {
  position: sticky;
  top: -2px;
  background-color: #212529;
}
#levelsPlaceholder + #levels > .text-left {
  position: sticky;
  top: -20px;
  z-index: 10;
  background-color: #222222;
}

@keyframes custom-fade {
  0% {color: yellow;}
  50% {color: yellow;}
  100% {color: white;}
}
.fade-in-custom {
  animation: custom-fade 1s ease-out;
}

h5 a {
  color: white;
}

h5 a:hover {
  color: #eee;
}

#customAvatar {
  max-height: 40px;
  margin-right: 5px;
}

#customRank {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.col-4 table:nth-child(5) {
  table-layout: fixed;
}

.col-4 table:nth-child(5) td.no-wrap:not(.entry-img) {
  padding-left: 4px;
  padding-right: 4px;
}

.col-4 table:nth-child(5) td:nth-child(2) div {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
`.split("\n").filter((e) => e !== "").join("\n");
	if (typeof GM_addStyle != "undefined") {
		GM_addStyle(css);
	} else if (typeof PRO_addStyle != "undefined") {
		PRO_addStyle(css);
	} else if (typeof addStyle != "undefined") {
		addStyle(css);
	} else {
		var node = document.createElement("style");
		node.type = "text/css";
		node.appendChild(document.createTextNode(css));
		var heads = document.getElementsByTagName("head");
		if (heads.length > 0) {
			heads[0].appendChild(node);
		} else {
			// no head yet, stick it whereever
			document.documentElement.appendChild(node);
		}
	}

	/* Add delimiter to numbers on homepage */

	const homepageRows = [
		document.querySelector(".container .row div form+div"),
		document.querySelector(".container .row div form+div+div"),
		document.querySelector(".container .row div form+div+div+div"),
		document.querySelector(".container .row div form+div+div+div+div")
	];
	if (homepageRows[3]) {
		const nums = homepageRows.map(r => r.children[0]);
		if (nums[3]) {
			nums.forEach(n => {
				n.innerText = parseInt(n.innerText, 10).toLocaleString();
			});
		}
	}


	/* Auto-refresh activity pages */

	let pause = false;
	let interval;

	function createRow(activity, pageType, fade = true) {
		let row = "";
		if (pageType === "player") {
			if (activity.sighting != null) {
				row = `
								<td class="text-left entry-img no-wrap"><div class="${(fade)?"fade-in":""}">
									<img class="card-img-top" src="${activity.sighting.leaderboard.imageURL}">
								</div></td>
								<td class="text-left"><div class="${(fade)?"fade-in":""}">
									<a class="link" href="/Leaderboard/Level?leaderboardID=${activity.sighting.leaderboard.id}">
										${activity.sighting.leaderboard.levelName}
									</a>
								</div></td>
								<td><div class="${(fade)?"fade-in":""}">${/*TODO*/''}</div></td>
								<td><div class="${(fade)?"fade-in":""}">${activity.sighting.millisecondsString} <span class="text-success">New!</span></div></td>
								<td><div class="${(fade)?"fade-in":"fade-in-custom"}">${activity.timeAgoString}</div></td>
							`;
			} else {
				row = `
								<td class="text-left entry-img no-wrap"><div class="${(fade)?"fade-in":""}">
									<img class="card-img-top" src="${activity.improvement.leaderboard.imageURL}">
								</div></td>
								<td class="text-left"><div class="${(fade)?"fade-in":""}">
									<a class="link" href="/Leaderboard/Level?leaderboardID=${activity.improvement.leaderboard.id}">
										${activity.improvement.leaderboard.levelName}
									</a>
								</div></td>
								<td><div class="${(fade)?"fade-in":""}">
									${activity.improvement.newRank}
									( <i class="fas fa-arrow-up fa-sm text-success"></i> ${activity.improvement.oldRank - activity.improvement.newRank} )</div></td>
								<td><div class="${(fade)?"fade-in":""}">
									${activity.improvement.millisecondsString}
									( <i class="fas fa-arrow-up fa-sm text-success"></i> ${activity.improvement.timeImprovement} )</div></td>
								<td><div class="${(fade)?"fade-in":"fade-in-custom"}">${activity.timeAgoString}</div></td>
							`;
			}
		} else {
			if (activity.sighting != null) {
				row = `
								<td class="text-left entry-img no-wrap"><div class="${(fade)?"fade-in":""}">
									<img src="${activity.sighting.player.steamAvatar}" />
									<a class="link" href="/Player?steamID=${activity.sighting.player.steamID}">
										${activity.sighting.player.name}
									</a>
								</div></td>
								<td class="text-left entry-img no-wrap"><div class="${(fade)?"fade-in":""}">
									<img src="${activity.sighting.leaderboard.imageURL}" />
									<a class="link" href="/Leaderboard/Level?leaderboardID=${activity.sighting.leaderboard.id}">
										${activity.sighting.leaderboard.levelName}
									</a>
								</div></td>
								<td class="no-wrap"><div class="${(fade)?"fade-in":""}">${/*TODO*/''}</div></td>
								<td class="no-wrap"><div class="${(fade)?"fade-in":""}">${activity.sighting.millisecondsString} <span class="text-success">New!</span></div></td>
								<td class="no-wrap"><div class="${(fade)?"fade-in":"fade-in-custom"}">${activity.timeAgoString}</div></td>
						`;
			} else {
				row = `
								<td class="text-left entry-img no-wrap"><div class="${(fade)?"fade-in":""}">
									<img src="${activity.improvement.player.steamAvatar}" />
									<a class="link" href="/Player?steamID=${activity.improvement.player.steamID}">
										${activity.improvement.player.name}
									</a>
								</div></td>
								<td class="text-left entry-img no-wrap"><div class="${(fade)?"fade-in":""}">
									<img src="${activity.improvement.leaderboard.imageURL}" />
									<a class="link" href="/Leaderboard/Level?leaderboardID=${activity.improvement.leaderboard.id}">
										${activity.improvement.leaderboard.levelName}
									</a>
								</div></td>
								<td class="no-wrap"><div class="${(fade)?"fade-in":""}">
									${activity.improvement.newRank}
									( <i class="fas fa-arrow-up fa-sm text-success"></i> ${activity.improvement.oldRank - activity.improvement.newRank} )</div></td>
								<td class="no-wrap"><div class="${(fade)?"fade-in":""}">
									${activity.improvement.millisecondsString}
									( <i class="fas fa-arrow-up fa-sm text-success"></i> ${activity.improvement.timeImprovement} )</div></td>
								<td class="no-wrap"><div class="${(fade)?"fade-in":"fade-in-custom"}">${activity.timeAgoString}</div></td>
						`;
			}
		}
		return row;
	}

	function compare(a, b, x, y) {
		if (x === y) return a[x].isEqualNode(b[x]);
		else return a[x].isEqualNode(b[x]) && compare(a, b, x + 1, y);
	}

	async function getNewActivity() {
		if (document.visibilityState !== 'visible') return;
		const h = document.location.href;
		const pageType = (h.match(/Player\?steamID=\d+$/)) ? "player" : (h.match(/\/Home\/(WR|Top100)Activity$/)) ? "activity" : null;
		if (!pageType) return;
		if (pause || (pageType === "player" && !($('.nav').children()[0].children[0].isSameNode($('.nav-link.active').get(0))))) {
			console.log("[CAL] paused");
			return;
		}
		try {
			let json;
			if (pageType === "player") {
				const player = document.location.search.split("=")[1];
				const res = await fetch(`/Player/GetRecentActivity?steamID=${player}`);
				json = await res.json();
			} else {
				let res;
				if (h.match(/GlobalActivity$/)) {
					res = await fetch("/Home/GetGlobalRecentActivity");
				} else if (h.match(/WRActivity$/)) {
					res = await fetch("/Home/GetWRActivity");
				} else if (h.match(/Top100Activity$/)) {
					res = await fetch("/Home/GetTop100RecentActivity");
				}
				json = await res.json();
			}
			const recentActivity = $('#recentActivity');
			console.log("[CAL] got data");
			const mostRecentVisible = recentActivity.children()[0];
			const rowsToInsert = [];
			let found = false;
			let i = 0;
			for (const activity of json) {
				const row = $("<tr>" + createRow(activity, pageType) + "</tr>");
				const c = document.querySelector("#recentActivity").children[0].children;
				const d = row.children();
				//const recentStr = c[0].innerHTML+c[1].innerHTML+c[2].innerHTML+c[3].innerHTML;
				//const rowStr = d[0].innerHTML+d[1].innerHTML+d[2].innerHTML+d[3].innerHTML;
				//console.log("recentStr", recentStr);
				//console.log("rowStr", rowStr);
				//console.log("compare", rowStr !== recentStr);
				//console.log("c", c);
				//console.log("d", d);
				if (!found && !compare(c, d, 0, 3)) {
					console.log("[CAL] insert row");
					rowsToInsert.push(row);
				} else {
					found = true;
					const rc = row.children()[4];
					const ra = document.querySelector("#recentActivity").children[i];
					//console.log("i", i);
					//console.log("document.querySelector(\"#recentActivity\").children", document.querySelector("#recentActivity").children);
					//console.log("ra", ra);

					if (rc.innerText !== ra.children[4].innerText) {
						$(ra.children[4]).replaceWith($("<tr>" + createRow(activity, pageType, false) + "</tr>").children()[4]);
					}
					i++;
				}
			}
			if (!rowsToInsert[0]) {
				return;
			}
			if (pageType === "player" && getGlobalStats) {
				console.log("[CAL] getting new global stats");
				getGlobalStats();
				displayRivals();
			}
			rowsToInsert.reverse();
			for (const row of rowsToInsert) {
				recentActivity.prepend(row);
				const maxCount = (pageType === "activity") ? 100 : 30;
				if (recentActivity.children().length > maxCount) {
					recentActivity.get(0).deleteRow(recentActivity.children().length - 1);
				}
			}
		} catch (e) {
			console.error(e);
			toastr.error('[HBT] Failed to load recent activity for this player');
			clearInterval(interval);
		}
	}

	if (document.location.href.match(/Player\?steamID=\d+$/) || document.location.href.match(/\/Home\/(WR|Top100)Activity$/)) {
		interval = setInterval(getNewActivity, 1000 * 10);
	}
	document.addEventListener("visibilitychange", () => {
		if (document.visibilityState === 'visible') {
			pause = false;
			getNewActivity();
			interval = setInterval(getNewActivity, 1000 * 10);
		} else {
			pause = true;
			clearInterval(interval);
		}
	});
	const navLinks = document.querySelectorAll(".nav-pills .nav-link");
	navLinks.forEach(l => {
		l.addEventListener("click", (e) => {
			const customFades = document.querySelectorAll(".fade-in-custom");
			customFades.forEach(f => {
				f.classList.remove("fade-in-custom");
				f.style.animation = "none";
				setTimeout(() => {
					const parent = f.parentElement;
					$(f).detach().addClass("fade-in").appendTo(parent);
					f.style.animation = "";
				}, 200);
			});
		});
	});

	/* Find rivals for player pages */

	function getRivals(steamId, data) {
		if (!data.success) {
			return 0;
		}
		// Find the index of the player with the given steam ID
		let playerIndex = -1;
		for (let i = 0; i < data.success.rows.length; i++) {
			if (data.success.rows[i][0] === steamId) {
				playerIndex = i;
				break;
			}
		}

		// Return null if the player with the given steam ID is not found
		if (playerIndex === -1) {
			return null;
		}

		// Initialize the list of rivals
		let rivals = [data.success.rows[playerIndex]]; // Add the player to the list of rivals

		// Add the 5 players ranked above the player to the list of rivals
		for (let i = playerIndex - 1; i >= Math.max(0, playerIndex - 5); i--) {
			if (i < 0) {
				break;
			}
			rivals.push(data.success.rows[i]);
		}

		// Add the 5 players ranked below the player to the list of rivals
		for (let i = playerIndex + 1; i <= Math.min(data.success.rows.length - 1, playerIndex + 5); i++) {
			if (i > data.success.rows.length - 1) {
				break;
			}
			rivals.push(data.success.rows[i]);
		}

		rivals.forEach((player, index) => {
			player.index = index;
		});

		// Sort the list of rivals by global rank
		rivals.sort((a, b) => {
			if (parseInt(a[1]) === parseInt(b[1])) { // If the ranks are the same
				return a.index - b.index; // Compare the indices of the players in the original list
			}
			return parseInt(a[1]) - parseInt(b[1]); // Compare the global ranks (column 1) of the players
		});

		return rivals;
	}

	async function displayRivals() {
		if (document.location.href.match(/Player\?steamID=\d+$/)) {
			try {
				const limit = 10000;
				const res = await fetch(`https://distance-db-sql-api.seekr.pw/?query=SELECT%0A%20%20steam_id,%0A%20%20RANK()%20OVER%20(%0A%20%20%20%20ORDER%20BY%20SUM(noodle_points)%20DESC%0A%20%20)%20as%20global_rank,%0A%20%20name,%0A%20%20SUM(noodle_points)%20as%20noodle_points,%0A%20%20ROUND((SUM(noodle_points)%20/%201200)::numeric,%202)%20as%20player_rating%0AFROM%20(%0A%20%20SELECT%0A%20%20%20%20user_info.steam_id,%0A%20%20%20%20user_info.name,%0A%20%20%20%20official_sprint.id,%0A%20%20%20%20CASE%20WHEN%20sle.rank%20is%20NULL%20OR%20sle.rank%20%3E%201000%20THEN%200%20ELSE%20ROUND(1000.0%20*%20(1.0%20-%20%7C/(1.0%20-%20(((sle.rank%20-%201.0)/1000.0)%20-%201.0)%5E2)))%20END%20AS%20noodle_points%0A%20%20FROM%20(%0A%20%20%20%20SELECT%20steam_id,%20name%20FROM%20Users%0A%20%20)%20user_info%0A%20%20CROSS%20JOIN%20(%0A%20%20%20%20SELECT%20id%20FROM%20official_levels%20WHERE%20is_sprint%0A%20%20)%20official_sprint%0A%20%20INNER%20JOIN%20sprint_leaderboard_entries%20sle%20ON%20sle.level_id%20=%20official_sprint.id%20AND%20sle.steam_id%20=%20user_info.steam_id%0A)%20official_ranks%0AGROUP%20BY%20steam_id,%20name%0AORDER%20BY%20SUM(noodle_points)%20DESC%0ALIMIT%20${limit}`, {
					"method": "GET",
					"mode": "cors",
					"credentials": "omit",
					"cache": "no-cache"
				});
				const json = await res.json();
				const currentPlayerId = document.location.href.match(/Player\?steamID=(\d+)$/)[1];
				const rivals = getRivals(currentPlayerId, json);
				console.log("[CAL] rivals:", rivals);
				const column = document.querySelector("body > div > main > div > div > div.col-xl-3.col-12");
				if (column.children.length >= 5) {
					column.children[4].remove();
				}
				if (column.children.length >= 4) {
					column.children[3].remove();
				}
				const hr = document.createElement("hr");
				const rivalDiv = document.createElement("div");
				rivalDiv.classList.add("fade-in");
				const rivalHead = document.createElement("h4");
				rivalHead.innerText = "Rivals";
				rivalDiv.appendChild(rivalHead);
				column.appendChild(hr);
				column.appendChild(rivalDiv);
				if (rivals === 0) {
					const p = document.createElement("p");
					p.innerHTML = "Rivals list could not be fetched. Try refreshing the page.";
					console.log("[CAL] failed fetching json", json);
					rivalDiv.appendChild(p);
				} else if (!rivals || rivals.find(r => r[0] === currentPlayerId)[3] === "0") {
					const p = document.createElement("p");
					p.innerHTML = "This player has 0 points.<br />They must be ranked within the top 1000 on any level to get points before a rivals list can be made.";
					rivalDiv.appendChild(p);
				} else {
					const ul = document.createElement("ul");
					ul.style.listStyleType = "none";
					ul.style.textAlign = "left";
					ul.style.paddingLeft = "0";
					rivalDiv.appendChild(ul);
					let oddEven = false;
					for (const rival of rivals) {
						const [id, rank, name, points, rating] = rival;
						const res2 = await fetch(`/Search/Players?q=${name}`);
						const players = await res2.json();
						const li = document.createElement("li");
						li.style.margin = "3px";
						li.style.padding = "3px";
						li.style.borderRadius = "5px";
						li.style.background = (oddEven) ? "#ffffff11" : "#ffffff1f";
						oddEven = !oddEven;
						const player = players.find(p => p.steamID === id);
						if (id === currentPlayerId) {
							li.style.background = "#00ffff2f";
						}
						let txt;
						if (!player) {
							txt = `${rank} &#9;&#9; <a class="link" style="margin-left:39px;" href="/Player?steamID=${id}">${name}</a>`
						} else {
							txt = `${rank} &#9;&#9; <a class="link" href="/Player?steamID=${id}"><img src="${player.steamAvatar}" class="mr-2" \>${name}</a>`;
						}
						ul.appendChild(li);

						li.innerHTML = txt;
					}
				}
				/*column.appendChild(rivalDiv);*/
			} catch (e) {
				console.error(e);
			}
		}
	}
	displayRivals();

	/* Add your own player ranks */

	const lssteamIDs = localStorage.getItem("mySteamID");
	const lsIds = lssteamIDs.split(",");

	async function getRankings(playerId) {
		if (!playerId) return;
		const match = document.location.href.match(/Leaderboard\/Level\?leaderboard(id|ID)=\d+$/);
		if (!match) return;
		const matches = document.location.href.match(/Leaderboard\/Level\?leaderboard(id|ID)=(\d+)$/);
		console.log(matches);
		if (!matches || !matches[2]) return;
		const [,, mapId] = matches;
		try {
			const res = await fetch(`/Player/GetLeaderboardRankings?steamID=${playerId}`);
			const json = await res.json();
			if (!json?.[0]) {
				return toastr.error('[HBT] No user found with the given SteamID64');
			}
			let thisMap = json.find(entry => entry.leaderboard.id == mapId);
			let unranked = false;
			if (!thisMap) {
				unranked = true;
				thisMap = json[0];
			}
			const topInfo = document.querySelector("body > div > main > div > div:nth-child(1)");
			while (topInfo.children.length >= 5 + lsIds.length) {
				topInfo.removeChild(topInfo.lastChild);
			}
			topInfo.style.marginBottom = "10px";
			const yourRank = `<div class="col-12"><h5 class="stroke" id="customRank"><img id="customAvatar" src="${thisMap.player.steamAvatar}" /><span><a href="/Player?steamID=${thisMap.player.steamID}">${thisMap.player.name}</a> Rank: ${(unranked) ? "None" : thisMap.rank}</span></h5></div>`
			topInfo.innerHTML += yourRank;
		} catch (e) {
			console.error(e);
		}
	}

	if (lsIds[0]) {
		for (const id of lsIds) {
			getRankings(id);
		}
	}

	async function getRankingsAll(playerId) {
		if (!playerId) return;
		if (!document.location.href.match(/Leaderboard\/Levels$/)) return;
		try {
			const res = await fetch(`/Player/GetLeaderboardRankings?steamID=${playerId}`);
			const json = await res.json();

			if (!json?.[0]) {
				return toastr.error('[HBT] No user found with the given SteamID64');
			}

			while (document.querySelectorAll(".level-card").length < 120) {
				await new Promise((resolve) => {
					setTimeout(resolve, 1000);
				});
			}
			const cards = Array.from(document.querySelectorAll(".level-card"));

			for (const card of cards) {
				const matches = card.children[0]?.href?.match(/Leaderboard\/Level\?leaderboard(id|ID)=(\d+)$/);
				if (!matches || !matches[2]) continue;
				const [,, mapId] = matches;
				if (!mapId) continue;
				const thisMap = json.find(entry => entry.leaderboard.id == mapId);
				if (!thisMap) continue;
				const cardInfo = card.children[0].children[0].children[1];
				while (cardInfo.children.length >= 3 + lsIds.length) {
					cardInfo.removeChild(cardInfo.lastChild);
				}
				const yourRank = `<h6 class="card-subtitle mb-2">${thisMap.player.name} Rank: ${thisMap.rank}</h6>`;
				cardInfo.innerHTML += yourRank;
			}
		} catch (e) {
			console.error(e);
		}
	}

	if (lsIds[0]) {
		for (const id of lsIds) {
			getRankingsAll(id);
		}
	}

	function insertSteamIDForm() {
		const form = `<form class="form-inline" id="steamID"><input class="form-control mr-sm-2" placeholder="Enter your SteamID64"><button class="btn btn-success">Profile</button></form>`;
		const div = document.createElement("div");
		div.innerHTML = form;
		const navbar = document.querySelector("body > header > nav");
		const search = navbar.querySelector("form input[placeholder=\"Enter player name\"]");
		if (search) {
			navbar.insertBefore(div.firstChild, navbar.querySelector("form"));
			document.querySelector("form#steamID button").style.marginRight = "15px";
		} else {
			navbar.appendChild(div.firstChild);
		}
		const steamIDs = localStorage.getItem("mySteamID");
		const ids = steamIDs.split(",");
		if (steamIDs && ids[0]) {
			document.querySelector("form#steamID input").value = steamIDs;
			document.querySelector("form#steamID button").addEventListener("click", (event) => {
				event.preventDefault();
				window.location.href = `http://holdboost.com/Player?steamID=${ids[0]}`;
			});
		}
		document.querySelector("form#steamID input").addEventListener("input", (event) => {
			const val = event.target.value;
			localStorage.setItem("mySteamID", val);
			console.log(`[CAL] Set SteamID: ${val}`);
			processChange(val);
		});
	}

	async function removeCustomProfile() {
		if (document.location.href.match(/Leaderboard\/Level\?leaderboard(id|ID)=\d+$/)) {
			const topInfo = document.querySelector("body > div > main > div > div:nth-child(1)");
			while (topInfo.children.length >= 6) {
				topInfo.removeChild(topInfo.lastChild);
			}
			topInfo.style.marginBottom = "50px";
		} else if (document.location.href.match(/Leaderboard\/Levels$/)) {
			while (document.querySelectorAll(".level-card").length < 120) {
				await new Promise((resolve) => {
					setTimeout(resolve, 1000);
				});
			}
			const cards = Array.from(document.querySelectorAll(".level-card"));
			for (const card of cards) {
				const cardInfo = card.children[0].children[0].children[1];
				while (cardInfo.children.length >= 3) {
					cardInfo.removeChild(cardInfo.lastChild);
				}
			}
		}
	}


	async function testValidID() {
		const idItem = localStorage.getItem("mySteamID");
		const ids = idItem.split(",");
		if (ids.length === 1 && ids[0] === "") {
			toastr.info('[HBT] Removed SteamID64');
			removeCustomProfile();
		}
		for (const id of ids) {
			const res = await fetch(`/Player/GetLeaderboardRankings?steamID=${id}`);
			const json = await res.json();
			if (!json?.[0]) {
				toastr.error(`[HBT] No user found with the given SteamID64: `);
				removeCustomProfile();
			} else {
				toastr.success(`[HBT] Found user with matching SteamID64: `);
				getRankings(id);
				getRankingsAll(id);
			}
		}
	}
	insertSteamIDForm();

	function debounce(func, timeout = 300) {
		let timer;
		return (...args) => {
			clearTimeout(timer);
			timer = setTimeout(() => { func.apply(this, args); }, timeout);
		};
	}
	const processChange = debounce(() => testValidID());

})();