Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js: Difference between revisions

MediaWiki interface page
No edit summary
No edit summary
 
(7 intermediate revisions by the same user not shown)
Line 8: Line 8:




/* ===========================
mw.hook('wikipage.content').add(function () {
  Dungeon Speedrun Leaderboard
  =========================== */


mw.hook('wikipage.content').add(function () {
  /* ===========================
    WAIT FOR TABLE (IMPORTANT)
    =========================== */


   function parseTime(t) {
   function waitForTable(callback) {
     if (!t || !t.includes(":")) return 999999;
     const table = document.getElementById("leaderboardTable");
     const parts = t.trim().split(":");
     if (table) {
     return parseInt(parts[0], 10) * 60 + parseInt(parts[1], 10);
      callback(table);
     } else {
      setTimeout(() => waitForTable(callback), 50);
    }
   }
   }




   const table = document.getElementById("leaderboardTable");
   waitForTable(function(table) {


  if (!table) return;
    /* ===========================
      HELPERS
      =========================== */


    function parseTime(t) {
      if (!t || !t.includes(":")) return 999999;
      const [m, s] = t.trim().split(":").map(Number);
      return m * 60 + s;
    }


  /* ===========================
    function getPoints(rank) {
    CREATE FILTER UI (ALWAYS)
      const scoring = {
    =========================== */
        1:25,2:20,3:17,4:15,5:13,
        6:11,7:10,8:9,9:8,10:7,
        11:6,12:5,13:4,14:3,15:2
      };
      return scoring[rank] || 1;
    }


  const wrapper = document.createElement("div");
  wrapper.style.marginBottom = "15px";


  const label = document.createElement("h3");
    /* ===========================
  label.innerText = "🔍 Filter Runs";
      FILTER UI
  wrapper.appendChild(label);
      =========================== */


  const dungeonLabel = document.createElement("strong");
    const filterWrapper = document.createElement("div");
  dungeonLabel.innerText = "Dungeon: ";
    filterWrapper.style.marginBottom = "20px";
  wrapper.appendChild(dungeonLabel);


  const dungeonSelect = document.createElement("select");
    const filterTitle = document.createElement("h3");
  dungeonSelect.style.marginRight = "20px";
    filterTitle.innerText = "🔍 Filter Runs";
    filterWrapper.appendChild(filterTitle);


  const dungeons = [
    const dungeonSelect = document.createElement("select");
    "All",
     dungeonSelect.style.marginRight = "10px";
    "Wolf's Den","Wolf's Den II","Wolf's Den III",
    "The Red Nest","For The Eggs",
    "Scarlet Revenge","Scarlet Revenge II",
     "Promised One","Promised One II","Promised One III",
    "Lost and Found","The Hostage","Cactus On Fire",
    "Deep Echoes","Deep Echoes II","Deep Echoes III",
    "King's Hoard","King's Hoard II",
    "Hall of Daggers"
  ];


  dungeons.forEach(name => {
    [
    const option = document.createElement("option");
      "All",
    option.value = name;
      "Wolf's Den","Wolf's Den II","Wolf's Den III",
    option.textContent = name;
      "The Red Nest","For The Eggs",
    dungeonSelect.appendChild(option);
      "Scarlet Revenge","Scarlet Revenge II",
  });
      "Promised One","Promised One II","Promised One III",
      "Lost and Found","The Hostage","Cactus On Fire",
      "Deep Echoes","Deep Echoes II","Deep Echoes III",
      "King's Hoard","King's Hoard II",
      "Hall of Daggers"
    ].forEach(name => {
      const opt = document.createElement("option");
      opt.value = name;
      opt.textContent = name;
      dungeonSelect.appendChild(opt);
    });


  wrapper.appendChild(dungeonSelect);
    filterWrapper.appendChild(document.createTextNode("Dungeon: "));
    filterWrapper.appendChild(dungeonSelect);


  const playerLabel = document.createElement("strong");
    const playerInput = document.createElement("input");
  playerLabel.innerText = "Player: ";
    playerInput.placeholder = "Search player...";
  wrapper.appendChild(playerLabel);
    playerInput.style.marginLeft = "10px";
    filterWrapper.appendChild(playerInput);


  const playerInput = document.createElement("input");
  playerInput.type = "text";
  playerInput.placeholder = "Search player name...";
  playerInput.style.padding = "4px";
  wrapper.appendChild(playerInput);


    /* ===========================
      SORT + RANK
      =========================== */


  /* ===========================
    function sortAndRank() {
    SORT + RANK
    =========================== */


  function sortAndRank() {
      const rows = Array.from(table.querySelectorAll("tr")).slice(1);


    const rows = Array.from(table.querySelectorAll("tr")).slice(1);
      rows.sort((a,b) => {


    rows.sort((a,b) => {
        if (a.cells.length < 5 || b.cells.length < 5) return 0;


      const dungeonA = a.cells[1].innerText.trim();
        const dA = a.cells[1].innerText.trim();
      const dungeonB = b.cells[1].innerText.trim();
        const dB = b.cells[1].innerText.trim();


      if (dungeonA < dungeonB) return -1;
        if (dA !== dB) return dA.localeCompare(dB);
      if (dungeonA > dungeonB) return 1;


      return parseTime(a.cells[4].innerText) - parseTime(b.cells[4].innerText);
        return parseTime(a.cells[4].innerText) - parseTime(b.cells[4].innerText);


    });
      });


    rows.forEach(row => table.appendChild(row));
      rows.forEach(r => table.appendChild(r));


    let currentDungeon = "";
      let currentDungeon = "";
    let dungeonRank = 0;
      let position = 0;
    let prevTime = null;
      let rank = 0;
    let position = 0;
      let prevTime = null;


    rows.forEach(row => {
      rows.forEach(row => {


      const dungeon = row.cells[1].innerText.trim();
        if (row.cells.length < 5) return;
      const time = parseTime(row.cells[4].innerText);
      const rankCell = row.cells[0];


      if (dungeon !== currentDungeon) {
         const dungeon = row.cells[1].innerText.trim();
         currentDungeon = dungeon;
         const time = parseTime(row.cells[4].innerText);
        dungeonRank = 1;
         position = 1;
        prevTime = time;
      } else {
        position++;


         if (time !== prevTime) {
         if (dungeon !== currentDungeon) {
           dungeonRank = position;
          currentDungeon = dungeon;
          position = 1;
          rank = 1;
          prevTime = time;
        } else {
          position++;
          if (time !== prevTime) rank = position;
           prevTime = time;
         }
         }


         prevTime = time;
         row.cells[0].innerText = rank;
       }
 
       });
    }
 


      rankCell.innerText = dungeonRank;
    /* ===========================
      BUILD TOP SECTION
      =========================== */


     });
     function buildTopSection() {
 
      const rows = Array.from(table.querySelectorAll("tr")).slice(1);
 
      const playerPoints = {};
      const records = {};
 
      rows.forEach(row => {
 
        if (row.cells.length < 5) return;
 
        const dungeon = row.cells[1].innerText.trim();
        const player = row.cells[2].innerText.trim();
        const time = row.cells[4].innerText.trim();
        const rank = parseInt(row.cells[0].innerText);
 
        // Only require valid time (CRITICAL FIX)
        if (!time || !time.includes(":")) return;
 
        /* ===========================
          POINTS
          =========================== */
 
        if (!isNaN(rank)) {
          playerPoints[player] = (playerPoints[player] || 0) + getPoints(rank);
        }


  }
        /* ===========================
          WORLD RECORDS (WORKING)
          =========================== */


        const seconds = parseTime(time);


  /* ===========================
        if (!records[dungeon]) {
    COMPETITION LEADERBOARD
          records[dungeon] = {
    =========================== */
            time: time,
            seconds: seconds,
            players: [player]
          };
        } else {
          if (seconds < records[dungeon].seconds) {
            records[dungeon] = {
              time: time,
              seconds: seconds,
              players: [player]
            };
          } else if (seconds === records[dungeon].seconds) {
            records[dungeon].players.push(player);
          }
        }


  function buildCompetitionLeaderboard() {
      });


    const rows = Array.from(table.querySelectorAll("tr")).slice(1);


    const playerPoints = {};
      /* REMOVE OLD */
      const existing = document.getElementById("topSection");
      if (existing) existing.remove();


    function getPoints(rank) {
      const scoring = {
        1: 25, 2: 20, 3: 17, 4: 15, 5: 13,
        6: 11, 7: 10, 8: 9, 9: 8, 10: 7,
        11: 6, 12: 5, 13: 4, 14: 3, 15: 2
      };
      return scoring[rank] || 1;
    }


    rows.forEach(row => {
      /* FLEX CONTAINER */
      const top = document.createElement("div");
      top.id = "topSection";
      top.style.display = "flex";
      top.style.gap = "30px";
      top.style.marginBottom = "25px";


      const rank = parseInt(row.cells[0].innerText);
      const player = row.cells[2].innerText.trim();


       if (!player || isNaN(rank)) return;
       /* ===========================
        COMPETITION TABLE
        =========================== */


       const points = getPoints(rank);
       const compBox = document.createElement("div");
      compBox.style.flex = "1";


       playerPoints[player] = (playerPoints[player] || 0) + points;
       const compTitle = document.createElement("h2");
      compTitle.innerText = "🏆 Competition Leaderboard";
      compBox.appendChild(compTitle);


    });
      const compTable = document.createElement("table");
      compTable.className = "wikitable";
      compTable.style.width = "100%";


    const leaderboard = Object.entries(playerPoints)
       compTable.innerHTML = `
       .map(([player, points]) => ({ player, points }))
        <tr>
       .sort((a, b) => b.points - a.points);
          <th>Rank</th>
          <th>Player</th>
          <th>Points</th>
        </tr>
       `;


      const leaderboard = Object.entries(playerPoints)
        .map(([player, points]) => ({player, points}))
        .sort((a,b)=>b.points-a.points);


    const existing = document.getElementById("competitionLeaderboard");
      let prevPoints = null;
    if (existing) existing.remove();
      let rank = 0;
      let position = 0;


    const compContainer = document.createElement("div");
      leaderboard.forEach(entry => {
    compContainer.id = "competitionLeaderboard";
    compContainer.style.marginBottom = "20px";


    const title = document.createElement("h2");
        position++;
    title.innerText = "🏆 Competition Leaderboard";
        if (entry.points !== prevPoints) rank = position;
    compContainer.appendChild(title);
        prevPoints = entry.points;


    const tableEl = document.createElement("table");
        const medal =
    tableEl.className = "wikitable";
          rank===1?"🥇 1":
    tableEl.style.width = "50%";
          rank===2?"🥈 2":
          rank===3?"🥉 3":
          rank;


    tableEl.innerHTML = `
        const row = document.createElement("tr");
      <tr>
        row.innerHTML = `
        <th>Rank</th>
          <td>${medal}</td>
        <th>Player</th>
          <td>${entry.player}</td>
        <th>Points</th>
          <td>${entry.points}</td>
      </tr>
        `;
    `;
        compTable.appendChild(row);


    let prevPoints = null;
      });
    let rank = 0;
    let position = 0;


    leaderboard.forEach((entry) => {
      compBox.appendChild(compTable);


      position++;


       if (entry.points !== prevPoints) {
       /* ===========================
        rank = position;
        WORLD RECORD TABLE
      }
        =========================== */


       prevPoints = entry.points;
       const wrBox = document.createElement("div");
      wrBox.style.flex = "1";


       let displayRank;
       const wrTitle = document.createElement("h2");
      if (rank === 1) displayRank = "🥇 1";
       wrTitle.innerText = "🏁 Competition Dungeon Records";
       else if (rank === 2) displayRank = "🥈 2";
       wrBox.appendChild(wrTitle);
       else if (rank === 3) displayRank = "🥉 3";
      else displayRank = rank;


       const row = document.createElement("tr");
       const wrTable = document.createElement("table");
      wrTable.className = "wikitable";
      wrTable.style.width = "100%";


       row.innerHTML = `
       wrTable.innerHTML = `
         <td>${displayRank}</td>
         <tr>
        <td>${entry.player}</td>
          <th>Dungeon</th>
        <td>${entry.points}</td>
          <th>Time</th>
          <th>Player(s)</th>
        </tr>
       `;
       `;


       tableEl.appendChild(row);
       Object.entries(records).forEach(([d, data]) => {


    });
        const row = document.createElement("tr");
 
        row.innerHTML = `
          <td>${d}</td>
          <td>${data.time}</td>
          <td>${data.players.join(", ")}</td>
        `;
 
        wrTable.appendChild(row);
 
      });
 
      wrBox.appendChild(wrTable);


    compContainer.appendChild(tableEl);


    // Insert leaderboard ABOVE table
      /* COMBINE */
    table.parentNode.insertBefore(compContainer, table);
      top.appendChild(compBox);
      top.appendChild(wrBox);


    // Insert filters BETWEEN leaderboard and table
      table.parentNode.insertBefore(top, table);
    table.parentNode.insertBefore(wrapper, table);


  }
      /* FILTER BELOW */
      table.parentNode.insertBefore(filterWrapper, table);
    }




  /* ===========================
    /* ===========================
    FILTERING (TABLE ONLY)
      FILTER
    =========================== */
      =========================== */


  function filterTable() {
    function filterTable() {


    const selectedDungeon = dungeonSelect.value;
      const dungeon = dungeonSelect.value;
    const playerSearch = playerInput.value.toLowerCase();
      const player = playerInput.value.toLowerCase();


    const rows = Array.from(table.querySelectorAll("tr")).slice(1);
      const rows = Array.from(table.querySelectorAll("tr")).slice(1);


    rows.forEach(row => {
      rows.forEach(row => {


      const dungeon = row.cells[1].innerText.trim();
        if (row.cells.length < 5) return;
      const player = row.cells[2].innerText.toLowerCase();


      row.style.display =
        const d = row.cells[1].innerText.trim();
        (selectedDungeon === "All" || dungeon === selectedDungeon) &&
         const p = row.cells[2].innerText.toLowerCase();
         player.includes(playerSearch)
          ? ""
          : "none";


    });
        row.style.display =
          (dungeon === "All" || d === dungeon) &&
          p.includes(player)
            ? ""
            : "none";
      });
    }


  }
    dungeonSelect.addEventListener("change", filterTable);
    playerInput.addEventListener("input", filterTable);


  dungeonSelect.addEventListener("change", filterTable);
  playerInput.addEventListener("input", filterTable);


    /* ===========================
      INIT
      =========================== */


  /* ===========================
    sortAndRank();
    INIT
    buildTopSection();
    =========================== */


   sortAndRank();
   });
  buildCompetitionLeaderboard();


});
});