您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improved lookup tool for geoguesser challenge leaderboard.
当前为
// ==UserScript== // @name Better Scoreboard [ Geoguessr ] [ geoguessr.com ] // @version 1.3.6.5 // @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>'); $('#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; }