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
Line 20: Line 20:
   }
   }


  /* ===========================
    SOLO TABLE
    =========================== */


   const table = document.getElementById("leaderboardTable");
   const table = document.getElementById("leaderboardTable");


   if (table) {
   if (!table) return;


    const container = document.getElementById("filterContainer");


    let wrapper; // <-- IMPORTANT (used later)
  /* ===========================
    CREATE FILTER UI (ALWAYS)
    =========================== */


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


      wrapper = document.createElement("div");
  const label = document.createElement("h3");
      wrapper.style.marginBottom = "15px";
  label.innerText = "🔍 Filter Runs";
  wrapper.appendChild(label);


      const label = document.createElement("h3");
  const dungeonLabel = document.createElement("strong");
      label.innerText = "🔍 Filter Runs";
  dungeonLabel.innerText = "Dungeon: ";
      wrapper.appendChild(label);
  wrapper.appendChild(dungeonLabel);


      const dungeonLabel = document.createElement("strong");
  const dungeonSelect = document.createElement("select");
      dungeonLabel.innerText = "Dungeon: ";
  dungeonSelect.style.marginRight = "20px";
      wrapper.appendChild(dungeonLabel);


      const dungeonSelect = document.createElement("select");
  const dungeons = [
      dungeonSelect.style.marginRight = "20px";
    "All",
    "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"
  ];


      const dungeons = [
  dungeons.forEach(name => {
        "All",
    const option = document.createElement("option");
        "Wolf's Den","Wolf's Den II","Wolf's Den III",
    option.value = name;
        "The Red Nest","For The Eggs",
    option.textContent = name;
        "Scarlet Revenge","Scarlet Revenge II",
    dungeonSelect.appendChild(option);
        "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 => {
  wrapper.appendChild(dungeonSelect);
        const option = document.createElement("option");
        option.value = name;
        option.textContent = name;
        dungeonSelect.appendChild(option);
      });


      wrapper.appendChild(dungeonSelect);
  const playerLabel = document.createElement("strong");
  playerLabel.innerText = "Player: ";
  wrapper.appendChild(playerLabel);


      const playerLabel = document.createElement("strong");
  const playerInput = document.createElement("input");
      playerLabel.innerText = "Player: ";
  playerInput.type = "text";
      wrapper.appendChild(playerLabel);
  playerInput.placeholder = "Search player name...";
  playerInput.style.padding = "4px";
  wrapper.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() {
  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) => {


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


          if (dungeonA < dungeonB) return -1;
      if (dungeonA < dungeonB) return -1;
          if (dungeonA > dungeonB) return 1;
      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(row => table.appendChild(row));


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


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


          const dungeon = row.cells[1].innerText.trim();
      const dungeon = row.cells[1].innerText.trim();
          const time = parseTime(row.cells[4].innerText);
      const time = parseTime(row.cells[4].innerText);
          const rankCell = row.cells[0];
      const rankCell = row.cells[0];


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


            if (time !== prevTime) {
        if (time !== prevTime) {
              dungeonRank = position;
          dungeonRank = position;
            }
         }
 
            prevTime = time;
          }
 
          rankCell.innerText = dungeonRank;
 
         });


        prevTime = time;
       }
       }


      rankCell.innerText = dungeonRank;


      /* ===========================
    });
        COMPETITION LEADERBOARD
        =========================== */


      function buildCompetitionLeaderboard() {
  }


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


        const playerPoints = {};
  /* ===========================
 
    COMPETITION LEADERBOARD
        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 => {
  function buildCompetitionLeaderboard() {


          const rank = parseInt(row.cells[0].innerText);
    const rows = Array.from(table.querySelectorAll("tr")).slice(1);
          const player = row.cells[2].innerText.trim();


          if (!player || isNaN(rank)) return;
    const playerPoints = {};


          const points = getPoints(rank);
    function getPoints(rank) {
 
      const scoring = {
          playerPoints[player] = (playerPoints[player] || 0) + points;
        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
 
      };
         const leaderboard = Object.entries(playerPoints)
      return scoring[rank] || 1;
          .map(([player, points]) => ({ player, points }))
    }
          .sort((a, b) => b.points - a.points);
 
        const existing = document.getElementById("competitionLeaderboard");
        if (existing) existing.remove();


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


        const title = document.createElement("h2");
      const rank = parseInt(row.cells[0].innerText);
        title.innerText = "🏆 Competition Leaderboard";
      const player = row.cells[2].innerText.trim();
        compContainer.appendChild(title);


        const tableEl = document.createElement("table");
      if (!player || isNaN(rank)) return;
        tableEl.className = "wikitable";
        tableEl.style.width = "50%";


        tableEl.innerHTML = `
      const points = getPoints(rank);
          <tr>
            <th>Rank</th>
            <th>Player</th>
            <th>Points</th>
          </tr>
        `;


        let prevPoints = null;
      playerPoints[player] = (playerPoints[player] || 0) + points;
        let rank = 0;
        let position = 0;


        leaderboard.forEach((entry) => {
    });


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


          if (entry.points !== prevPoints) {
            rank = position;
          }


          prevPoints = entry.points;
    const existing = document.getElementById("competitionLeaderboard");
    if (existing) existing.remove();


          let displayRank;
    const compContainer = document.createElement("div");
          if (rank === 1) displayRank = "🥇 1";
    compContainer.id = "competitionLeaderboard";
          else if (rank === 2) displayRank = "🥈 2";
    compContainer.style.marginBottom = "20px";
          else if (rank === 3) displayRank = "🥉 3";
          else displayRank = rank;


          const row = document.createElement("tr");
    const title = document.createElement("h2");
    title.innerText = "🏆 Competition Leaderboard";
    compContainer.appendChild(title);


          row.innerHTML = `
    const tableEl = document.createElement("table");
            <td>${displayRank}</td>
    tableEl.className = "wikitable";
            <td>${entry.player}</td>
    tableEl.style.width = "50%";
            <td>${entry.points}</td>
          `;


          tableEl.appendChild(row);
    tableEl.innerHTML = `
      <tr>
        <th>Rank</th>
        <th>Player</th>
        <th>Points</th>
      </tr>
    `;


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


        compContainer.appendChild(tableEl);
    leaderboard.forEach((entry) => {


        // Insert leaderboard above table
      position++;
        table.parentNode.insertBefore(compContainer, table);
 
        // Insert filters UNDER leaderboard
        compContainer.insertAdjacentElement("afterend", wrapper);


      if (entry.points !== prevPoints) {
        rank = position;
       }
       }


      prevPoints = entry.points;


       function filterTable() {
       let displayRank;
      if (rank === 1) displayRank = "🥇 1";
      else if (rank === 2) displayRank = "🥈 2";
      else if (rank === 3) displayRank = "🥉 3";
      else displayRank = rank;


        const selectedDungeon = dungeonSelect.value;
      const row = document.createElement("tr");
        const playerSearch = playerInput.value.toLowerCase();


         const rows = Array.from(table.querySelectorAll("tr")).slice(1);
      row.innerHTML = `
        <td>${displayRank}</td>
         <td>${entry.player}</td>
        <td>${entry.points}</td>
      `;


        rows.forEach(row => {
      tableEl.appendChild(row);


          const dungeon = row.cells[1].innerText.trim();
    });
          const player = row.cells[2].innerText.toLowerCase();


          row.style.display =
    compContainer.appendChild(tableEl);
            (selectedDungeon === "All" || dungeon === selectedDungeon) &&
            player.includes(playerSearch)
              ? ""
              : "none";
 
        });
 
      }


      dungeonSelect.addEventListener("change", filterTable);
    // Insert leaderboard ABOVE table
      playerInput.addEventListener("input", filterTable);
    table.parentNode.insertBefore(compContainer, table);


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


    }
   }
   }




   /* ===========================
   /* ===========================
     PARTY TABLE (unchanged)
     FILTERING (TABLE ONLY)
     =========================== */
     =========================== */


   const partyTable = document.getElementById("partyLeaderboardTable");
   function filterTable() {


  if (partyTable) {
    const selectedDungeon = dungeonSelect.value;
    const playerSearch = playerInput.value.toLowerCase();


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


      const rows = Array.from(partyTable.querySelectorAll("tr")).slice(1);
    rows.forEach(row => {


       rows.sort((a,b) => {
       const dungeon = row.cells[1].innerText.trim();
      const player = row.cells[2].innerText.toLowerCase();


        const dungeonA = a.cells[1].innerText.trim();
      row.style.display =
         const dungeonB = b.cells[1].innerText.trim();
        (selectedDungeon === "All" || dungeon === selectedDungeon) &&
         player.includes(playerSearch)
          ? ""
          : "none";


        if (dungeonA < dungeonB) return -1;
    });
        if (dungeonA > dungeonB) return 1;


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


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


      rows.forEach(row => partyTable.appendChild(row));


      let currentDungeon = "";
  /* ===========================
      let dungeonRank = 0;
    INIT
      let prevTime = null;
    =========================== */
      let position = 0;
 
      rows.forEach(row => {
 
        const dungeon = row.cells[1].innerText.trim();
        const time = parseTime(row.cells[5].innerText);
        const rankCell = row.cells[0];
 
        if (dungeon !== currentDungeon) {
          currentDungeon = dungeon;
          dungeonRank = 1;
          position = 1;
          prevTime = time;
        } else {
          position++;
 
          if (time !== prevTime) {
            dungeonRank = position;
          }
 
          prevTime = time;
        }
 
        rankCell.innerText = dungeonRank;


      });
  sortAndRank();
 
  buildCompetitionLeaderboard();
    }
 
    sortAndRankParty();
  }


});
});

