// ==UserScript==
// @name Better Scoreboard [ Geoguessr ] [ geoguessr.com ]
// @version 1.4.1
// @author Han75 - @Han75#4985
// @description Improved lookup tool for geoguesser challenge leaderboard.
// @match https://www.geoguessr.com/*
// @require http://code.jquery.com/jquery-latest.js
// @namespace han75.com
// ==/UserScript==
/*
* API endpoint for the Geoguessr challenge mode
*
* Edpoint: api/ v3/ results/ scores/ <match ID>/ <Lowest_Checked(max:50)>
*/
const GEOGUESSR_ENDPOINT = "https://www.geoguessr.com/api/v3/results/scores/";
let preferedUnits = "miles";
/**
* Data is holds every record collected from geoguessr's API.
* DATA IS
* Map<String : Map<String..(4),Number..(2),Array< Number >..(2) >>
* WHERE
* {ID:DATA}= {Scoreboard_Pos : Name, uid, pfp, Game_Token, Total_Tcore, Total_Distance, All_Scores, All_Distances, {All API "guesses" data}}}
*/
let data=[];
/*
*meta is where I store the correct locations
*/
let meta={};
/*
* nameMap IS
* Map< String : STRING >
* WHERE
* {NAME : ID} = {name:coreBd_Pos}
*/
let nameMap = {}
// Number of players in the lobby
let numPlayers = 0;
let verifiedUsers = [];
$(document).ready(function () {
//AJAX stops document.ready from running when you click to a new page, so waitforkeyelements pauses the script until on the relevant page
waitForKeyElements(".results_switch__Qj1HI", start);
});
/**
* Helper function for creating a search option container div
* @param {String} name - Name of the div
* @returns String - HTML div
*/
function buildSearchOption(name){
return `<div class="bsbSearchOption" name=${name}></div>`
}
/**
* Helper function for creating an input field.
* @param {Map<String,String>} input - Input and Label Parameters {ID,TYPE,TEXT}
* @param {Map<String,String>} button - Button Parameters {ID, TEXT}
* @returns String - HTML label,input,button.
*/
function buildInputField(input,button){
return `<label class="bsbInputLbl" for="${input["ID"]}">${input["TEXT"]}</label><input type="${input["TYPE"]}" min="1" id="${input["ID"]}"><button class="bsbGuiBtn" id="${button["ID"]}">${button["TEXT"]}</button>`
}
/**
* Helper function for creating a result row.
* @param {Number} position - Player's position on the lb
* @param {Map} a - The player's data, or one row from the data variable.
* @returns String - HTML Result row
*/
function buildResultRow(position,a){
let resultStrs = new Array(5);
let totTime = 0;
let totalDist;
switch(preferedUnits){
case "miles":
totalDist = Number(a["totDist"])*0.621371;
if(totalDist<1){
totalDistString = Math.round(totalDist*1760)+ " yd";
}
else if(totalDist<10){
totalDistString = totalDist.toFixed(1)+" miles";
}else{
totalDistString = Math.round(totalDist) +" miles";
}
break;
case "meters":
totalDist = Number(a["totDist"]);
if(totalDist<1){
totalDistString = Math.round(totalDist*1000)+ " m";
}
else if(totalDist<10){
totalDistString = totalDist.toFixed(1)+" km";
}else{
totalDistString = Math.round(totalDist) +" km";
}
break;
}
for(let i=0;i<5;i++){
let time=a["guesses"][i]["time"];
totTime+=time;
resultStrs[i] = `${a["guesses"][i]["distance"][preferedUnits]["amount"]} ${a["guesses"][i]["distance"][preferedUnits]["unit"]} - ${Math.floor(time/60)==0?"":Math.floor(time/60)} ${Math.floor(time/60)==0?"":"min,"} ${time%60} sec`
}
return `<div class="results_row__2iTV4" id="bsbEntry${position}">
<div class="results_column__BTeok results_player__F8U_T">
<span class="results_position__KWMOY">
${position + 1}.
</span>
<div class="results_userLink__V6cBI">
<a target="_blank" href="/user/${a["uid"]}">
<div class="user-nick_root__DUfvc">
<div class="user-nick_avatar__lW3e2">
<div class="styles_rectangle___6gqv" style="padding-top: 100%;">
<div class="styles_circle__QFYEk styles_variantFloating__Srm_N styles_colorWhite__Y640w styles_borderSizeFactorOne__8iP_3">
<div class="styles_rectangle___6gqv" style="padding-top: 100%;">
<div class="styles_innerCircle__Y_L_e">
<div class="styles_content__otIVG">
<img src="/images/auto/48/48/ce/0/plain/${a["pic"]}" class="styles_image__8M_kp" alt="${a["name"]}" loading="auto" style="object-fit: cover;">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="user-nick_nickWrapper__8Tnk4">
<div class="user-nick_nick__y4VIt">
${a["name"]}
</div>
${a["isVerified"]?`<div class="user-nick_verifiedWrapper__yocOV">
<img class="user-nick_verified__WdndT" src="/_next/static/images/verified-badge-566f0efd4d90928c6e044cbe588456dc.svg" alt="Verified user">
</div>`:``}
</div>
</div>
</a>
</div>
</div>
<div class="results_column__BTeok results_hideOnSmallScreen__hrv5O">
<div class="results_score__jUqyZ">
${a["scores"][0]} pts
</div>
<div class="results_scoreDetails__rvWSm">
${resultStrs[0]}
</div>
</div>
<div class="results_column__BTeok results_hideOnSmallScreen__hrv5O">
<div class="results_score__jUqyZ">
${a["scores"][1]} pts
</div>
<div class="results_scoreDetails__rvWSm">
${resultStrs[1]}
</div>
</div>
<div class="results_column__BTeok results_hideOnSmallScreen__hrv5O">
<div class="results_score__jUqyZ">
${a["scores"][2]} pts
</div>
<div class="results_scoreDetails__rvWSm">
${resultStrs[2]}
</div>
</div>
<div class="results_column__BTeok results_hideOnSmallScreen__hrv5O">
<div class="results_score__jUqyZ">
${a["scores"][3]} pts
</div>
<div class="results_scoreDetails__rvWSm">
${resultStrs[3]}
</div>
</div>
<div class="results_column__BTeok results_hideOnSmallScreen__hrv5O">
<div class="results_score__jUqyZ">
${a["scores"][4]} pts
</div>
<div class="results_scoreDetails__rvWSm">
${resultStrs[4]}
</div>
</div>
<div class="results_column__BTeok">
<div class="results_score__jUqyZ">
${a["totscore"]} pts
</div>
<div class="results_scoreDetails__rvWSm">
${totalDistString} - ${Math.floor(totTime/60)==0?"":Math.floor(totTime/60)} ${Math.floor(totTime/60)==0?"":"min,"} ${totTime%60} sec
</div>
</div>
</div>`;
}
/**
* Helper function for creating the header row
* @returns String - HTML Leaderboard header(round 1/../5)
*/
function buildHeaderRow(){
return `<div class="results_row__2iTV4 results_headerRow__C91Ks">
<div>
</div>
<div class="results_hideOnSmallScreen__hrv5O">
Round 1
</div>
<div class="results_hideOnSmallScreen__hrv5O">
Round 2
</div>
<div class="results_hideOnSmallScreen__hrv5O">
Round 3
</div>
<div class="results_hideOnSmallScreen__hrv5O">
Round 4
</div>
<div class="results_hideOnSmallScreen__hrv5O">
Round 5
</div>
<div>
Total
</div>
</div>`;
}
/**
* Renders the tool to the DOM and begins scanning the data.
*/
function start() {
id = $(`meta[property='og:url']`).attr("content").split("/")[4];
const modalElement = `
<div class="statsModal">
<div class="statsModalContent">
<div class="statsModalHeader">
<span class="statsModalClose">×</span>
<h2>Challenge Statistics</h2>
</div>
<div class="statsModalBody">
</div>
</div>
</div>
`;
$('body').prepend(modalElement);
$('.results_switch__Qj1HI').after('<div id="bsbHeaderContainer"></div>');
$('#bsbHeaderContainer').after('<div id="bsbBodyContainer"></div>');
/** HEADER */
$('#bsbHeaderContainer').append('<div id="bsbHeader"></div>');
$('#bsbHeader').append(`<div id="bsbInfo"></div>`);
let showVerifiedButton = `
<button class = "bsbGuiBtn" id="showVerifiedUsers">Show Verified Users</button>
`;
$('#bsbHeader').append(showVerifiedButton);
$('#bsbHeader').append('<span class="bsbSearchTab" id="singularTab">Search Single Record</span>');
$('#bsbHeader').append(`<div class="bsbSearchContainer" id="singularContainer"></div>`);
$('#bsbHeader').append('<span class="bsbSearchTab" id="rangeTab">Search Record Range(max:200)</span>');
$('#bsbHeader').append(`<div class="bsbSearchContainer" id="rangeContainer"></div>`);
$('#bsbInfo').append('<h2 class=`bsbInfoText`>Better Leaderboard</h2>');
$('#singularContainer').append(buildSearchOption("rankSearch"));
$('#singularContainer').append(buildSearchOption("nameSearch"));
//Search by LB position input field
let i = {"ID":"searchPosition","TYPE":"number","TEXT":"Position:"};
let b = {"ID":"bsbSearchPosBtn","TEXT":"Search By Leaderboard Position"};
$('.bsbSearchOption[name="rankSearch"]').append(buildInputField(i,b));
//Search by username input field
i = {"ID":"searchName","TYPE":"text","TEXT":"Username:"};
b = {"ID":"bsbSearchNameBtn","TEXT":"Search By Username"};
$('.bsbSearchOption[name="nameSearch"]').append(buildInputField(i,b));
const searchRangeField = `
<div class="bsbTextBoxes" style="display:flex;flex-direction: row;"> <div class="bsbSearchOption" name="rangeFirst"
style="width: 50%; border: 1px solid rgb(70, 35, 57); border-radius: 5px; grid-row:1; display: flex; flex-direction: column;">
<label class="bsbInputLbl" for="searchRangeFirst" style="padding: 4px; font-weight: bold;">First:</label><input
type="number" min="1" id="searchRangeFirst">
</div>
<div class="bsbSearchOption" name="rangeLast"
style="width: 50%; border: 1px solid rgb(70, 35, 57); border-radius: 5px; display: flex; flex-direction: column;">
<label class="bsbInputLbl" for="searchRangeLast" style="padding: 4px; font-weight: bold;">Last:</label><input
type="number" min="1" id="searchRangeLast">
</div>
</div>
<button class="bsbGuiBtn" id="bsbSearchRangeBtn"
style="border: none; padding: 10px; background-color: rgb(71, 62, 96); color: white; cursor: auto;">Search
Range</button>
`;
$('#rangeContainer').append(searchRangeField);
$('#bsbHeader').append('<a id="exportRecords">Export All Records As .json</a>');
/**BODY */
$('#bsbBodyContainer').append('<div class="results_table__FHKQm" id="bsbBody"></div>');
$('#bsbBodyContainer').after('<span id="closeDefaultScoreboard">Click to Toggle Default Leaderboard</span>')
/* ====================== ALL HTML GOES ABOVE THIS LINE ========================*/
/*======================== ALL CSS GOES BELOW THIS LINE ========================*/
$('#bsbHeader').css({ "display": "flex", 'flex-direction': 'column',"background-color": "#4D5180", "text-align": "center","border-radius":"25px" });
$('#bsbHeaderContainer').css({"width":"60%", "display": "flex", 'flex-direction': 'column' });
$('.bsbSearchContainer').css({ "display": "flex", "flex-direction": "row" });
$("#rangeContainer").css("flex-direction","column");
$('.bsbSearchTab').css({ "display": "block", "text-align": "left", "background-color": "rgb(121,80,229)", "padding": "5px" });
$('.bsbSearchTab').mouseover(function () {
$(this).css({ "background-color": "rgb(157,41,56)", "cursor": "pointer" });
}).mouseout(function () {
$(this).css({ "background-color": "rgb(121,80,229)", "cursor": "auto" });
});
/* Begin Search Singular Tab Animate Open and Close */
$('#singularTab').click(function () {
$('#singularContainer').slideToggle({
"opacity": "show",
"bottom": "100"
}, 500);
//$('#singularTab').css("background-color","#4D5180");
$('#singularTab').addClass('singularTriggerClose');
});
$('.singularTriggerClose').click(function () {
$('#singularContainer').slideToggle({ "opacity": "show", "top": "100" }, 500);
//$('#singularTab').css("background-color","rgb(121,80,229)");
$('#singularTab').removeClass('singularTriggerClose');
});
/* End Search Singular Tab Animate Open and Close */
/* Begin Search Range Tab Animate Open and Close */
$('#rangeTab').click(function () {
$('#rangeContainer').slideToggle({
"opacity": "show",
"bottom": "100"
}, 500);
//$('#rangeTab').css("background-color","#4D5180");
$('#rangeTab').addClass('rangeTriggerClose');
});
$('.rangeTriggerClose').click(function () {
$('#rangeContainer').slideToggle({ "opacity": "show", "top": "100" }, 500);
//$('#rangeTab').css("background-color","rgb(121,80,229)");
$('#rangeTab').removeClass('rangeTriggerClose');
});
/* End Search Range Tab Animate Open and Close */
$('#rangeTab').click();
$('.bsbSearchOption').css({"width":"50%", "border": "1px solid rgb(70,35,57)", "border-radius": "5px", "display": "flex", "flex-direction": "column" });
$(".bsgGuiBtn").css({"padding":"10px"})
$("#bsbSearchPosBtn").prop("disabled", true);
$("#bsbSearchNameBtn").prop("disabled", true);
$("#bsbSearchRangeBtn").prop("disabled", true);
$("#bsbSearchPosBtn").click(findPosition);
$("#bsbSearchNameBtn").click(findUsername);
$("#bsbSearchRangeBtn").click(findRange);
$("#exportRecords").click(download);
$("#exportRecords").css({
"color": "white",
"padding": "10px",
"font-weight": "bold"
});
$(".bsb")
$('.bsbGuiBtn').css({"border":"none","padding":"10px","font-family":"var(--font-neo-sans);","background-color": "rgb(71,62,96)","color":"white"})
$('.bsbGuiBtn').mouseover(function () {
$(this).css({ "background-color": "rgb(26,26,46)", "cursor": "pointer" });
}).mouseout(function () {
$(this).css({ "background-color": "rgb(71,62,96)", "cursor": "auto" });
});
$('#exportRecords').mouseover(function () {
$(this).css({"cursor": "pointer" });
}).mouseout(function () {
$(this).css({"cursor": "auto" });
});
$('#bsbBodyContainer').css("width","100%");
$('.bsbInputLbl').css({"padding":"4px","font-weight":"bold"});
$('#bsbInfo').css("padding","10px");
$('#closeDefaultScoreboard').css({ "font-weight":"bold","display": "block", "text-align": "left", "background-color": "rgb(121,80,229)", "padding": "10px","width":"100%" });
$('#closeDefaultScoreboard').click(function () {
$('.results_container__9fcR8').find("> .results_table__FHKQm").slideToggle({
"opacity": "show",
"bottom": "100"
}, 500);
//$('#singularTab').css("background-color","#4D5180");
});
$('.cldTriggerClose').click(function () {
$('.results_container__9fcR8').find("> .results_table__FHKQm").slideToggle({ "opacity": "show", "top": "100" }, 500);
//$('#singularTab').css("background-color","rgb(121,80,229)");
$('#closeDefaultScoreboard').removeClass('cldTriggerClose');
});
$('#closeDefaultScoreboard').mouseover(function () {
$(this).css({ "background-color": "rgb(157,41,56)", "cursor": "pointer" });
}).mouseout(function () {
$(this).css({ "background-color": "rgb(121,80,229)", "cursor": "auto" });
});
let modalCss = {
"display": "none",
"position": "fixed",
"z-index": "3",
"padding-top": "100px",
"left": "0",
"top": "0",
"width": "100%",
"height": "100%",
"overflow": "auto",
"background-color": "rgb(0,0,0)",
"background-color":"rgba(0,0,0,0.4)"
}
let modalContentCss = {
"position": "relative",
"background-color": "#fefefe",
"margin": "auto",
"padding": "0",
"border": "1px solid #888",
"width": "80%",
"box-shadow": "0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)"
}
let closeCss = {
"float": "right",
"font-size": "28px",
"font-weight": "bold"
}
$(".statsModal").css(modalCss);
$(".statsModalContent").css(modalContentCss);
$(".statsModalClose").css(closeCss);
$(".statsModalClose").click(function(){
$(".statsModal").css("display","none");
});
$("#showVerifiedUsers").click(function(){
showVerifieds();
});
let u = $(".results_scoreDetails__rvWSm:first").text();
if(!(u.includes("yd")||u.includes("miles"))){
preferedUnits="meters";
}
// The final stack trace is || RoundUp(NumPlayers/50) api requests <- getData() <- getNumPlayers(log(playercount) api requests)
// I thought this was a bad choice at first, but it’s probably better than accidentally sending sh*t loads of API requests because of preliminary fetch delays and getting slammed with a rate limit
//This ones for testing, only 200 records
//findNumberOfPlayers(0,250);
//This ones for final
findNumberOfPlayers(0,10000);
}
/**
* Finds how many players are in a lobby
* @param {2 Numbers} LowerBound,UpperBound
*/
async function findNumberOfPlayers(lowerBound, upperBound) {
//Binary search babyy
if (Math.ceil(lowerBound) >= Math.floor(upperBound) - 1) {
data = Array(Math.ceil(lowerBound));
getData(Math.ceil(lowerBound));
} else {
let midpoint = Math.ceil((lowerBound + upperBound) / 2);
//Fetch 1 entry from API, change search bounds and search again
fetch(`${GEOGUESSR_ENDPOINT}${id}/${midpoint}/1`, { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" }).then((response) => response.json())
.then((resp) => {
if("message" in resp){
displayRateLimitError();
}
else if (resp.length == 0) {
findNumberOfPlayers(lowerBound, midpoint);
} else {
findNumberOfPlayers(midpoint, upperBound);
}
});;
}
}
/**
* Loads all of the data and stores it in data dictionary.
*
*/
async function getData(nPlayers) {
numPlayers = nPlayers;
//Fetch all the players.
//Can fetch a max of 50 datapoints at a time
for (let i = 0; i < nPlayers; i += 50) {
//accept parameter is used by native application and I added it to ensure that it Satan himself(CORS) doesn't stop me
fetch(`${GEOGUESSR_ENDPOINT}${id}/${i}/50`, { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" }).then((response) => response.json()).then((resp) => {
// I hate the variable name. resp ? Like ew whyd i do that
if(i==0){
meta={"map":resp[0]["game"]["map"],"rounds":resp[0]["game"]["rounds"]};
}
for (let k = 0; k < resp.length; k++) {
let scores = [];
let dists = [];
let tDist = 0;
for (let round = 0; round < resp[k]["game"]["player"]["guesses"].length; round++) {
scores.push(resp[k]["game"]["player"]["guesses"][round]["roundScoreInPoints"]);
dists.push((resp[k]["game"]["player"]["guesses"][round]["distanceInMeters"] / 1000).toFixed(3));
tDist += resp[k]["game"]["player"]["guesses"][round]["distanceInMeters"] / 1000;
}
let isVerified = resp[k]["game"]["player"]["isVerified"]||resp[k]["playerName"]=="Han75";
data[i + k] = { "name": resp[k]["playerName"], "uid": resp[k]["userId"], "pic": resp[k]["pinUrl"], "gametoken": resp[k]["gameToken"], "totscore": resp[k]["game"]["player"]["totalScore"]["amount"], "totDist": tDist.toFixed(3), "scores": scores, "dists": dists, "guesses":resp[k]["game"]["player"]["guesses"],"isVerified":isVerified}
// Map user name to score for fast lookup
nameMap[resp[k]["playerName"].toLowerCase()] = i + k;
if(isVerified){
verifiedUsers.push(i+k);
}
}
})
}
$('#bsbInfo').append(`<p>${nPlayers + 1} players </p>`);
$("#bsbSearchPosBtn").prop("disabled", false);
$("#bsbSearchNameBtn").prop("disabled", false);
$('#bsbSearchRangeBtn').prop("disabled",false);
}
/**
* Terminates execution if rate limit is detected
*/
function displayRateLimitError(){
$("#bsbBody").append(`<p>Sorry, your account has been temporarily rate limited. Please try again in 30 minutes. In the meantime, you can play another game <a href="https://www.geoguessr.com/maps/59a1514f17631e74145b6f47/play" style="color:white; text-decoration: underline;">here</a></p>`);
}
/**
* Downloads all game data to a .json file.
*/
function download() {
meta["URL"]=$(`meta[property='og:url']`).attr("content")
meta["downloadTime"]=Date();
let dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify({"metadata":meta,"playerdata":data}));
$('#exportRecords').attr("href", dataStr);
$('#exportRecords').attr("download", `Geoguessr_Export_n${numPlayers}.json`);
}
/**
* Searches data by Scoreboard Position from the input box
*/
function findPosition() {
let position = Math.floor($('#searchPosition').val() - 1);
if (position>=0&&position<data.length) {
let a = data[position];
// I’m not gonna cap I straight up ripped these fancy ahh stylized ahh divs straight from the results table, whose gonna stop me? 😈
$("#bsbBody").empty();
$("#bsbBody").append(buildHeaderRow());
//Literally what is this naming convention? Nothing about this makes sense. You don’t have to make it this hard on yourself, @GeoGuessr’s singular front end engineer
$("#bsbBody").append(buildResultRow(position,a));
// When I click the div I open game overview in a new tab
// I can’t show it on this page because I can’t add the the native event listener to the new div because it’s disgustingly obfuscated
$(`#bsbEntry${position}`).click(function (e) {
e.preventDefault();
window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);
});
} else if (position < 1 || position > numPlayers) {
$('#searchPosition').val("");
} else {
$("#bsbBody").empty();
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Undefined error or you broke the script probably. This isn’t gonna show up any other way </div>`);
}
}
/**
* searches data by username
*/
function findUsername() {
// Get that shi from the input box. MAN I love jquery
let name = $('#searchName').val();
if (name.toLowerCase() in nameMap) {
let position = nameMap[name.toLowerCase()];
let a = data[position];
// Everything below this like is exactly the same as searchPosition
$("#bsbBody").empty();
$("#bsbBody").append(buildHeaderRow());
$("#bsbBody").append(buildResultRow(position,a));
$(`#bsbEntry${position}`).click(function (e) {
//TODO: make user display on map when you click thier score tab.
// console.log(a["name"])
// let i=0;
// while($(`.results_row__2iTV4:contains(${a["name"]})`).length <2 && i<position){
// //console.log($(`.results_row__2iTV4:contains("${a["name"]}")`))
// //console.log("probably stuck here");
// $(".button_variantSecondary__lSxsR").click()
// i++;
// }
// console.log("After while");
// console.log($(`.results_row__2iTV4:contains(${a["name"]})`)[1])
// $(`.results_row__2iTV4:contains(${a["name"]})`)[1].click();
// e.preventDefault();
window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);
});
} else {
$("#bsbBody").empty();
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">User not found</div>`);
}
}
/**
* Displays 1<=n<=200 results, starting at the specified start index, ending at end index.
*/
function findRange(){
let rStart = Math.floor($("#searchRangeFirst").val())-1;
let rEnd = Math.floor($("#searchRangeLast").val())-1;
let n = rEnd-rStart;
$("#bsbBody").empty();
if(rEnd>numPlayers||rEnd<0||rStart>numPlayers||rStart<0){
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Invalid search bounds. Please change your search</div>`);
}else if(n<=0||n>200){
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Number of records to search must be between 0 and 200</div>`);
}else{
$("#bsbBody").append(buildHeaderRow());
for(let i=rStart;i<rEnd;i++){
let a = data[i];
$("#bsbBody").append(buildResultRow(i,a));
$("#bsbBody").append(`<div class="results_rowDivider__py9ZY"></div>`);
// When I click the div I open game overview in a new tab
// I can’t show it on this page because I can’t add the the native event listener to the new div because it’s disgustingly obfuscated
$(`#bsbEntry${i}`).click(function (e) {
e.preventDefault();
window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);
});
}
}
}
/*
* Shows all of the verified users.
*/
function showVerifieds(){
$("#bsbBody").empty();
$("#bsbBody").append(buildHeaderRow());
verifiedUsers.sort((a,b)=>{
return a-b;
});
for(let i=0;i<verifiedUsers.length;i++){
let a=data[verifiedUsers[i]];
$("#bsbBody").append(buildResultRow(verifiedUsers[i],a));
$("#bsbBody").append(`<div class="results_rowDivider__py9ZY"></div>`);
// When I click the div I open game overview in a new tab
// I can’t show it on this page because I can’t add the the native event listener to the new div because it’s disgustingly obfuscated
$(`#bsbEntry${verifiedUsers[i]}`).click(function (e) {
e.preventDefault();
window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);
});
}
}