dreifachpunkt

League List Enhanced v0.1.8

Dec 12th, 2022 (edited)
191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 41.13 KB | Gaming | 0 0
  1. // ==UserScript==
  2. // @name GeoGuessr League List Enhanced
  3. // @namespace    dreifachpunkt.
  4. // @author dreifachpunkt.
  5. // @description Display all your leagues in a table or calendar, ordered by the next leg's expiration date. Code Framework by Kommu.
  6. // @version 0.1.8
  7. // @include https://www.geoguessr.com/*
  8. // @run-at document-start
  9. // @license MIT
  10. // @updateURL https://openuserjs.org/meta/dreifachpunkt./GeoGuessr_League_List_Enhanced.meta.js
  11. // ==/UserScript==
  12.  
  13. /*jshint esversion: 6 */
  14.  
  15. // ------------------------------- IMPORTANT ---------------------------------------------
  16.  
  17. // THIS IS AN OLD VERSION OF THE SCRIPT. IF YOU HAVE THE NEW DARK MODE DESIGN OF THE
  18. // LEAGUE PAGE, PLEASE UPDATE:
  19. // https://openuserjs.org/scripts/dreifachpunkt./GeoGuessr_League_List_Enhanced
  20.  
  21. // -------------------------------- SETTINGS ---------------------------------------------
  22.  
  23. // Hide the information on the top of the Pro Leagues page.
  24. var hideInfo = false; //available: true, false
  25.  
  26. // This script features two different designs:
  27. // (1) An OVERVIEW design in which each league only appears once and
  28. // (2) a CALENDAR design in which every upcoming leg of each league is shown.
  29. // You can set the design to be displayed first when you load the page.
  30. var startDesign = "overview"; //available: overview, calendar
  31.  
  32. // You can select whether the "leg ends" column shows the expiration date, the remaining
  33. // time or both (not for the compact calendar).
  34. var legEndsFormat = "both"; //available: date, remaining, both
  35.  
  36. // Did you join a league but then decide that you do not want to play it? With this feature
  37. // you can hide leagues. Click on the league and copy the last part of the URL into the
  38. // array.
  39. var hiddenLeagues = [];
  40. //e.g. ["2DAgdm62UtJcleXj", "DkgnJKbEvKaya67s"] for the Diverse World and World League 2021
  41.  
  42. // You can choose your preferred symbols or text for the different types of league statuses.
  43. // E.g. use one of these symbols: ⚫⚪🔴🟠🟡🟢🔵🟣🟤⭕❌✅❎.
  44. var symbolPlayed = "🟢";
  45. var symbolNotPlayed = "🔴";
  46. var symbolNotAvailable = "⚪";
  47.  
  48. // For those who have difficulties reading the new font, you can change it to Arial.
  49. var arialFont = false; //available: true, false
  50.  
  51. // ------------------------- DESIGN-SPECIFIC SETTINGS ------------------------------------
  52.  
  53. // For the OVERVIEW, you can activate an advanced version to display additional
  54. // information:
  55. // (1) The time limit and game mode (moving, NM, NMPZ, etc.),
  56. // (2) the duration of a leg and
  57. // (3) the amount of players who already played the leg.
  58. // The advanced overview takes a bit longer to load if you have many leagues.
  59. // Also for smaller screen sizes (e.g. laptops) I do not recommend the advanced version.
  60. var overviewAdvancedVersion = false; //available: true, false
  61.  
  62. // For the CALENDAR, you can choose between a compact and a list appearance.
  63. // The compact calendar does not show leagues that are open for registration.
  64. var calendarAppearance = "compact"; //available: compact, list
  65.  
  66. // For the COMPACT CALENDAR, you can display the current month or the upcoming two weeks.
  67. var calendarCompactType = "weeks"; //available: weeks, month
  68.  
  69. // For the LIST CALENDAR, you can set the amount of entries to be shown in the table.
  70. var calendarListHowManyCalEvents = 0; //available: 0 (= display all entries), 1, 2, ...
  71.  
  72. // ---------------------------------------------------------------------------------------
  73. // If you encounter any bugs or errors, please contact me on discord: dreifachpunkt.#9954
  74. // ---------------------------------------------------------------------------------------
  75. // ---------------------- NO NEED TO EDIT BELOW THIS LINE --------------------------------
  76. // ---------------------------------------------------------------------------------------
  77.  
  78. var version = "v0.1.8";
  79. var buttonClicked = false;
  80. var amountOfSoonExpiringLegsToBePlayed = 0;
  81. var advancedVersion = overviewAdvancedVersion;
  82. var calendarCompact = calendarAppearance === "compact" ? true : false;
  83. var legEndsRemaining = legEndsFormat === "remaining" ? true : false;
  84. var legEndsBoth = legEndsFormat === "both" ? true : false;
  85. var listFormatCalendar = startDesign === "calendar" ? true : false;
  86. var isCalWeek = calendarCompactType === "weeks" ? true : false;
  87. const timeStrOptions = {
  88.   hour: "numeric",
  89.   minute: "numeric",
  90. };
  91. const dateStrOptionsCompactCal = {
  92.   month: "numeric",
  93.   day: "numeric",
  94. };
  95.  
  96. var oldHref = document.location.href;
  97. var tableRow = 0;
  98.  
  99. window.onload = (event) => {
  100.   leaguesBehavior();
  101.  
  102.   var bodyList = document.querySelector("body"),
  103.     observer = new MutationObserver(function (mutations) {
  104.       mutations.forEach(function (mutation) {
  105.         if (oldHref != document.location.href) {
  106.           oldHref = document.location.href;
  107.           leaguesBehavior();
  108.         }
  109.       });
  110.     });
  111.  
  112.   var config = {
  113.     childList: true,
  114.     subtree: true,
  115.   };
  116.  
  117.   observer.observe(bodyList, config);
  118. };
  119.  
  120. function leaguesBehavior() {
  121.   if (location.pathname !== "/leagues" || location.pathname.endsWith("#")) {
  122.     return false;
  123.   }
  124.  
  125.   var style, table;
  126.  
  127.   if (!listFormatCalendar) {
  128.     //table for overview mode
  129.     style =
  130.       "table.custom-table {\n" +
  131.       "    border: 1px solid #e2e2e2;\n" +
  132.       "    width: 100%;\n" +
  133.       "    border-collapse: collapse;\n" +
  134.       "    margin-bottom: 40px;\n" +
  135.       "        font-size: 12px;\n" +
  136.       "}\n" +
  137.       "\n" +
  138.       "table.custom-table td, table.custom-table th {\n" +
  139.       "    padding: 5px 10px;\n" +
  140.       "    border: 1px solid #e2e2e2;\n" +
  141.       "}\n" +
  142.       "\n" +
  143.       "table.custom-table th {\n" +
  144.       "    text-transform: uppercase;\n" +
  145.       "    font-size: 10px;\n" +
  146.       "    color: #FCFAF9;\n" +
  147.       "}\n" +
  148.       "\n" +
  149.       "table.custom-table td:nth-child(3) {\n" +
  150.       "    max-width: 500px;\n" +
  151.       "    word-break: break-word;\n" +
  152.       "    text-align: center;\n" +
  153.       "}\n" +
  154.       "\n" +
  155.       "table.custom-table td:nth-child(2) {\n" +
  156.       "    text-align: center;\n" +
  157.       "}\n" +
  158.       "\n" +
  159.       "table.custom-table td:nth-child(1),\n" +
  160.       "table.custom-table td:nth-child(4),\n" +
  161.       "table.custom-table td:nth-child(5),\n" +
  162.       "table.custom-table td:nth-child(6) {\n" +
  163.       "    text-align: center;\n" +
  164.       "}\n" +
  165.       (advancedVersion
  166.         ? "table.custom-table td:nth-child(7),\n" +
  167.           "table.custom-table td:nth-child(8) {\n" +
  168.           "    text-align: center;\n" +
  169.           "}\n"
  170.         : "") +
  171.       "\n";
  172.     table =
  173.       "<style>" +
  174.       style +
  175.       '</style><table class="custom-table" style="margin-bottom: 15px"><tr bgcolor="282828"><th>Creator</th><th>Name / Current map</th>' +
  176.       (advancedVersion ? "<th>Mode</th><th>Leg duration</th>" : "") +
  177.       "<th>Leg</th><th>Leg ends</th><th>played</th><th>Participants</th></tr>";
  178.   } else {
  179.     if (!calendarCompact) {
  180.       style =
  181.         "table.custom-table {\n" +
  182.         "    border: 1px solid #e2e2e2;\n" +
  183.         "    width: 100%;\n" +
  184.         "    border-collapse: collapse;\n" +
  185.         "    margin-bottom: 40px;\n" +
  186.         "        font-size: 12px;\n" +
  187.         "}\n" +
  188.         "\n" +
  189.         "table.custom-table td, table.custom-table th {\n" +
  190.         "    padding: 5px 10px;\n" +
  191.         "    border: 1px solid #e2e2e2;\n" +
  192.         "}\n" +
  193.         "\n" +
  194.         "table.custom-table th {\n" +
  195.         "    text-transform: uppercase;\n" +
  196.         "    font-size: 10px;\n" +
  197.         "    color: #FCFAF9;\n" +
  198.         "}\n" +
  199.         "\n" +
  200.         "table.custom-table td:nth-child(1),\n" +
  201.         "table.custom-table td:nth-child(2),\n" +
  202.         "table.custom-table td:nth-child(3),\n" +
  203.         "table.custom-table td:nth-child(4),\n" +
  204.         "table.custom-table td:nth-child(5) {\n" +
  205.         "    text-align: center;\n" +
  206.         "}\n" +
  207.         "\n";
  208.       table =
  209.         "<style>" +
  210.         style +
  211.         '</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>';
  212.     } else {
  213.       style =
  214.         "table.custom-table {\n" +
  215.         "    border: 1px solid #e2e2e2;\n" +
  216.         "    width: 100%;\n" +
  217.         "    border-collapse: collapse;\n" +
  218.         "    margin-bottom: 40px;\n" +
  219.         "        font-size: 12px;\n" +
  220.         "}\n" +
  221.         "\n" +
  222.         "table.custom-table td, table.custom-table th {\n" +
  223.         "    padding: 5px 10px;\n" +
  224.         "    border: 1px solid #e2e2e2;\n" +
  225.         "width: 14%;\n" +
  226.         "}\n" +
  227.         "\n" +
  228.         "table.custom-table th {\n" +
  229.         "    text-transform: uppercase;\n" +
  230.         "    font-size: 10px;\n" +
  231.         "    color: #FCFAF9;\n" +
  232.         "}\n" +
  233.         "\n" +
  234.         "table.custom-table td:nth-child(1),\n" +
  235.         "table.custom-table td:nth-child(2),\n" +
  236.         "table.custom-table td:nth-child(3),\n" +
  237.         "table.custom-table td:nth-child(4),\n" +
  238.         "table.custom-table td:nth-child(5),\n" +
  239.         "table.custom-table td:nth-child(6),\n" +
  240.         "table.custom-table td:nth-child(7) {\n" +
  241.         "    text-align: center;\n" +
  242.         "}\n" +
  243.         "\n";
  244.       table =
  245.         "<style>" +
  246.         style +
  247.         '</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>';
  248.     }
  249.   }
  250.  
  251.   var current = null;
  252.   var page = 0;
  253.   var dataBeforeSorting = [];
  254.   var dataNotStarted = [];
  255.  
  256.   while (current === null || current % 10 === 0) {
  257.     if (current === null) {
  258.       current = 0;
  259.     }
  260.     var xmlhttp = new XMLHttpRequest();
  261.     xmlhttp.open(
  262.       "GET",
  263.       "https://www.geoguessr.com/api/v3/leagues/started?page=" + page,
  264.       false
  265.     );
  266.     xmlhttp.send();
  267.     if (xmlhttp.status === 200) {
  268.       var data = JSON.parse(xmlhttp.responseText);
  269.       current = current + data.length;
  270.       if (data.length === 0) {
  271.         current++;
  272.       }
  273.       page++;
  274.       dataBeforeSorting.push(data);
  275.     } else {
  276.       alert("An error occured, while getting maps.");
  277.       return false;
  278.     }
  279.   }
  280.  
  281.   current = null;
  282.   page = 0;
  283.  
  284.   while (current === null || current % 10 === 0) {
  285.     if (current === null) {
  286.       current = 0;
  287.     }
  288.  
  289.     var xmlhttp2 = new XMLHttpRequest();
  290.     xmlhttp2.open(
  291.       "GET",
  292.       "https://www.geoguessr.com/api/v3/leagues/notstarted?page=" + page,
  293.       false
  294.     );
  295.     xmlhttp2.send();
  296.     if (xmlhttp2.status === 200) {
  297.       var data2 = JSON.parse(xmlhttp2.responseText);
  298.       current = current + data2.length;
  299.       if (data2.length === 0) {
  300.         current++;
  301.       }
  302.       page++;
  303.       dataNotStarted.push(data2);
  304.     } else {
  305.       alert("An error occured, while getting maps.");
  306.       return false;
  307.     }
  308.   }
  309.  
  310.   //show finished leagues with the help of localStorage
  311.   if (!buttonClicked) {
  312.     let tokenListOfAllLeaguesInTheStorageToBeShown = [];
  313.     let leaguesStorage = JSON.parse(localStorage.leaguesStorage || "{}");
  314.     var showHorizontalLine = false;
  315.     var leagueHasFinishedInfo = document.createElement("div");
  316.  
  317.     for (league in leaguesStorage) {
  318.       if (leaguesStorage[league].expDate - Date.now() > 0) {
  319.         //the league is in the localStorage, but not over yet
  320.         continue;
  321.       }
  322.       showHorizontalLine = true;
  323.       tokenListOfAllLeaguesInTheStorageToBeShown.push(
  324.         leaguesStorage[league].token
  325.       );
  326.       var aFinishedLeague = document.createElement("span");
  327.       aFinishedLeague.setAttribute("style", "color:red;");
  328.  
  329.       var linkSpan = document.createElement("span");
  330.       linkSpan.setAttribute("id", leaguesStorage[league].token);
  331.       linkSpan.innerHTML =
  332.         '<b><a style="text-decoration: underline; color: red;" href="https://www.geoguessr.com/leagues/' +
  333.         leaguesStorage[league].token +
  334.         '">here</a></b>';
  335.  
  336.       aFinishedLeague.innerHTML =
  337.         "<b>" +
  338.         leaguesStorage[league].trueExpDate +
  339.         " at " +
  340.         leaguesStorage[league].trueExpTime +
  341.         ': The league "' +
  342.         leaguesStorage[league].name +
  343.         '" finished after ' +
  344.         leaguesStorage[league].totalLegs +
  345.         (leaguesStorage[league].totalLegs === 1 ? " leg" : " legs") +
  346.         ". Click <b>";
  347.  
  348.       aFinishedLeague.appendChild(linkSpan);
  349.       aFinishedLeague.innerHTML += "<b> to see the results!</b><br><br>";
  350.  
  351.       leagueHasFinishedInfo.appendChild(aFinishedLeague);
  352.       console.log(leagueHasFinishedInfo);
  353.     }
  354.     if (showHorizontalLine) {
  355.       leagueHasFinishedInfo.innerHTML += "<hr><br><br>";
  356.     }
  357.  
  358.     document
  359.       .getElementsByClassName("title--medium")[0]
  360.       .parentElement.insertBefore(
  361.         leagueHasFinishedInfo,
  362.         document.getElementsByClassName("title--medium")[0]
  363.       );
  364.  
  365.     tokenListOfAllLeaguesInTheStorageToBeShown.forEach((el) => {
  366.       document.getElementById(el).addEventListener(
  367.         "click",
  368.         () => {
  369.           deleteStorageEntry(el);
  370.         },
  371.         false
  372.       );
  373.     });
  374.   }
  375.  
  376.   //show script info
  377.   if (!hideInfo && !buttonClicked) {
  378.     var textInfo = document.createElement("div");
  379.     textInfo.setAttribute("id", "textInfo");
  380.     textInfo.innerHTML =
  381.       '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 "hideInfo" to true.</b></span> There you will also find all the customization options.<br><br>New in ' +
  382.       version +
  383.       ":<br>- Have you ever had the problem that a league ends and disappers from the league list but you only recognize it much later? This small update introduces a reminder of recently finished leagues at the top of the page so you can take a look at their results. (The feature uses localStorage, same as e.g. the GeoGuessr Path Logger. Notifications are only shown if you access the league page during the last leg of the respective league.)<br>- You can click on the red/green icons in the 'Played' coloumn in order to access the current challenge.<br><br><hr><br><br>";
  384.     document
  385.       .getElementsByClassName("title--medium")[0]
  386.       .parentElement.insertBefore(
  387.         textInfo,
  388.         document.getElementsByClassName("title--medium")[0]
  389.       );
  390.   }
  391.  
  392.   if (!listFormatCalendar) {
  393.     document.getElementsByClassName("title--medium")[0].innerText =
  394.       "Pro Leagues Enhanced";
  395.     table += getHtmlOverview(dataBeforeSorting);
  396.     table += getHtmlNotStarted(dataNotStarted);
  397.     document.title =
  398.       "Pro Leagues Enhanced" +
  399.       (amountOfSoonExpiringLegsToBePlayed > 0
  400.         ? " (" + amountOfSoonExpiringLegsToBePlayed + ")"
  401.         : "");
  402.   } else {
  403.     document.getElementsByClassName("title--medium")[0].innerText =
  404.       "Pro Leagues Calendar";
  405.     table += getHtmlCalendar(dataBeforeSorting);
  406.     if (!calendarCompact) {
  407.       table += getHtmlNotStarted(dataNotStarted);
  408.     }
  409.     document.title =
  410.       "Pro Leagues Calendar" +
  411.       (amountOfSoonExpiringLegsToBePlayed > 0
  412.         ? " (" + amountOfSoonExpiringLegsToBePlayed + ")"
  413.         : "");
  414.   }
  415.  
  416.   table +=
  417.     '</table><div style="margin-bottom: 15px; color:#ACAAA9; font-size: 12px; float: right">by dreifachpunkt. | ' +
  418.     version +
  419.     "</div>";
  420.   table +=
  421.     '<input type="button" id="btnChangeDesign" value="' +
  422.     (listFormatCalendar ? "Go to Overview" : "Go to Calendar") +
  423.     '" class="button button--small button--primary" style="margin-bottom: 15px;"/>';
  424.   table +=
  425.     listFormatCalendar && calendarCompact
  426.       ? ' <input type="button" id="btnChangeCalendar" value="' +
  427.         (isCalWeek ? "Show Month" : "Show 2 Weeks") +
  428.         '" class="button button--small button--secondary" style="margin-bottom: 15px;"/>'
  429.       : "";
  430.   var div = document.createElement("div");
  431.   div.setAttribute("id", "theBestDiv");
  432.   div.innerHTML = table;
  433.   if (arialFont) {
  434.     div.style.fontFamily = "Arial, sans-serif";
  435.   }
  436.  
  437.   var elements = document.getElementsByClassName("container__content");
  438.   console.log(elements);
  439.  
  440.   var oldDiv = document.getElementById("theBestDiv");
  441.   if (!buttonClicked) {
  442.     elements[0].parentNode.insertBefore(div, elements[0]);
  443.     elements[0].parentNode.removeChild(elements[0]);
  444.     // elements[0].innerHTML = "";
  445.   } else {
  446.     if (oldDiv !== null) {
  447.       oldDiv.parentNode.replaceChild(div, oldDiv);
  448.     } else {
  449.       elements[0].parentNode.insertBefore(div, elements[0]);
  450.       elements[0].parentNode.removeChild(elements[0]);
  451.       //elements[0].innerHTML = "";
  452.     }
  453.   }
  454.  
  455.   document
  456.     .getElementById("btnChangeDesign")
  457.     .addEventListener("click", changeDesign, false);
  458.   if (document.getElementById("btnChangeCalendar") != null) {
  459.     document
  460.       .getElementById("btnChangeCalendar")
  461.       .addEventListener("click", changeCalendar, false);
  462.   }
  463. }
  464.  
  465. function changeDesign() {
  466.   listFormatCalendar = listFormatCalendar ? false : true;
  467.   tableRow = 0;
  468.   buttonClicked = true;
  469.   amountOfSoonExpiringLegsToBePlayed = 0;
  470.   leaguesBehavior();
  471. }
  472.  
  473. function changeCalendar() {
  474.   isCalWeek = isCalWeek ? false : true;
  475.   tableRow = 0;
  476.   buttonClicked = true;
  477.   amountOfSoonExpiringLegsToBePlayed = 0;
  478.   leaguesBehavior();
  479. }
  480.  
  481. function addStorageEntry(
  482.   token,
  483.   name,
  484.   totalLegs,
  485.   expDate,
  486.   trueExpDate,
  487.   trueExpTime
  488. ) {
  489.   let leaguesStorage = JSON.parse(localStorage.leaguesStorage || "{}");
  490.   leaguesStorage[token] = {
  491.     name,
  492.     totalLegs,
  493.     expDate,
  494.     trueExpDate,
  495.     trueExpTime,
  496.     token,
  497.   };
  498.   localStorage.leaguesStorage = JSON.stringify(leaguesStorage);
  499. }
  500.  
  501. function deleteStorageEntry(token) {
  502.   console.log("delete entry");
  503.   let leaguesStorage = JSON.parse(localStorage.leaguesStorage || "{}");
  504.   delete leaguesStorage[token];
  505.   localStorage.leaguesStorage = JSON.stringify(leaguesStorage);
  506. }
  507.  
  508. function getHtmlOverview(data) {
  509.   var output = "";
  510.   var dataSorted = [];
  511.  
  512.   for (let arr in data) {
  513.     for (let objInner in data[arr]) {
  514.       if (data[arr][objInner].league.currentLeg != null) {
  515.         //if the league is ending and the last scoring is awaited, the status is still "started" but the currentLeg is null
  516.         dataSorted.push(data[arr][objInner]);
  517.       }
  518.     }
  519.   }
  520.  
  521.   for (var i = 0; i < dataSorted.length; i++) {
  522.     for (var j = 0; j < dataSorted.length - 1; j++) {
  523.       if (
  524.         dataSorted[j].league.currentLeg.endsAt >
  525.         dataSorted[j + 1].league.currentLeg.endsAt
  526.       ) {
  527.         var temp = dataSorted[j];
  528.         dataSorted[j] = dataSorted[j + 1];
  529.         dataSorted[j + 1] = temp;
  530.       }
  531.     }
  532.   }
  533.  
  534.   var now = Date.now(); // returns millis
  535.  
  536.   for (let obj in dataSorted) {
  537.     var league = dataSorted[obj];
  538.  
  539.     var isHidden = false;
  540.     for (i = 0; i < hiddenLeagues.length; i++) {
  541.       if (league.league.token === hiddenLeagues[i]) {
  542.         isHidden = true;
  543.       }
  544.     }
  545.     if (isHidden) {
  546.       continue;
  547.     }
  548.  
  549.     var startDate = Date.parse(league.league.currentLeg.startsAt);
  550.     var expDate = Date.parse(league.league.currentLeg.endsAt);
  551.  
  552.     league.league.currentLeg.duration =
  553.       getDuration(startDate, expDate)[0] +
  554.       " " +
  555.       getDuration(startDate, expDate)[1];
  556.  
  557.     league.league.currentLeg.remainingTime =
  558.       getDuration(now, expDate)[0] + " " + getDuration(now, expDate)[1];
  559.     var trueExpDate = new Date(expDate);
  560.     league.league.currentLeg.trueExpDate = trueExpDate.toLocaleDateString();
  561.     league.league.currentLeg.trueExpTime = trueExpDate.toLocaleTimeString(
  562.       undefined,
  563.       timeStrOptions
  564.     );
  565.     league.league.currentLeg.nextLegSoon =
  566.       (expDate - now) / 1000 / 60 / 60 < 24;
  567.     if (
  568.       league.league.currentLeg.nextLegSoon &&
  569.       !league.hasUserFinishedCurrentLeg
  570.     ) {
  571.       amountOfSoonExpiringLegsToBePlayed++;
  572.     }
  573.     tableRow++;
  574.     var currentChallenge = {};
  575.  
  576.     if (advancedVersion) {
  577.       var xmlhttpLeagueSpecific = new XMLHttpRequest();
  578.       xmlhttpLeagueSpecific.open(
  579.         "GET",
  580.         "https://www.geoguessr.com/api/v3/challenges/" +
  581.           league.league.currentLeg.challengeId,
  582.         false
  583.       );
  584.       xmlhttpLeagueSpecific.send();
  585.       if (xmlhttpLeagueSpecific.status === 200) {
  586.         var dataLeagueSpecific = JSON.parse(xmlhttpLeagueSpecific.responseText);
  587.         currentChallenge = dataLeagueSpecific;
  588.       } else {
  589.         alert("An error occured, while getting a specific challenge.");
  590.         return false;
  591.       }
  592.     }
  593.  
  594.     //wenn letztes Leg: erstelle Entry in localStorage, damit League angezeigt wird wenn sie endet
  595.     if (league.league.totalLegs === league.league.currentLeg.legNumber) {
  596.       addStorageEntry(
  597.         league.league.token,
  598.         league.league.name,
  599.         league.league.totalLegs,
  600.         expDate,
  601.         league.league.currentLeg.trueExpDate,
  602.         league.league.currentLeg.trueExpTime
  603.       );
  604.     }
  605.  
  606.     output += getOutputStrOverview(league, currentChallenge);
  607.   }
  608.  
  609.   return output;
  610. }
  611.  
  612. function getHtmlNotStarted(data) {
  613.   var output = "";
  614.  
  615.   var dataSorted = [];
  616.  
  617.   for (let arr in data) {
  618.     for (let objInner in data[arr]) {
  619.       dataSorted.push(data[arr][objInner]);
  620.     }
  621.   }
  622.  
  623.   if (dataSorted.length > 0) {
  624.     tableRow++;
  625.  
  626.     if (!listFormatCalendar) {
  627.       output +=
  628.         "<tr " +
  629.         (tableRow % 2 === 0 ? "bgcolor='#ECEAE9'" : "") +
  630.         "><td></td><td></td><td></td><td></td>" +
  631.         (advancedVersion ? "<td></td><td></td>" : "") +
  632.         "<td></td><td></td></tr>";
  633.     } else {
  634.       output +=
  635.         "<tr " +
  636.         (tableRow % 2 === 0 ? "bgcolor='#ECEAE9'" : "") +
  637.         "><td></td><td></td><td></td><td></td><td></td></tr>";
  638.     }
  639.   }
  640.  
  641.   for (var i = 0; i < dataSorted.length; i++) {
  642.     for (var j = 0; j < dataSorted.length - 1; j++) {
  643.       if (
  644.         dataSorted[j].league.numberOfParticipants <
  645.         dataSorted[j + 1].league.numberOfParticipants
  646.       ) {
  647.         var temp = dataSorted[j];
  648.         dataSorted[j] = dataSorted[j + 1];
  649.         dataSorted[j + 1] = temp;
  650.       }
  651.     }
  652.   }
  653.  
  654.   for (let obj in dataSorted) {
  655.     var league = dataSorted[obj];
  656.     tableRow++;
  657.  
  658.     output += getOutputStrNotStarted(league);
  659.   }
  660.   return output;
  661. }
  662.  
  663. //this function also returns the output string of the compact calendar
  664. function getHtmlCalendar(data) {
  665.   var output = "";
  666.   var dataSorted = [];
  667.  
  668.   for (let arr in data) {
  669.     for (let objInner in data[arr]) {
  670.       if (data[arr][objInner].league.currentLeg != null) {
  671.         // league is ending
  672.         dataSorted.push(data[arr][objInner]);
  673.       }
  674.     }
  675.   }
  676.  
  677.   var now = Date.now(); // returns millis
  678.   var additionalArtificialLeagues = [];
  679.   var additionalArtificialPreviousLeagues = [];
  680.  
  681.   for (var it = 0; it < dataSorted.length; it++) {
  682.     var league = dataSorted[it];
  683.     league.league.currentLeg.endsAt = Date.parse(
  684.       league.league.currentLeg.endsAt
  685.     );
  686.     league.league.currentLeg.startsAt = Date.parse(
  687.       league.league.currentLeg.startsAt
  688.     );
  689.     var durationMillis =
  690.       league.league.currentLeg.endsAt - league.league.currentLeg.startsAt;
  691.  
  692.     league.league.currentLeg.remainingTime =
  693.       getDuration(now, league.league.currentLeg.endsAt)[0] +
  694.       " " +
  695.       getDuration(now, league.league.currentLeg.endsAt)[1];
  696.     var trueExpDateOriginal = new Date(league.league.currentLeg.endsAt);
  697.     league.league.currentLeg.trueExpDate =
  698.       trueExpDateOriginal.toLocaleDateString();
  699.     league.league.currentLeg.trueExpTime =
  700.       trueExpDateOriginal.toLocaleTimeString(undefined, timeStrOptions);
  701.  
  702.     league.league.currentLeg.hasStarted = true;
  703.     league.league.currentLeg.nextLegSoon =
  704.       (league.league.currentLeg.endsAt - now) / 1000 / 60 / 60 < 24;
  705.     if (
  706.       league.league.currentLeg.nextLegSoon &&
  707.       !league.hasUserFinishedCurrentLeg
  708.     ) {
  709.       amountOfSoonExpiringLegsToBePlayed++;
  710.     }
  711.  
  712.     if (league.league.totalLegs === league.league.currentLeg.legNumber) {
  713.       addStorageEntry(
  714.         league.league.token,
  715.         league.league.name,
  716.         league.league.totalLegs,
  717.         league.league.currentLeg.endsAt,
  718.         league.league.currentLeg.trueExpDate,
  719.         league.league.currentLeg.trueExpTime
  720.       );
  721.     }
  722.  
  723.     //calculate future legs of a league by creating additional leagues with the
  724.     //current leg ending in the future
  725.     for (
  726.       var legNr = league.league.currentLeg.legNumber;
  727.       legNr < league.league.totalLegs;
  728.       legNr++
  729.     ) {
  730.       var artificialLeague = {};
  731.       artificialLeague.hasUserFinishedCurrentLeg = false;
  732.       artificialLeague.league = {};
  733.       artificialLeague.league.name = league.league.name;
  734.       artificialLeague.league.token = league.league.token;
  735.       artificialLeague.league.currentLeg = {};
  736.       artificialLeague.league.currentLeg.mapSlug = "";
  737.       artificialLeague.league.currentLeg.hasStarted = false;
  738.       artificialLeague.league.currentLeg.mapName = "t.b.a.";
  739.       artificialLeague.league.currentLeg.legNumber = legNr + 1;
  740.       artificialLeague.league.totalLegs = league.league.totalLegs;
  741.       artificialLeague.league.currentLeg.endsAt =
  742.         league.league.currentLeg.endsAt +
  743.         (legNr - league.league.currentLeg.legNumber + 1) * durationMillis;
  744.  
  745.       artificialLeague.league.currentLeg.nextLegSoon =
  746.         (artificialLeague.league.currentLeg.endsAt - now) / 1000 / 60 / 60 < 24;
  747.       artificialLeague.league.currentLeg.remainingTime =
  748.         getDuration(now, artificialLeague.league.currentLeg.endsAt)[0] +
  749.         " " +
  750.         getDuration(now, artificialLeague.league.currentLeg.endsAt)[1];
  751.       var trueExpDate = new Date(artificialLeague.league.currentLeg.endsAt);
  752.       artificialLeague.league.currentLeg.trueExpDate =
  753.         trueExpDate.toLocaleDateString();
  754.       artificialLeague.league.currentLeg.trueExpTime =
  755.         trueExpDate.toLocaleTimeString(undefined, timeStrOptions);
  756.  
  757.       additionalArtificialLeagues.push(artificialLeague);
  758.     }
  759.  
  760.     //calculate past legs of a league by creating additional leagues with the
  761.     //current leg ending in the past
  762.     if (calendarCompact) {
  763.       for (legNr = league.league.currentLeg.legNumber; legNr > 1; legNr--) {
  764.         artificialLeague = {};
  765.         artificialLeague.hasUserFinishedCurrentLeg = true;
  766.         artificialLeague.league = {};
  767.         artificialLeague.league.name = league.league.name;
  768.         artificialLeague.league.token = league.league.token;
  769.         artificialLeague.league.currentLeg = {};
  770.         artificialLeague.league.currentLeg.mapSlug = "";
  771.         artificialLeague.league.currentLeg.hasStarted = false;
  772.         artificialLeague.league.currentLeg.mapName = "Leg has already finished";
  773.         artificialLeague.league.currentLeg.legNumber = legNr - 1;
  774.         artificialLeague.league.totalLegs = league.league.totalLegs;
  775.         artificialLeague.league.currentLeg.endsAt =
  776.           league.league.currentLeg.endsAt +
  777.           (legNr - league.league.currentLeg.legNumber - 1) * durationMillis;
  778.  
  779.         artificialLeague.league.currentLeg.nextLegSoon =
  780.           (artificialLeague.league.currentLeg.endsAt - now) / 1000 / 60 / 60 <
  781.           24;
  782.         artificialLeague.league.currentLeg.remainingTime =
  783.           "Leg has already finished";
  784.         trueExpDate = new Date(artificialLeague.league.currentLeg.endsAt);
  785.         artificialLeague.league.currentLeg.trueExpDate =
  786.           trueExpDate.toLocaleDateString(undefined, dateStrOptionsCompactCal);
  787.         artificialLeague.league.currentLeg.trueExpTime =
  788.           trueExpDate.toLocaleTimeString(undefined, timeStrOptions);
  789.  
  790.         additionalArtificialLeagues.push(artificialLeague);
  791.       }
  792.     }
  793.   }
  794.  
  795.   for (var ite = 0; ite < additionalArtificialLeagues.length; ite++) {
  796.     dataSorted.push(additionalArtificialLeagues[ite]);
  797.   }
  798.  
  799.   for (var i = 0; i < dataSorted.length; i++) {
  800.     for (var j = 0; j < dataSorted.length - 1; j++) {
  801.       if (
  802.         dataSorted[j].league.currentLeg.endsAt >
  803.         dataSorted[j + 1].league.currentLeg.endsAt
  804.       ) {
  805.         var temp = dataSorted[j];
  806.         dataSorted[j] = dataSorted[j + 1];
  807.         dataSorted[j + 1] = temp;
  808.       }
  809.     }
  810.   }
  811.  
  812.   if (!calendarCompact) {
  813.     for (let obj in dataSorted) {
  814.       var leag = dataSorted[obj];
  815.       if (
  816.         tableRow >= calendarListHowManyCalEvents &&
  817.         calendarListHowManyCalEvents !== 0
  818.       ) {
  819.         break;
  820.       }
  821.       var isHidden = false;
  822.       for (i = 0; i < hiddenLeagues.length; i++) {
  823.         if (leag.league.token === hiddenLeagues[i]) {
  824.           isHidden = true;
  825.         }
  826.       }
  827.       if (isHidden) {
  828.         continue;
  829.       }
  830.  
  831.       output += getOutputStrCalendarList(leag);
  832.     }
  833.   } else {
  834.     //here comes the big mess :D
  835.     if (dataSorted.length === 0) {
  836.       return "";
  837.     }
  838.  
  839.     var tempOut = "";
  840.     var arrOfArrs = [];
  841.     var tempArr = [];
  842.  
  843.     var dayMillisIter = getMillisOfDateStart(
  844.       dataSorted[0].league.currentLeg.endsAt
  845.     );
  846.  
  847.     i = 0;
  848.     while (i <= dataSorted.length) {
  849.       if (i >= dataSorted.length) {
  850.         arrOfArrs.push(tempArr);
  851.         tempArr = [];
  852.         break;
  853.       }
  854.  
  855.       isHidden = false;
  856.       for (j = 0; j < hiddenLeagues.length && i < dataSorted.length; j++) {
  857.         if (dataSorted[i].league.token === hiddenLeagues[j]) {
  858.           isHidden = true;
  859.         }
  860.       }
  861.       if (isHidden) {
  862.         i++;
  863.         continue;
  864.       }
  865.  
  866.       if (tempArr.length === 0) {
  867.         tempArr.push(dayMillisIter);
  868.         dayMillisIter = getNextDayStartFromCurrentDateStart(dayMillisIter);
  869.       }
  870.       if (
  871.         tempArr[0] ===
  872.         getMillisOfDateStart(dataSorted[i].league.currentLeg.endsAt)
  873.       ) {
  874.         tempArr.push(dataSorted[i]);
  875.         i++;
  876.       } else {
  877.         arrOfArrs.push(tempArr);
  878.         tempArr = [];
  879.       }
  880.     }
  881.  
  882.     if (arrOfArrs.length > 1) {
  883.       var firstArrSpot = arrOfArrs[0];
  884.       var tempTime = firstArrSpot[0];
  885.  
  886.       for (i = 0; i < 42; i++) {
  887.         tempTime = getPreviousDayStartFromCurrentDateStart(tempTime);
  888.         arrOfArrs.unshift([tempTime]);
  889.       }
  890.     }
  891.  
  892.     var firstField = !isCalWeek
  893.       ? new Date(getFirstDayOfWeek(getFirstDayOfMonth(Date.now())))
  894.       : new Date(getFirstDayOfWeek(Date.now()));
  895.     var current = 0;
  896.  
  897.     while (arrOfArrs[current][0] < firstField) {
  898.       current++;
  899.       if (current === arrOfArrs.length) {
  900.         return "";
  901.       }
  902.     }
  903.  
  904.     var dayOfWeek = 0;
  905.  
  906.     var amountOfDays = isCalWeek ? 14 : 42;
  907.  
  908.     for (i = current; i < current + amountOfDays; i++) {
  909.       if (dayOfWeek === 7) {
  910.         dayOfWeek = 0;
  911.       }
  912.       if (dayOfWeek === 0) {
  913.         output += "<tr bgcolor='#ECEAE9'>";
  914.         tempOut += "<tr>";
  915.       }
  916.  
  917.       if (arrOfArrs[i] == undefined || arrOfArrs[i].length == 0) {
  918.         output += "<td></td>";
  919.         tempOut += "<td>";
  920.       } else {
  921.         output +=
  922.           "<td " +
  923.           (getMillisOfDateStart(Date.now()) === arrOfArrs[i][0]
  924.             ? "bgcolor='#CCCAC9'"
  925.             : " ") +
  926.           ">" +
  927.           new Date(arrOfArrs[i][0]).toLocaleDateString() +
  928.           "</td>";
  929.         tempOut +=
  930.           "<td " +
  931.           (getMillisOfDateStart(Date.now()) === arrOfArrs[i][0]
  932.             ? "bgcolor='#CCCAC9'"
  933.             : !isCalWeek
  934.             ? new Date(arrOfArrs[i][0]).getMonth() !==
  935.               new Date(Date.now()).getMonth()
  936.               ? "bgcolor='#ECEAE9'"
  937.               : ""
  938.             : "") +
  939.           ' style="text-align: left; vertical-align: top;">';
  940.  
  941.         for (j = 1; j < arrOfArrs[i].length; j++) {
  942.           tempOut +=
  943.             '<b style="display: inline-flex;">' +
  944.             arrOfArrs[i][j].league.currentLeg.trueExpTime +
  945.             "</b> " +
  946.             (arrOfArrs[i][j].league.currentLeg.legNumber ===
  947.             arrOfArrs[i][j].league.totalLegs
  948.               ? '<span style="color:blue;"><b><i>LAST LEG</i></b> </span>'
  949.               : "") +
  950.             (isCalWeek ? "<br>" : "") +
  951.             '<a style="font-size: 10px;display: inline-flex; color: ' +
  952.             (arrOfArrs[i][j].hasUserFinishedCurrentLeg ? "black" : "red") +
  953.             ";" +
  954.             (arrOfArrs[i][j].league.currentLeg.hasStarted
  955.               ? "text-decoration: underline"
  956.               : "") +
  957.             ';" href="https://www.geoguessr.com/leagues/' +
  958.             arrOfArrs[i][j].league.token +
  959.             '">' +
  960.             arrOfArrs[i][j].league.name +
  961.             "</a><br>";
  962.         }
  963.       }
  964.  
  965.       tempOut += "</td>";
  966.  
  967.       if (dayOfWeek === 6) {
  968.         output += "</tr>";
  969.         tempOut += "</tr>";
  970.         output += tempOut;
  971.         tempOut = "";
  972.       }
  973.  
  974.       dayOfWeek++;
  975.     }
  976.   }
  977.  
  978.   return output;
  979. }
  980.  
  981. function getOutputStrOverview(league, currentChallenge) {
  982.   //for normal overview, currentChallenge is {}
  983.   var out =
  984.     "<tr " +
  985.     (tableRow % 2 === 0 ? "bgcolor='#ECEAE9'" : "") +
  986.     "><td>" +
  987.     getTableLeagueCreator(league) +
  988.     "</td><td>" +
  989.     getTableLeagueName(league) +
  990.     "<br>" +
  991.     getTableLeagueCurrMap(league) +
  992.     "</td>" +
  993.     (advancedVersion
  994.       ? "<td>" +
  995.         getTableLeagueMode(currentChallenge) +
  996.         "</td><td>" +
  997.         getTableLeagueDuration(league) +
  998.         "</td>"
  999.       : "") +
  1000.     "<td>" +
  1001.     getTableLeagueLeg(league) +
  1002.     "</td>" +
  1003.     "<td " +
  1004.     (league.league.currentLeg.nextLegSoon && !league.hasUserFinishedCurrentLeg
  1005.       ? 'style="color:red;">'
  1006.       : ">") +
  1007.     getTableLeagueLegEnds(league) +
  1008.     "</td>" +
  1009.     "<td>" +
  1010.     getTableLeaguePlayed(league) +
  1011.     "</td>" +
  1012.     "<td>" +
  1013.     getTableLeagueParticipants(league, currentChallenge) +
  1014.     "</td>" +
  1015.     "</tr>";
  1016.  
  1017.   return out;
  1018. }
  1019.  
  1020. function getOutputStrCalendarList(league) {
  1021.   var output = "";
  1022.   tableRow++;
  1023.  
  1024.   output +=
  1025.     "<tr " +
  1026.     (tableRow % 2 === 0 ? "bgcolor='#ECEAE9'" : "") +
  1027.     ">" +
  1028.     "<td>" +
  1029.     getTableLeagueName(league) +
  1030.     "</td>" +
  1031.     "<td>" +
  1032.     (league.league.currentLeg.hasStarted
  1033.       ? '<a href="https://www.geoguessr.com/maps/' +
  1034.         league.league.currentLeg.mapSlug +
  1035.         '">'
  1036.       : "") +
  1037.     league.league.currentLeg.mapName +
  1038.     (league.league.currentLeg.hasStarted ? "</a>" : "") +
  1039.     "</td>" +
  1040.     "<td>" +
  1041.     getTableLeagueLeg(league) +
  1042.     "</td>" +
  1043.     "<td " +
  1044.     (league.league.currentLeg.nextLegSoon && !league.hasUserFinishedCurrentLeg
  1045.       ? 'style="color:red;">'
  1046.       : ">") +
  1047.     getTableLeagueLegEnds(league) +
  1048.     "</td>" +
  1049.     "<td>" +
  1050.     getTableLeaguePlayed(league) +
  1051.     "</td>" +
  1052.     "</tr>";
  1053.  
  1054.   return output;
  1055. }
  1056.  
  1057. function getOutputStrNotStarted(league) {
  1058.   var out = "";
  1059.  
  1060.   if (!listFormatCalendar) {
  1061.     out +=
  1062.       "<tr " +
  1063.       (tableRow % 2 === 0 ? "bgcolor='#ECEAE9'" : "") +
  1064.       ">" +
  1065.       "<td>" +
  1066.       getTableLeagueCreator(league) +
  1067.       "</td>" +
  1068.       "<td>" +
  1069.       getTableLeagueName(league) +
  1070.       "</td>" +
  1071.       (advancedVersion ? "<td></td><td></td>" : "") +
  1072.       "<td>0 of " +
  1073.       league.league.totalLegs +
  1074.       "</td>" +
  1075.       "<td>League has not started yet</td>" +
  1076.       "<td>" +
  1077.       symbolNotAvailable +
  1078.       "</td>" +
  1079.       "<td>" +
  1080.       league.league.numberOfParticipants +
  1081.       "</td>" +
  1082.       "</tr>";
  1083.   } else {
  1084.     out +=
  1085.       "<tr " +
  1086.       (tableRow % 2 === 0 ? "bgcolor='#ECEAE9'" : "") +
  1087.       ">" +
  1088.       "<td>" +
  1089.       getTableLeagueName(league) +
  1090.       "</td>" +
  1091.       "<td></td>" +
  1092.       "<td>0 of " +
  1093.       league.league.totalLegs +
  1094.       "</td>" +
  1095.       "<td>League has not started yet</td>" +
  1096.       "<td>" +
  1097.       symbolNotAvailable +
  1098.       "</td>" +
  1099.       "</tr>";
  1100.   }
  1101.  
  1102.   return out;
  1103. }
  1104.  
  1105. function getTableLeagueCreator(league) {
  1106.   return (
  1107.     '<a href="https://www.geoguessr.com' +
  1108.     league.league.creator.url +
  1109.     '">' +
  1110.     league.league.creator.nick +
  1111.     "</a>"
  1112.   );
  1113. }
  1114.  
  1115. function getTableLeagueName(league) {
  1116.   return (
  1117.     '<b><a href="https://www.geoguessr.com/leagues/' +
  1118.     league.league.token +
  1119.     '" style="text-decoration: underline;">' +
  1120.     league.league.name +
  1121.     "</a></b>"
  1122.   );
  1123. }
  1124.  
  1125. function getTableLeagueCurrMap(league) {
  1126.   return (
  1127.     '<a href="https://www.geoguessr.com/maps/' +
  1128.     league.league.currentLeg.mapSlug +
  1129.     '">' +
  1130.     league.league.currentLeg.mapName +
  1131.     "</a>"
  1132.   );
  1133. }
  1134.  
  1135. function getTableLeagueMode(currentChallenge) {
  1136.   return (
  1137.     (currentChallenge.challenge.timeLimit > 60
  1138.       ? +(
  1139.           Math.round(currentChallenge.challenge.timeLimit / 60 + "e+2") + "e-2"
  1140.         ) + " mins per round"
  1141.       : currentChallenge.challenge.timeLimit === 0
  1142.       ? "no time limit"
  1143.       : currentChallenge.challenge.timeLimit + " secs per round") +
  1144.     (currentChallenge.challenge.forbidMoving ||
  1145.     currentChallenge.challenge.forbidZooming ||
  1146.     currentChallenge.challenge.forbidRotating
  1147.       ? " (N"
  1148.       : "") +
  1149.     (currentChallenge.challenge.forbidMoving ? "M" : "") +
  1150.     (currentChallenge.challenge.forbidRotating ? "P" : "") +
  1151.     (currentChallenge.challenge.forbidZooming ? "Z" : "") +
  1152.     (currentChallenge.challenge.forbidMoving ||
  1153.     currentChallenge.challenge.forbidZooming ||
  1154.     currentChallenge.challenge.forbidRotating
  1155.       ? ")"
  1156.       : "")
  1157.   );
  1158. }
  1159.  
  1160. function getTableLeagueDuration(league) {
  1161.   return league.league.currentLeg.duration;
  1162. }
  1163.  
  1164. function getTableLeagueLeg(league) {
  1165.   return (
  1166.     league.league.currentLeg.legNumber +
  1167.     " of " +
  1168.     league.league.totalLegs +
  1169.     (league.league.currentLeg.legNumber === league.league.totalLegs
  1170.       ? '<br><span style="color:blue;"><b><i>LAST LEG</i></b></span>'
  1171.       : "")
  1172.   );
  1173. }
  1174.  
  1175. function getTableLeagueLegEnds(league) {
  1176.   return (
  1177.     (!legEndsRemaining || legEndsBoth
  1178.       ? league.league.currentLeg.trueExpDate +
  1179.         " at " +
  1180.         league.league.currentLeg.trueExpTime
  1181.       : "") +
  1182.     (legEndsBoth ? "<br>" : "") +
  1183.     (legEndsRemaining || legEndsBoth
  1184.       ? league.league.currentLeg.remainingTime
  1185.       : "")
  1186.   );
  1187. }
  1188.  
  1189. function getTableLeaguePlayed(league) {
  1190.   var startLinkString = '<a href="https://www.geoguessr.com/challenge/';
  1191.  
  1192.   return !listFormatCalendar
  1193.     ? league.hasUserFinishedCurrentLeg
  1194.       ? startLinkString +
  1195.         league.league.currentLeg.challengeId +
  1196.         '">' +
  1197.         symbolPlayed +
  1198.         "</a>"
  1199.       : startLinkString +
  1200.         league.league.currentLeg.challengeId +
  1201.         '">' +
  1202.         symbolNotPlayed +
  1203.         "</a>"
  1204.     : league.league.currentLeg.hasStarted
  1205.     ? league.hasUserFinishedCurrentLeg
  1206.       ? startLinkString +
  1207.         league.league.currentLeg.challengeId +
  1208.         '">' +
  1209.         symbolPlayed +
  1210.         "</a>"
  1211.       : startLinkString +
  1212.         league.league.currentLeg.challengeId +
  1213.         '">' +
  1214.         symbolNotPlayed +
  1215.         "</a>"
  1216.     : symbolNotAvailable;
  1217. }
  1218.  
  1219. function getTableLeagueParticipants(league, currentChallenge) {
  1220.   return advancedVersion
  1221.     ? currentChallenge.challenge.numberOfParticipants +
  1222.         (league.hasUserFinishedCurrentLeg ? true : false) +
  1223.         " of " +
  1224.         league.league.numberOfParticipants +
  1225.         " played"
  1226.     : league.league.numberOfParticipants;
  1227. }
  1228.  
  1229. function getFirstDayOfWeek(dateMillis) {
  1230.   var dayOfWeek =
  1231.     new Date(dateMillis).getDay() === 0 ? 7 : new Date(dateMillis).getDay();
  1232.   var retVal = getMillisOfDateStart(dateMillis);
  1233.   while (dayOfWeek > 1) {
  1234.     retVal = getPreviousDayStartFromCurrentDateStart(retVal);
  1235.     dayOfWeek--;
  1236.   }
  1237.   return retVal;
  1238. }
  1239.  
  1240. function getFirstDayOfMonth(dateMillis) {
  1241.   var dayOfMonth = new Date(dateMillis).getDate();
  1242.   var retVal = getMillisOfDateStart(dateMillis);
  1243.   while (dayOfMonth > 1) {
  1244.     retVal = getPreviousDayStartFromCurrentDateStart(retVal);
  1245.     dayOfMonth--;
  1246.   }
  1247.   return retVal;
  1248. }
  1249.  
  1250. function getDayMillis() {
  1251.   return 1 * 1000 * 60 * 60 * 24;
  1252. }
  1253.  
  1254. function getPreviousDayStartFromCurrentDateStart(dateMillis) {
  1255.   return getMillisOfDateStart(dateMillis - 1 * 1000 * 60 * 60 * 22);
  1256. }
  1257.  
  1258. function getNextDayStartFromCurrentDateStart(dateMillis) {
  1259.   return getMillisOfDateStart(dateMillis + 1 * 1000 * 60 * 60 * 26);
  1260. }
  1261.  
  1262. function getMillisOfDateStart(dateMillis) {
  1263.   var date = new Date(dateMillis);
  1264.   var dateStart = new Date(
  1265.     date.getFullYear(),
  1266.     date.getMonth(),
  1267.     date.getDate(),
  1268.     0,
  1269.     0,
  1270.     0,
  1271.     0
  1272.   );
  1273.   return Date.parse(dateStart);
  1274. }
  1275.  
  1276. function getDuration(startDate, endDate) {
  1277.   //both in millis
  1278.   var durationHours = (endDate - startDate) / 1000 / 60 / 60;
  1279.  
  1280.   var retArray = [0, ""];
  1281.  
  1282.   switch (true) {
  1283.     case durationHours < 1:
  1284.       retArray[0] = Math.floor(durationHours * 60);
  1285.       retArray[1] = "minutes";
  1286.       break;
  1287.     case durationHours === 1:
  1288.       retArray[0] = durationHours;
  1289.       retArray[1] = "hour";
  1290.       break;
  1291.     case durationHours < 24:
  1292.       retArray[0] = Math.floor(durationHours);
  1293.       retArray[1] = Math.floor(durationHours) === 1 ? "hour" : "hours";
  1294.       break;
  1295.     case durationHours >= 24 && durationHours < 48:
  1296.       retArray[0] = Math.floor(durationHours / 24);
  1297.       retArray[1] = "day";
  1298.       break;
  1299.     case durationHours > 24 && durationHours < 24 * 7:
  1300.     default:
  1301.       retArray[0] = Math.floor(durationHours / 24);
  1302.       retArray[1] = "days";
  1303.       break;
  1304.     case durationHours >= 24 * 7 && durationHours < 24 * 7 * 2:
  1305.       retArray[0] = Math.floor(durationHours / 24 / 7);
  1306.       retArray[1] = "week";
  1307.       break;
  1308.     case durationHours > 24 * 7:
  1309.       retArray[0] = Math.floor(durationHours / 24 / 7);
  1310.       retArray[1] = "weeks";
  1311.       break;
  1312.   }
  1313.   return retArray;
  1314. }
  1315.  
Add Comment
Please, Sign In to add comment