NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name GeoGuessr League List Enhanced // @namespace dreifachpunkt. // @author dreifachpunkt. // @description Display all your leagues in a table or calendar, ordered by the next leg's expiration date. Code Framework by Kommu. // @version 0.1.10 // @include https://www.geoguessr.com/* // @run-at document-start // @license MIT // @updateURL https://openuserjs.org/meta/dreifachpunkt./GeoGuessr_League_List_Enhanced.meta.js // ==/UserScript== /*jshint esversion: 6 */ // -------------------------------- SETTINGS --------------------------------------------- // Hide the information on the top of the Pro Leagues page. var hideInfo = false; //available: true, false // This script features two different designs: // (1) An OVERVIEW design in which each league only appears once and // (2) a CALENDAR design in which every upcoming leg of each league is shown. // You can set the design to be displayed first when you load the page. var startDesign = "overview"; //available: overview, calendar // You can select whether the "leg ends" column shows the expiration date, the remaining // time or both (not for the compact calendar). var legEndsFormat = "both"; //available: date, remaining, both // Did you join a league but then decide that you do not want to play it? With this feature // you can hide leagues. Click on the league and copy the last part of the URL into the // array. var hiddenLeagues = []; //e.g. ["CPt0tzDt4fMUZcEL", "nUOfPCg358UZ0h5R"] for the Diverse World and NPMZ League 2022 // You can choose your preferred symbols or text for the different types of league statuses. // E.g. use one of these symbols: ⚫⚪🔴🟠🟡🟢🔵🟣🟤⭕❌✅❎. var symbolPlayed = "🟢"; var symbolNotPlayed = "🔴"; var symbolNotAvailable = "⚪"; // For those who have difficulties reading the new font, you can change it to Arial. var arialFont = false; //available: true, false // ------------------------- DESIGN-SPECIFIC SETTINGS ------------------------------------ // For the OVERVIEW, you can activate an advanced version to display additional // information: // (1) The time limit and game mode (moving, NM, NMPZ, etc.), // (2) the duration of a leg and // (3) the amount of players who already played the leg. // The advanced overview takes a bit longer to load if you have many leagues. // Also for smaller screen sizes (e.g. laptops) I do not recommend the advanced version. var overviewAdvancedVersion = false; //available: true, false // For the CALENDAR, you can choose between a compact and a list appearance. // The compact calendar does not show leagues that are open for registration. var calendarAppearance = "compact"; //available: compact, list // For the COMPACT CALENDAR, you can display the current month or the upcoming two weeks. var calendarCompactType = "weeks"; //available: weeks, month // For the LIST CALENDAR, you can set the amount of entries to be shown in the table. var calendarListHowManyCalEvents = 0; //available: 0 (= display all entries), 1, 2, ... // --------------------------------------------------------------------------------------- // If you encounter any bugs or errors, please contact me on discord: dreifachpunkt.#9954 // --------------------------------------------------------------------------------------- // ---------------------- NO NEED TO EDIT BELOW THIS LINE -------------------------------- // --------------------------------------------------------------------------------------- var version = "v0.1.10"; var accessibilityHighlightColor = 'style="background-color: rgba(255, 255, 255, 0.1);"'; var buttonClicked = false; var amountOfSoonExpiringLegsToBePlayed = 0; var advancedVersion = overviewAdvancedVersion; var calendarCompact = calendarAppearance === "compact" ? true : false; var legEndsRemaining = legEndsFormat === "remaining" ? true : false; var legEndsBoth = legEndsFormat === "both" ? true : false; var listFormatCalendar = startDesign === "calendar" ? true : false; var isCalWeek = calendarCompactType === "weeks" ? true : false; const timeStrOptions = { hour: "numeric", minute: "numeric", }; const dateStrOptionsCompactCal = { month: "numeric", day: "numeric", }; var oldHref = document.location.href; var tableRow = 0; window.onload = (event) => { leaguesBehavior(); var bodyList = document.querySelector("body"), observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (oldHref != document.location.href) { oldHref = document.location.href; leaguesBehavior(); } }); }); var config = { childList: true, subtree: true, }; observer.observe(bodyList, config); }; function leaguesBehavior() { if (location.pathname !== "/leagues" || location.pathname.endsWith("#")) { return false; } // check if the account still has old design if (document.getElementsByClassName("title-logo").length === 0) { var textInfo = document.createElement("div"); textInfo.setAttribute("id", "textInfo"); textInfo.innerHTML = 'Thank you for using the enhanced Pro Leagues script. <span style="color:red;"><b>Your account still has the old league page design, but the current version (' + version + ") of the script only supports the new dark mode design. Please use v0.1.8 until you have the new design. You can install the old version by copying the content of https://pastebin.com/cb06LGMZ into a new tampermonkey script.</b></span><br><br><hr><br><br>"; document .getElementsByClassName("title--medium")[0] .parentElement.insertBefore( textInfo, document.getElementsByClassName("title--medium")[0] ); } var style, table; if (!listFormatCalendar) { // table for overview mode style = "table.custom-table {\n" + " border: 1px solid #e2e2e2;\n" + " width: 100%;\n" + " background-color: rgba(102, 0, 255, 0.1);" + " border-collapse: collapse;\n" + " margin-bottom: 40px;\n" + " font-size: 12px;\n" + "}\n" + "\n" + "table.custom-table td, table.custom-table th {\n" + " padding: 5px 10px;\n" + " border: 1px solid #e2e2e2;\n" + "}\n" + "\n" + "table.custom-table th {\n" + " text-transform: uppercase;\n" + " font-size: 10px;\n" + " color: #FCFAF9;\n" + "}\n" + "\n" + "table.custom-table td:nth-child(3) {\n" + " max-width: 500px;\n" + " word-break: break-word;\n" + " text-align: center;\n" + "}\n" + "\n" + "table.custom-table td:nth-child(2) {\n" + " text-align: center;\n" + "}\n" + "\n" + "table.custom-table td:nth-child(1),\n" + "table.custom-table td:nth-child(4),\n" + "table.custom-table td:nth-child(5),\n" + "table.custom-table td:nth-child(6) {\n" + " text-align: center;\n" + "}\n" + (advancedVersion ? "table.custom-table td:nth-child(7),\n" + "table.custom-table td:nth-child(8) {\n" + " text-align: center;\n" + "}\n" : "") + "\n"; table = "<style>" + style + '</style><table class="custom-table" style="margin-bottom: 15px"><tr bgcolor="282828"><th>Creator</th><th>Name / Current map</th>' + (advancedVersion ? "<th>Mode</th><th>Leg duration</th>" : "") + "<th>Leg</th><th>Leg ends</th><th>played</th><th>Participants</th></tr>"; } else { if (!calendarCompact) { style = "table.custom-table {\n" + " border: 1px solid #e2e2e2;\n" + " width: 100%;\n" + " background-color: rgba(102, 0, 255, 0.1);" + " border-collapse: collapse;\n" + " margin-bottom: 40px;\n" + " font-size: 12px;\n" + "}\n" + "\n" + "table.custom-table td, table.custom-table th {\n" + " padding: 5px 10px;\n" + " border: 1px solid #e2e2e2;\n" + "}\n" + "\n" + "table.custom-table th {\n" + " text-transform: uppercase;\n" + " font-size: 10px;\n" + " color: #FCFAF9;\n" + "}\n" + "\n" + "table.custom-table td:nth-child(1),\n" + "table.custom-table td:nth-child(2),\n" + "table.custom-table td:nth-child(3),\n" + "table.custom-table td:nth-child(4),\n" + "table.custom-table td:nth-child(5) {\n" + " text-align: center;\n" + "}\n" + "\n"; table = "<style>" + style + '</style><table class="custom-table" style="margin-bottom: 15px"><tr bgcolor="282828"><th>Name</th><th>Map</th><th>Leg</th><th>Leg Ends</th><th>Played</th></tr>'; } else { style = "table.custom-table {\n" + " border: 1px solid #e2e2e2;\n" + " width: 100%;\n" + " background-color: rgba(102, 0, 255, 0.1);" + " border-collapse: collapse;\n" + " margin-bottom: 40px;\n" + " font-size: 12px;\n" + "}\n" + "\n" + "table.custom-table td, table.custom-table th {\n" + " padding: 5px 10px;\n" + " border: 1px solid #e2e2e2;\n" + "width: 14%;\n" + "}\n" + "\n" + "table.custom-table th {\n" + " text-transform: uppercase;\n" + " font-size: 10px;\n" + " color: #FCFAF9;\n" + "}\n" + "#theBestDiv {\n" + " width: 90%;\n" + " margin: auto;\n"; "}\n" + "\n" + "table.custom-table td:nth-child(1),\n" + "table.custom-table td:nth-child(2),\n" + "table.custom-table td:nth-child(3),\n" + "table.custom-table td:nth-child(4),\n" + "table.custom-table td:nth-child(5),\n" + "table.custom-table td:nth-child(6),\n" + "table.custom-table td:nth-child(7) {\n" + " text-align: center;\n" + "}\n" + "\n"; table = "<style>" + style + '</style><table class="custom-table" style="margin-bottom: 15px"><tr bgcolor="282828"><th>Mon</th><th>Tue</th><th>Wen</th><th>Thu</th><th>Fri</th><th>Sat</th><th>Sun</th></tr>'; } } var current = null; var page = 0; var dataBeforeSorting = []; var dataNotStarted = []; while (current === null || current % 10 === 0) { if (current === null) { current = 0; } var xmlhttp = new XMLHttpRequest(); xmlhttp.open( "GET", "https://www.geoguessr.com/api/v3/leagues/started?page=" + page, false ); xmlhttp.send(); if (xmlhttp.status === 200) { var data = JSON.parse(xmlhttp.responseText); current = current + data.length; if (data.length === 0) { current++; } page++; dataBeforeSorting.push(data); } else { alert("An error occured, while getting maps."); return false; } } current = null; page = 0; while (current === null || current % 10 === 0) { if (current === null) { current = 0; } var xmlhttp2 = new XMLHttpRequest(); xmlhttp2.open( "GET", "https://www.geoguessr.com/api/v3/leagues/notstarted?page=" + page, false ); xmlhttp2.send(); if (xmlhttp2.status === 200) { var data2 = JSON.parse(xmlhttp2.responseText); current = current + data2.length; if (data2.length === 0) { current++; } page++; dataNotStarted.push(data2); } else { alert("An error occured, while getting maps."); return false; } } //show finished leagues with the help of localStorage if (!buttonClicked) { let tokenListOfAllLeaguesInTheStorageToBeShown = []; let leaguesStorage = JSON.parse(localStorage.leaguesStorage || "{}"); var showHorizontalLine = false; var leagueHasFinishedInfo = document.createElement("div"); for (league in leaguesStorage) { if (leaguesStorage[league].expDate - Date.now() > 0) { //the league is in the localStorage, but not over yet continue; } showHorizontalLine = true; tokenListOfAllLeaguesInTheStorageToBeShown.push( leaguesStorage[league].token ); var aFinishedLeague = document.createElement("span"); aFinishedLeague.setAttribute("style", "color:red;"); var linkSpan = document.createElement("span"); linkSpan.setAttribute("id", leaguesStorage[league].token); linkSpan.innerHTML = '<b><a style="text-decoration: underline; color: red;" href="https://www.geoguessr.com/leagues/' + leaguesStorage[league].token + '">here</a></b>'; aFinishedLeague.innerHTML = "<b>" + leaguesStorage[league].trueExpDate + " at " + leaguesStorage[league].trueExpTime + ': The league "' + leaguesStorage[league].name + '" finished after ' + leaguesStorage[league].totalLegs + (leaguesStorage[league].totalLegs === 1 ? " leg" : " legs") + ". Click <b>"; aFinishedLeague.appendChild(linkSpan); aFinishedLeague.innerHTML += "<b> to see the results!</b><br><br>"; leagueHasFinishedInfo.appendChild(aFinishedLeague); console.log(leagueHasFinishedInfo); } if (showHorizontalLine) { leagueHasFinishedInfo.innerHTML += "<hr><br><br>"; } document .getElementsByClassName("title-logo")[0] .parentElement.insertBefore( leagueHasFinishedInfo, document.getElementsByClassName("title-logo")[0] ); tokenListOfAllLeaguesInTheStorageToBeShown.forEach((el) => { document.getElementById(el).addEventListener( "click", () => { deleteStorageEntry(el); }, false ); }); } //show script info if (!hideInfo && !buttonClicked) { var textInfo = document.createElement("div"); textInfo.setAttribute("id", "textInfo"); textInfo.innerHTML = 'Thank you for using the enhanced Pro Leagues script. <span style="color:red;"><b>To hide this info text, visit the settings at the top of the script and set the variable "hideInfo" to true.</b></span> There you will also find all the customization options.<br><br>New in ' + version + ":<br>- Adapts to changed API results of finished current legs.<br><br><hr><br><br>"; document .getElementsByClassName("title-logo")[0] .parentElement.insertBefore( textInfo, document.getElementsByClassName("title-logo")[0] ); } if (!listFormatCalendar) { document.getElementsByClassName("title-logo")[0].innerText = "Pro Leagues Enhanced"; table += getHtmlOverview(dataBeforeSorting); table += getHtmlNotStarted(dataNotStarted); document.title = "Pro Leagues Enhanced" + (amountOfSoonExpiringLegsToBePlayed > 0 ? " (" + amountOfSoonExpiringLegsToBePlayed + ")" : ""); } else { document.getElementsByClassName("title-logo")[0].innerText = "Pro Leagues Calendar"; table += getHtmlCalendar(dataBeforeSorting); if (!calendarCompact) { table += getHtmlNotStarted(dataNotStarted); } document.title = "Pro Leagues Calendar" + (amountOfSoonExpiringLegsToBePlayed > 0 ? " (" + amountOfSoonExpiringLegsToBePlayed + ")" : ""); } table += '</table><div style="margin-bottom: 15px; color:#ACAAA9; font-size: 12px; float: right">by dreifachpunkt. | ' + version + "</div>"; table += '<input type="button" id="btnChangeDesign" value="' + (listFormatCalendar ? "Go to Overview" : "Go to Calendar") + '" class="button button--small button--primary" style="margin-bottom: 15px; float: left"/>'; table += listFormatCalendar && calendarCompact ? ' <input type="button" id="btnChangeCalendar" value="' + (isCalWeek ? "Show Month" : "Show 2 Weeks") + '" class="button button--small button--secondary" style="margin-bottom: 15px; float: left; margin-left: 10px"/>' : ""; var div = document.createElement("div"); div.setAttribute("id", "theBestDiv"); div.innerHTML = table; //div.setAttribute("style", "width=90%;"); if (arialFont) { div.style.fontFamily = "Arial, sans-serif"; } var elements = document.getElementsByClassName("container__content"); var oldDiv = document.getElementById("theBestDiv"); if (!buttonClicked) { elements[0].parentNode.insertBefore(div, elements[0]); elements[0].parentNode.removeChild(elements[0]); // elements[0].innerHTML = ""; } else { if (oldDiv !== null) { oldDiv.parentNode.replaceChild(div, oldDiv); } else { elements[0].parentNode.insertBefore(div, elements[0]); elements[0].parentNode.removeChild(elements[0]); //elements[0].innerHTML = ""; } } document .getElementById("btnChangeDesign") .addEventListener("click", changeDesign, false); if (document.getElementById("btnChangeCalendar") != null) { document .getElementById("btnChangeCalendar") .addEventListener("click", changeCalendar, false); } } function changeDesign() { listFormatCalendar = listFormatCalendar ? false : true; tableRow = 0; buttonClicked = true; amountOfSoonExpiringLegsToBePlayed = 0; leaguesBehavior(); } function changeCalendar() { isCalWeek = isCalWeek ? false : true; tableRow = 0; buttonClicked = true; amountOfSoonExpiringLegsToBePlayed = 0; leaguesBehavior(); } function addStorageEntry( token, name, totalLegs, expDate, trueExpDate, trueExpTime ) { let leaguesStorage = JSON.parse(localStorage.leaguesStorage || "{}"); leaguesStorage[token] = { name, totalLegs, expDate, trueExpDate, trueExpTime, token, }; localStorage.leaguesStorage = JSON.stringify(leaguesStorage); } function deleteStorageEntry(token) { console.log("delete entry"); let leaguesStorage = JSON.parse(localStorage.leaguesStorage || "{}"); delete leaguesStorage[token]; localStorage.leaguesStorage = JSON.stringify(leaguesStorage); } function getHtmlOverview(data) { var output = ""; var dataSorted = []; // combine multiple arrays for (let arr in data) { for (let objInner in data[arr]) { if (data[arr][objInner].league.currentLeg != null) { //if the league is ending and the last scoring is awaited, the status is still "started" but the currentLeg is null dataSorted.push(data[arr][objInner]); } } } // sort by current leg ending for (var i = 0; i < dataSorted.length; i++) { for (var j = 0; j < dataSorted.length - 1; j++) { if ( dataSorted[j].league.currentLeg.endsAt > dataSorted[j + 1].league.currentLeg.endsAt ) { var temp = dataSorted[j]; dataSorted[j] = dataSorted[j + 1]; dataSorted[j + 1] = temp; } } } var now = Date.now(); // returns millis for (let obj in dataSorted) { var league = dataSorted[obj]; // skip hidden leagues var isHidden = false; for (i = 0; i < hiddenLeagues.length; i++) { if (league.league.token === hiddenLeagues[i]) { isHidden = true; } } if (isHidden) { continue; } // modify league object to have necessary information var startDate = Date.parse(league.league.currentLeg.startsAt); var expDate = Date.parse(league.league.currentLeg.endsAt); league.league.currentLeg.duration = getDuration(startDate, expDate)[0] + " " + getDuration(startDate, expDate)[1]; league.league.currentLeg.remainingTime = getDuration(now, expDate)[0] + " " + getDuration(now, expDate)[1]; var trueExpDate = new Date(expDate); league.league.currentLeg.trueExpDate = trueExpDate.toLocaleDateString(); league.league.currentLeg.trueExpTime = trueExpDate.toLocaleTimeString( undefined, timeStrOptions ); league.league.currentLeg.nextLegSoon = (expDate - now) / 1000 / 60 / 60 < 24; if ( league.league.currentLeg.nextLegSoon && !league.hasUserFinishedCurrentLeg.result ) { amountOfSoonExpiringLegsToBePlayed++; } tableRow++; var currentChallenge = {}; if (advancedVersion) { var xmlhttpLeagueSpecific = new XMLHttpRequest(); xmlhttpLeagueSpecific.open( "GET", "https://www.geoguessr.com/api/v3/challenges/" + league.league.currentLeg.challengeId, false ); xmlhttpLeagueSpecific.send(); if (xmlhttpLeagueSpecific.status === 200) { var dataLeagueSpecific = JSON.parse(xmlhttpLeagueSpecific.responseText); currentChallenge = dataLeagueSpecific; } else { alert("An error occured, while getting a specific challenge."); return false; } } // wenn letztes Leg: erstelle Entry in localStorage, damit League angezeigt wird wenn sie endet if (league.league.totalLegs === league.league.currentLeg.legNumber) { addStorageEntry( league.league.token, league.league.name, league.league.totalLegs, expDate, league.league.currentLeg.trueExpDate, league.league.currentLeg.trueExpTime ); } output += getOutputStrOverview(league, currentChallenge); } return output; } function getHtmlNotStarted(data) { var output = ""; var dataSorted = []; for (let arr in data) { for (let objInner in data[arr]) { dataSorted.push(data[arr][objInner]); } } // check whether there exists a league that is not hidden var existsLeagueThatIsNotHidden = false; for (let obj in dataSorted) { var league = dataSorted[obj]; if (!hiddenLeagues.includes(league.league.token)) { existsLeagueThatIsNotHidden = true; } } if (dataSorted.length > 0 && existsLeagueThatIsNotHidden) { tableRow++; if (!listFormatCalendar) { output += "<tr " + (tableRow % 2 === 0 ? 'style="background-color: rgba(255, 255, 255, 0.1);"' : "") + "><td></td><td></td><td></td><td></td>" + (advancedVersion ? "<td></td><td></td>" : "") + "<td></td><td></td></tr>"; } else { output += "<tr " + (tableRow % 2 === 0 ? 'style="background-color: rgba(255, 255, 255, 0.1);"' : "") + "><td></td><td></td><td></td><td></td><td></td></tr>"; } } for (var i = 0; i < dataSorted.length; i++) { for (var j = 0; j < dataSorted.length - 1; j++) { if ( dataSorted[j].league.numberOfParticipants < dataSorted[j + 1].league.numberOfParticipants ) { var temp = dataSorted[j]; dataSorted[j] = dataSorted[j + 1]; dataSorted[j + 1] = temp; } } } for (let obj in dataSorted) { var league = dataSorted[obj]; var isHidden = false; for (i = 0; i < hiddenLeagues.length; i++) { if (league.league.token === hiddenLeagues[i]) { isHidden = true; } } if (isHidden) { continue; } tableRow++; output += getOutputStrNotStarted(league); } return output; } //this function also returns the output string of the compact calendar function getHtmlCalendar(data) { var output = ""; var dataSorted = []; for (let arr in data) { for (let objInner in data[arr]) { if (data[arr][objInner].league.currentLeg != null) { // league is ending dataSorted.push(data[arr][objInner]); } } } var now = Date.now(); // returns millis var additionalArtificialLeagues = []; var additionalArtificialPreviousLeagues = []; for (var it = 0; it < dataSorted.length; it++) { var league = dataSorted[it]; league.league.currentLeg.endsAt = Date.parse( league.league.currentLeg.endsAt ); league.league.currentLeg.startsAt = Date.parse( league.league.currentLeg.startsAt ); var durationMillis = league.league.currentLeg.endsAt - league.league.currentLeg.startsAt; league.league.currentLeg.remainingTime = getDuration(now, league.league.currentLeg.endsAt)[0] + " " + getDuration(now, league.league.currentLeg.endsAt)[1]; var trueExpDateOriginal = new Date(league.league.currentLeg.endsAt); league.league.currentLeg.trueExpDate = trueExpDateOriginal.toLocaleDateString(); league.league.currentLeg.trueExpTime = trueExpDateOriginal.toLocaleTimeString(undefined, timeStrOptions); league.league.currentLeg.hasStarted = true; league.league.currentLeg.nextLegSoon = (league.league.currentLeg.endsAt - now) / 1000 / 60 / 60 < 24; if ( league.league.currentLeg.nextLegSoon && !league.hasUserFinishedCurrentLeg.result ) { amountOfSoonExpiringLegsToBePlayed++; } if (league.league.totalLegs === league.league.currentLeg.legNumber) { addStorageEntry( league.league.token, league.league.name, league.league.totalLegs, league.league.currentLeg.endsAt, league.league.currentLeg.trueExpDate, league.league.currentLeg.trueExpTime ); } //calculate future legs of a league by creating additional leagues with the //current leg ending in the future for ( var legNr = league.league.currentLeg.legNumber; legNr < league.league.totalLegs; legNr++ ) { var artificialLeague = {}; artificialLeague.hasUserFinishedCurrentLeg = {}; artificialLeague.hasUserFinishedCurrentLeg.result = false; artificialLeague.league = {}; artificialLeague.league.name = league.league.name; artificialLeague.league.token = league.league.token; artificialLeague.league.currentLeg = {}; artificialLeague.league.currentLeg.mapSlug = ""; artificialLeague.league.currentLeg.hasStarted = false; artificialLeague.league.currentLeg.mapName = "t.b.a."; artificialLeague.league.currentLeg.legNumber = legNr + 1; artificialLeague.league.totalLegs = league.league.totalLegs; artificialLeague.league.currentLeg.endsAt = league.league.currentLeg.endsAt + (legNr - league.league.currentLeg.legNumber + 1) * durationMillis; artificialLeague.league.currentLeg.nextLegSoon = (artificialLeague.league.currentLeg.endsAt - now) / 1000 / 60 / 60 < 24; artificialLeague.league.currentLeg.remainingTime = getDuration(now, artificialLeague.league.currentLeg.endsAt)[0] + " " + getDuration(now, artificialLeague.league.currentLeg.endsAt)[1]; var trueExpDate = new Date(artificialLeague.league.currentLeg.endsAt); artificialLeague.league.currentLeg.trueExpDate = trueExpDate.toLocaleDateString(); artificialLeague.league.currentLeg.trueExpTime = trueExpDate.toLocaleTimeString(undefined, timeStrOptions); additionalArtificialLeagues.push(artificialLeague); } //calculate past legs of a league by creating additional leagues with the //current leg ending in the past if (calendarCompact) { for (legNr = league.league.currentLeg.legNumber; legNr > 1; legNr--) { artificialLeague = {}; artificialLeague.hasUserFinishedCurrentLeg = {}; artificialLeague.hasUserFinishedCurrentLeg.result = true; artificialLeague.league = {}; artificialLeague.league.name = league.league.name; artificialLeague.league.token = league.league.token; artificialLeague.league.currentLeg = {}; artificialLeague.league.currentLeg.mapSlug = ""; artificialLeague.league.currentLeg.hasStarted = false; artificialLeague.league.currentLeg.mapName = "Leg has already finished"; artificialLeague.league.currentLeg.legNumber = legNr - 1; artificialLeague.league.totalLegs = league.league.totalLegs; artificialLeague.league.currentLeg.endsAt = league.league.currentLeg.endsAt + (legNr - league.league.currentLeg.legNumber - 1) * durationMillis; artificialLeague.league.currentLeg.nextLegSoon = (artificialLeague.league.currentLeg.endsAt - now) / 1000 / 60 / 60 < 24; artificialLeague.league.currentLeg.remainingTime = "Leg has already finished"; trueExpDate = new Date(artificialLeague.league.currentLeg.endsAt); artificialLeague.league.currentLeg.trueExpDate = trueExpDate.toLocaleDateString(undefined, dateStrOptionsCompactCal); artificialLeague.league.currentLeg.trueExpTime = trueExpDate.toLocaleTimeString(undefined, timeStrOptions); additionalArtificialLeagues.push(artificialLeague); } } } for (var ite = 0; ite < additionalArtificialLeagues.length; ite++) { dataSorted.push(additionalArtificialLeagues[ite]); } for (var i = 0; i < dataSorted.length; i++) { for (var j = 0; j < dataSorted.length - 1; j++) { if ( dataSorted[j].league.currentLeg.endsAt > dataSorted[j + 1].league.currentLeg.endsAt ) { var temp = dataSorted[j]; dataSorted[j] = dataSorted[j + 1]; dataSorted[j + 1] = temp; } } } if (!calendarCompact) { for (let obj in dataSorted) { var leag = dataSorted[obj]; if ( tableRow >= calendarListHowManyCalEvents && calendarListHowManyCalEvents !== 0 ) { break; } var isHidden = false; for (i = 0; i < hiddenLeagues.length; i++) { if (leag.league.token === hiddenLeagues[i]) { isHidden = true; } } if (isHidden) { continue; } output += getOutputStrCalendarList(leag); } } else { //here comes the big mess :D if (dataSorted.length === 0) { return ""; } var tempOut = ""; var arrOfArrs = []; var tempArr = []; var dayMillisIter = getMillisOfDateStart( dataSorted[0].league.currentLeg.endsAt ); i = 0; while (i <= dataSorted.length) { if (i >= dataSorted.length) { arrOfArrs.push(tempArr); tempArr = []; break; } isHidden = false; for (j = 0; j < hiddenLeagues.length && i < dataSorted.length; j++) { if (dataSorted[i].league.token === hiddenLeagues[j]) { isHidden = true; } } if (isHidden) { i++; continue; } if (tempArr.length === 0) { tempArr.push(dayMillisIter); dayMillisIter = getNextDayStartFromCurrentDateStart(dayMillisIter); } if ( tempArr[0] === getMillisOfDateStart(dataSorted[i].league.currentLeg.endsAt) ) { tempArr.push(dataSorted[i]); i++; } else { arrOfArrs.push(tempArr); tempArr = []; } } if (arrOfArrs.length > 1) { var firstArrSpot = arrOfArrs[0]; var tempTime = firstArrSpot[0]; for (i = 0; i < 42; i++) { tempTime = getPreviousDayStartFromCurrentDateStart(tempTime); arrOfArrs.unshift([tempTime]); } } var firstField = !isCalWeek ? new Date(getFirstDayOfWeek(getFirstDayOfMonth(Date.now()))) : new Date(getFirstDayOfWeek(Date.now())); var current = 0; while (arrOfArrs[current][0] < firstField) { current++; if (current === arrOfArrs.length) { return ""; } } var dayOfWeek = 0; var amountOfDays = isCalWeek ? 14 : 42; for (i = current; i < current + amountOfDays; i++) { if (dayOfWeek === 7) { dayOfWeek = 0; } if (dayOfWeek === 0) { output += '<tr style="background-color: rgba(255, 255, 255, 0.1);">'; tempOut += "<tr>"; } if (arrOfArrs[i] == undefined || arrOfArrs[i].length == 0) { output += "<td></td>"; tempOut += "<td>"; } else { output += "<td " + (getMillisOfDateStart(Date.now()) === arrOfArrs[i][0] ? 'style="background-color: rgba(255, 255, 255, 0.3);"' : " ") + ">" + new Date(arrOfArrs[i][0]).toLocaleDateString() + "</td>"; tempOut += '<td style="text-align: left; vertical-align: top;' + (getMillisOfDateStart(Date.now()) === arrOfArrs[i][0] ? "background-color: rgba(255, 255, 255, 0.3);" : !isCalWeek ? new Date(arrOfArrs[i][0]).getMonth() !== new Date(Date.now()).getMonth() ? "background-color: rgba(255, 255, 255, 0.1);" : "" : "") + ' ">'; for (j = 1; j < arrOfArrs[i].length; j++) { tempOut += '<b style="display: inline-flex;">' + arrOfArrs[i][j].league.currentLeg.trueExpTime + "</b> " + (arrOfArrs[i][j].league.currentLeg.legNumber === arrOfArrs[i][j].league.totalLegs ? '<span style="color:DeepSkyBlue;"><b><i>LAST LEG</i></b> </span>' : "") + (isCalWeek ? "<br>" : "") + '<a style="font-size: 10px;display: inline-flex; color: ' + (arrOfArrs[i][j].hasUserFinishedCurrentLeg.result ? "white" : "red") + ";" + (arrOfArrs[i][j].league.currentLeg.hasStarted ? "text-decoration: underline" : "") + ';" href="https://www.geoguessr.com/leagues/' + arrOfArrs[i][j].league.token + '">' + arrOfArrs[i][j].league.name + "</a><br>"; } } tempOut += "</td>"; if (dayOfWeek === 6) { output += "</tr>"; tempOut += "</tr>"; output += tempOut; tempOut = ""; } dayOfWeek++; } } return output; } function getOutputStrOverview(league, currentChallenge) { // if normal overview, currentChallenge is {} var out = '<div class="tablerowdiv" ><tr ' + (tableRow % 2 === 0 ? 'style="background-color: rgba(255, 255, 255, 0.1);"' : "") + "><td>" + getTableLeagueCreator(league) + "</td><td>" + getTableLeagueName(league) + "<br>" + getTableLeagueCurrMap(league) + "</td>" + (advancedVersion ? "<td>" + getTableLeagueMode(currentChallenge) + "</td><td>" + getTableLeagueDuration(league) + "</td>" : "") + "<td>" + getTableLeagueLeg(league) + "</td>" + "<td " + (league.league.currentLeg.nextLegSoon && !league.hasUserFinishedCurrentLeg.result ? 'style="color:red;">' : ">") + getTableLeagueLegEnds(league) + "</td>" + "<td>" + getTableLeaguePlayed(league) + "</td>" + "<td>" + getTableLeagueParticipants(league, currentChallenge) + "</td>" + "</tr></div>"; return out; } function getOutputStrCalendarList(league) { var output = ""; tableRow++; output += "<tr " + (tableRow % 2 === 0 ? 'style="background-color: rgba(255, 255, 255, 0.1);"' : "") + ">" + "<td>" + getTableLeagueName(league) + "</td>" + "<td>" + (league.league.currentLeg.hasStarted ? '<a style="color:white;" href="https://www.geoguessr.com/maps/' + league.league.currentLeg.mapSlug + '">' : "") + league.league.currentLeg.mapName + (league.league.currentLeg.hasStarted ? "</a>" : "") + "</td>" + "<td>" + getTableLeagueLeg(league) + "</td>" + "<td " + (league.league.currentLeg.nextLegSoon && !league.hasUserFinishedCurrentLeg.result ? 'style="color:red;">' : ">") + getTableLeagueLegEnds(league) + "</td>" + "<td>" + getTableLeaguePlayed(league) + "</td>" + "</tr>"; return output; } function getOutputStrNotStarted(league) { var out = ""; if (!listFormatCalendar) { out += "<tr " + (tableRow % 2 === 0 ? 'style="background-color: rgba(255, 255, 255, 0.1);"' : "") + ">" + "<td>" + getTableLeagueCreator(league) + "</td>" + "<td>" + getTableLeagueName(league) + "</td>" + (advancedVersion ? "<td></td><td></td>" : "") + "<td>0 of " + league.league.totalLegs + "</td>" + "<td>League has not started yet</td>" + "<td>" + symbolNotAvailable + "</td>" + "<td>" + league.league.numberOfParticipants + "</td>" + "</tr>"; } else { out += "<tr " + (tableRow % 2 === 0 ? 'style="background-color: rgba(255, 255, 255, 0.1);"' : "") + ">" + "<td>" + getTableLeagueName(league) + "</td>" + "<td></td>" + "<td>0 of " + league.league.totalLegs + "</td>" + "<td>League has not started yet</td>" + "<td>" + symbolNotAvailable + "</td>" + "</tr>"; } return out; } function getTableLeagueCreator(league) { return ( '<a href="https://www.geoguessr.com' + league.league.creator.url + '" style="color: white;">' + league.league.creator.nick + "</a>" ); } function getTableLeagueName(league) { return ( '<b><a href="https://www.geoguessr.com/leagues/' + league.league.token + '" style="text-decoration: underline;color: white;">' + league.league.name + "</a></b>" ); } function getTableLeagueCurrMap(league) { return ( '<a href="https://www.geoguessr.com/maps/' + league.league.currentLeg.mapSlug + '" style="color: white;">' + league.league.currentLeg.mapName + "</a>" ); } function getTableLeagueMode(currentChallenge) { return ( (currentChallenge.challenge.timeLimit > 60 ? +( Math.round(currentChallenge.challenge.timeLimit / 60 + "e+2") + "e-2" ) + " mins per round" : currentChallenge.challenge.timeLimit === 0 ? "no time limit" : currentChallenge.challenge.timeLimit + " secs per round") + (currentChallenge.challenge.forbidMoving || currentChallenge.challenge.forbidZooming || currentChallenge.challenge.forbidRotating ? " (N" : "") + (currentChallenge.challenge.forbidMoving ? "M" : "") + (currentChallenge.challenge.forbidRotating ? "P" : "") + (currentChallenge.challenge.forbidZooming ? "Z" : "") + (currentChallenge.challenge.forbidMoving || currentChallenge.challenge.forbidZooming || currentChallenge.challenge.forbidRotating ? ")" : "") ); } function getTableLeagueDuration(league) { return league.league.currentLeg.duration; } function getTableLeagueLeg(league) { return ( league.league.currentLeg.legNumber + " of " + league.league.totalLegs + (league.league.currentLeg.legNumber === league.league.totalLegs ? '<br><span style="color:DeepSkyBlue;"><b><i>LAST LEG</i></b></span>' : "") ); } function getTableLeagueLegEnds(league) { return ( (!legEndsRemaining || legEndsBoth ? league.league.currentLeg.trueExpDate + " at " + league.league.currentLeg.trueExpTime : "") + (legEndsBoth ? "<br>" : "") + (legEndsRemaining || legEndsBoth ? league.league.currentLeg.remainingTime : "") ); } function getTableLeaguePlayed(league) { var startLinkString = '<a href="https://www.geoguessr.com/challenge/'; return !listFormatCalendar ? league.hasUserFinishedCurrentLeg.result ? startLinkString + league.league.currentLeg.challengeId + '">' + symbolPlayed + "</a>" : startLinkString + league.league.currentLeg.challengeId + '">' + symbolNotPlayed + "</a>" : league.league.currentLeg.hasStarted ? league.hasUserFinishedCurrentLeg.result ? startLinkString + league.league.currentLeg.challengeId + '">' + symbolPlayed + "</a>" : startLinkString + league.league.currentLeg.challengeId + '">' + symbolNotPlayed + "</a>" : symbolNotAvailable; } function getTableLeagueParticipants(league, currentChallenge) { return advancedVersion ? currentChallenge.challenge.numberOfParticipants + (league.hasUserFinishedCurrentLeg.result ? true : false) + " of " + league.league.numberOfParticipants + " played" : league.league.numberOfParticipants; } function getFirstDayOfWeek(dateMillis) { var dayOfWeek = new Date(dateMillis).getDay() === 0 ? 7 : new Date(dateMillis).getDay(); var retVal = getMillisOfDateStart(dateMillis); while (dayOfWeek > 1) { retVal = getPreviousDayStartFromCurrentDateStart(retVal); dayOfWeek--; } return retVal; } function getFirstDayOfMonth(dateMillis) { var dayOfMonth = new Date(dateMillis).getDate(); var retVal = getMillisOfDateStart(dateMillis); while (dayOfMonth > 1) { retVal = getPreviousDayStartFromCurrentDateStart(retVal); dayOfMonth--; } return retVal; } function getDayMillis() { return 1 * 1000 * 60 * 60 * 24; } function getPreviousDayStartFromCurrentDateStart(dateMillis) { return getMillisOfDateStart(dateMillis - 1 * 1000 * 60 * 60 * 22); } function getNextDayStartFromCurrentDateStart(dateMillis) { return getMillisOfDateStart(dateMillis + 1 * 1000 * 60 * 60 * 26); } function getMillisOfDateStart(dateMillis) { var date = new Date(dateMillis); var dateStart = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0 ); return Date.parse(dateStart); } function getDuration(startDate, endDate) { //both in millis var durationHours = (endDate - startDate) / 1000 / 60 / 60; var retArray = [0, ""]; switch (true) { case durationHours < 1: retArray[0] = Math.floor(durationHours * 60); retArray[1] = "minutes"; break; case durationHours === 1: retArray[0] = durationHours; retArray[1] = "hour"; break; case durationHours < 24: retArray[0] = Math.floor(durationHours); retArray[1] = Math.floor(durationHours) === 1 ? "hour" : "hours"; break; case durationHours >= 24 && durationHours < 48: retArray[0] = Math.floor(durationHours / 24); retArray[1] = "day"; break; case durationHours > 24 && durationHours < 24 * 7: default: retArray[0] = Math.floor(durationHours / 24); retArray[1] = "days"; break; case durationHours >= 24 * 7 && durationHours < 24 * 7 * 2: retArray[0] = Math.floor(durationHours / 24 / 7); retArray[1] = "week"; break; case durationHours > 24 * 7: retArray[0] = Math.floor(durationHours / 24 / 7); retArray[1] = "weeks"; break; } return retArray; }