// ==UserScript==
// @name Better Scoreboard [ Geoguessr ] [ geoguessr.com ]
// @version 1.3.6
// @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 endpoint = "https://www.geoguessr.com/api/v3/results/scores/";
/**
* 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}}}
*/
var data = {}
/*
* nameMap IS
* Map< String : STRING >
* WHERE
* {NAME : ID} = {name:coreBd_Pos}
*/
var nameMap = {}
// Number of players in the lobby
let numPlayers = 0;
$(document).ready(function () {
//render context
waitForKeyElements(".results_switch__Qj1HI", start);
});
function start() {
id = $(`meta[property='og:url']`).attr("content").split("/")[4];
$('.results_switch__Qj1HI').after('<div id="bsbHeaderContainer"></div>');
$('#bsbHeaderContainer').after('<div id="bsbBodyContainer"></div>');
console.log('should now set css');
$('#bsbHeaderContainer').append('<div id="bsbHeader"></div>');
/** HEADER */
$('#bsbHeader').append(`<div id="bsbInfo"></div>`);
$('#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:50)</span>');
$('#bsbHeader').append(`<div class="bsbSearchContainer" id="rangeContainer"></div>`);
$('#bsbInfo').append('<h2 class=`bsbInfoText`>Better Scoreboard</h2>');
$('#bsbInfo').append('<h4 class=`bsbInfoText`>By Han75</h4>');
$('#singularContainer').append(`<div class="bsbSearchOption" name='rankSearch'></div>`);
$('#singularContainer').append(`<div class="bsbSearchOption" name='nameSearch'></div>`);
$('.bsbSearchOption[name="rankSearch"]').append(`<label class="bsbInputLbl" for="searchPosition">Rank:</label><input type="number" min="1" id="searchPosition"><button class="bsbGuiBtn" id="bsbSearchPosBtn">Search By Rank</button>`);
$('.bsbSearchOption[name="nameSearch"]').append(`<label class="bsbInputLbl for="searchName">Username:</label><input type="text" min="1" id="searchName"><button class="bsbGuiBtn" id="bsbSearchNameBtn">Search By Username</button>`);
$('#rangeContainer').append(`<div class="bsbSearchOption" name='rangeFirst'></div>`);
$('#rangeContainer').append(`<div class="bsbSearchOption" name='rangeLast'></div>`);
$('.bsbSearchOption[name="rangeFirst"]').append(`<label class="bsbInputLbl for="searchRangeFirst">First:</label><input type="Number" id="searchRangeFirst">`);
$('.bsbSearchOption[name="rangeLast"]').append(`<label class="bsbInputLbl for="searchRangeLast">Last:</label><input type="Number" id="searchRangeLast"><button class="bsbGuiBtn" id="bsbSearchRangeBtn">Search Range</button>`);
$('#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 Scoreboard</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" });
$('.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" });
});
/***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');
});
/***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');
});
$('#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" });
});
// 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 100 records
//findNumberOfPlayers(0,100);
//This ones for final
findNumberOfPlayers(0,6000);
}
/**
* 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) {
getData(Math.ceil(lowerBound));
} else {
let midpoint = Math.ceil((lowerBound + upperBound) / 2);
//Fetch 1 entry from API, change search bounds and search again
fetch(`${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 (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(`${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
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;
}
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"]}
// Map user name to score for 1 : 1 correlation
nameMap[resp[k]["playerName"].toLowerCase()] = i + k;
}
})
}
$('#bsbInfo').append(`<p>${nPlayers + 1} players </p>`);
$("#bsbSearchPosBtn").prop("disabled", false);
$("#bsbSearchNameBtn").prop("disabled", false);
$('#bsbSearchRangeBtn').prop("disabled",false);
}
// TODO: I want the @p to say “N records found (download)”
function download() {
//TODO: do this function later take a break, jesus.
console.log(`File will have ${Object.keys(data).length} entries`);
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data));
console.log(`File will have ${Object.keys(data).length} entries`);
$('#exportRecords').attr("href", dataStr);
$('#exportRecords').attr("download", `Geoguessr_Export_n${numPlayers}.json`);
}
/**
* Searches data by Scoreboard Position from the input box
*/
// refactor to a better name
function findPosition() {
let position = Math.floor($('#searchPosition').val() - 1);
if (position in data) {
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(`<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>`);
//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(`<div class="results_row__2iTV4" id="bsbEntry"><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="Efesoturum" 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></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">${a["guesses"][0]["distance"]["miles"]["amount"]} ${a["guesses"][0]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][0]["time"]/60)==0?"":Math.floor(a["guesses"][0]["time"]/60)} ${Math.floor(a["guesses"][0]["time"]/60)==0?"":"min,"} ${a["guesses"][0]["time"]%60} sec
</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">${a["guesses"][1]["distance"]["miles"]["amount"]} ${a["guesses"][1]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][1]["time"]/60)==0?"":Math.floor(a["guesses"][1]["time"]/60)} ${Math.floor(a["guesses"][1]["time"]/60)==0?"":"min,"} ${a["guesses"][1]["time"]%60} sec</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">${a["guesses"][2]["distance"]["miles"]["amount"]} ${a["guesses"][2]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][2]["time"]/60)==0?"":Math.floor(a["guesses"][2]["time"]/60)} ${Math.floor(a["guesses"][2]["time"]/60)==0?"":"min,"} ${a["guesses"][2]["time"]%60} sec
</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">${a["guesses"][3]["distance"]["miles"]["amount"]} ${a["guesses"][3]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][3]["time"]/60)==0?"":Math.floor(a["guesses"][3]["time"]/60)} ${Math.floor(a["guesses"][3]["time"]/60)==0?"":"min,"} ${a["guesses"][3]["time"]%60} sec
</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">${a["guesses"][4]["distance"]["miles"]["amount"]} ${a["guesses"][4]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][4]["time"]/60)==0?"":Math.floor(a["guesses"][4]["time"]/60)} ${Math.floor(a["guesses"][4]["time"]/60)==0?"":"min,"} ${a["guesses"][4]["time"]%60} sec</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></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").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];
let a = data[nameMap[name]];
// Everything below this like is exactly the same as searchPosition
$("#bsbBody").empty();
$("#bsbBody").append(`<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>`);
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry"><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="Efesoturum" 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></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">${a["guesses"][0]["distance"]["miles"]["amount"]} ${a["guesses"][0]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][0]["time"]/60)==0?"":Math.floor(a["guesses"][0]["time"]/60)} ${Math.floor(a["guesses"][0]["time"]/60)==0?"":"min,"} ${a["guesses"][0]["time"]%60} sec
</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">${a["guesses"][1]["distance"]["miles"]["amount"]} ${a["guesses"][1]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][1]["time"]/60)==0?"":Math.floor(a["guesses"][1]["time"]/60)} ${Math.floor(a["guesses"][1]["time"]/60)==0?"":"min,"} ${a["guesses"][1]["time"]%60} sec</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">${a["guesses"][2]["distance"]["miles"]["amount"]} ${a["guesses"][2]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][2]["time"]/60)==0?"":Math.floor(a["guesses"][2]["time"]/60)} ${Math.floor(a["guesses"][2]["time"]/60)==0?"":"min,"} ${a["guesses"][2]["time"]%60} sec
</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">${a["guesses"][3]["distance"]["miles"]["amount"]} ${a["guesses"][3]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][3]["time"]/60)==0?"":Math.floor(a["guesses"][3]["time"]/60)} ${Math.floor(a["guesses"][3]["time"]/60)==0?"":"min,"} ${a["guesses"][3]["time"]%60} sec
</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">${a["guesses"][4]["distance"]["miles"]["amount"]} ${a["guesses"][4]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][4]["time"]/60)==0?"":Math.floor(a["guesses"][4]["time"]/60)} ${Math.floor(a["guesses"][4]["time"]/60)==0?"":"min,"} ${a["guesses"][4]["time"]%60} sec</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`);
$("#bsbEntry").click(function (e) {
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>`);
}
}
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>50){
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Number of records to search must be between 0 and 50</div>`);
}else{
$("#bsbBody").append(`<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>`);
for(let i=rStart;i<rEnd;i++){
let a = data[i];
$("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry${i}"><div class="results_column__BTeok results_player__F8U_T"><span class="results_position__KWMOY">${i + 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="Efesoturum" 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></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">${a["guesses"][0]["distance"]["miles"]["amount"]} ${a["guesses"][0]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][0]["time"]/60)==0?"":Math.floor(a["guesses"][0]["time"]/60)} ${Math.floor(a["guesses"][0]["time"]/60)==0?"":"min,"} ${a["guesses"][0]["time"]%60} sec
</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">${a["guesses"][1]["distance"]["miles"]["amount"]} ${a["guesses"][1]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][1]["time"]/60)==0?"":Math.floor(a["guesses"][1]["time"]/60)} ${Math.floor(a["guesses"][1]["time"]/60)==0?"":"min,"} ${a["guesses"][1]["time"]%60} sec</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">${a["guesses"][2]["distance"]["miles"]["amount"]} ${a["guesses"][2]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][2]["time"]/60)==0?"":Math.floor(a["guesses"][2]["time"]/60)} ${Math.floor(a["guesses"][2]["time"]/60)==0?"":"min,"} ${a["guesses"][2]["time"]%60} sec
</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">${a["guesses"][3]["distance"]["miles"]["amount"]} ${a["guesses"][3]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][3]["time"]/60)==0?"":Math.floor(a["guesses"][3]["time"]/60)} ${Math.floor(a["guesses"][3]["time"]/60)==0?"":"min,"} ${a["guesses"][3]["time"]%60} sec
</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">${a["guesses"][4]["distance"]["miles"]["amount"]} ${a["guesses"][4]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][4]["time"]/60)==0?"":Math.floor(a["guesses"][4]["time"]/60)} ${Math.floor(a["guesses"][4]["time"]/60)==0?"":"min,"} ${a["guesses"][4]["time"]%60} sec</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`);
$("#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"]}`);
});
}
}
}
/*****waitforkeyelements.js Used to combat no page refresh due to AJAX. *
* @Author BrockA
* /
/*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
that detects and handles AJAXed content.
Usage example:
waitForKeyElements (
"div.comments"
, commentCallbackFunction
);
//--- Page-specific function to do what we want when the node is found.
function commentCallbackFunction (jNode) {
jNode.text ("This comment changed by waitForKeyElements().");
}
IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements (
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents ()
.find (selectorTxt);
if (targetNodes && targetNodes.length > 0) {
btargetsFound = true;
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each ( function () {
var jThis = $(this);
var alreadyFound = jThis.data ('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
var cancelFound = actionFunction (jThis);
if (cancelFound)
btargetsFound = false;
else
jThis.data ('alreadyFound', true);
}
} );
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace (/[^\w]/g, "_");
var timeControl = controlObj [controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval (timeControl);
delete controlObj [controlKey]
}
else {
//--- Set a timer, if needed.
if ( ! timeControl) {
timeControl = setInterval ( function () {
waitForKeyElements ( selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
300
);
controlObj [controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}