Revision as of 14:12, 18 March 2026

/* Any JavaScript here will be loaded for all users on every page load. */

mw.hook('wikipage.content').add(function ($content) {
  $content.find('.spoiler').on('click', function () {
    $(this).toggleClass('revealed');
  });
});


/* ===========================
   Dungeon Speedrun Leaderboard
   =========================== */

mw.hook('wikipage.content').add(function () {

  function parseTime(t) {
    if (!t || !t.includes(":")) return 999999;
    const parts = t.trim().split(":");
    return parseInt(parts[0], 10) * 60 + parseInt(parts[1], 10);
  }


  const table = document.getElementById("leaderboardTable");

  if (!table) return;


  /* ===========================
     CREATE FILTER UI (ALWAYS)
     =========================== */

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

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

  const dungeonLabel = document.createElement("strong");
  dungeonLabel.innerText = "Dungeon: ";
  wrapper.appendChild(dungeonLabel);

  const dungeonSelect = document.createElement("select");
  dungeonSelect.style.marginRight = "20px";

  const dungeons = [
    "All",
    "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");
    option.value = name;
    option.textContent = name;
    dungeonSelect.appendChild(option);
  });

  wrapper.appendChild(dungeonSelect);

  const playerLabel = document.createElement("strong");
  playerLabel.innerText = "Player: ";
  wrapper.appendChild(playerLabel);

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


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

  function sortAndRank() {

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

    rows.sort((a,b) => {

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

      if (dungeonA < dungeonB) return -1;
      if (dungeonA > dungeonB) return 1;

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

    });

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

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

    rows.forEach(row => {

      const dungeon = row.cells[1].innerText.trim();
      const time = parseTime(row.cells[4].innerText);
      const rankCell = row.cells[0];

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

        if (time !== prevTime) {
          dungeonRank = position;
        }

        prevTime = time;
      }

      rankCell.innerText = dungeonRank;

    });

  }


  /* ===========================
     COMPETITION LEADERBOARD
     =========================== */

  function buildCompetitionLeaderboard() {

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

    const playerPoints = {};

    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 => {

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

      if (!player || isNaN(rank)) return;

      const points = getPoints(rank);

      playerPoints[player] = (playerPoints[player] || 0) + points;

    });

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


    const existing = document.getElementById("competitionLeaderboard");
    if (existing) existing.remove();

    const compContainer = document.createElement("div");
    compContainer.id = "competitionLeaderboard";
    compContainer.style.marginBottom = "20px";

    const title = document.createElement("h2");
    title.innerText = "🏆 Competition Leaderboard";
    compContainer.appendChild(title);

    const tableEl = document.createElement("table");
    tableEl.className = "wikitable";
    tableEl.style.width = "50%";

    tableEl.innerHTML = `
      <tr>
        <th>Rank</th>
        <th>Player</th>
        <th>Points</th>
      </tr>
    `;

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

    leaderboard.forEach((entry) => {

      position++;

      if (entry.points !== prevPoints) {
        rank = position;
      }

      prevPoints = entry.points;

      let displayRank;
      if (rank === 1) displayRank = "🥇 1";
      else if (rank === 2) displayRank = "🥈 2";
      else if (rank === 3) displayRank = "🥉 3";
      else displayRank = rank;

      const row = document.createElement("tr");

      row.innerHTML = `
        <td>${displayRank}</td>
        <td>${entry.player}</td>
        <td>${entry.points}</td>
      `;

      tableEl.appendChild(row);

    });

    compContainer.appendChild(tableEl);

    // Insert leaderboard ABOVE table
    table.parentNode.insertBefore(compContainer, table);

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

  }


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

  function filterTable() {

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

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

    rows.forEach(row => {

      const dungeon = row.cells[1].innerText.trim();
      const player = row.cells[2].innerText.toLowerCase();

      row.style.display =
        (selectedDungeon === "All" || dungeon === selectedDungeon) &&
        player.includes(playerSearch)
          ? ""
          : "none";

    });

  }

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


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

  sortAndRank();
  buildCompetitionLeaderboard();

});