FF Progs

Adds a Button to view logs in xivanalysis also some minor improvements.

目前为 2023-04-30 提交的版本。查看 最新版本

// ==UserScript==
// @name                FF Progs
// @name:en             FF Progs
// @namespace           k_fizzel
// @version             1.0.3
// @author              Chad Bradly
// @description         Adds a Button to view logs in xivanalysis also some minor improvements.
// @description:en      Adds a Button to view logs in xivanalysis also some minor improvements.
// @website             https://www.fflogs.com/character/id/12781922
// @icon                https://assets.rpglogs.com/img/ff/favicon.png?v=2
// @match               https://*.etro.gg/*
// @match               https://*.fflogs.com/*
// @grant               unsafeWindow
// @grant               GM_addStyle
// @grant               GM_getValue
// @grant               GM_setValue
// @grant               GM_deleteValue
// @license             MIT License
// ==/UserScript==

(function () {
  "use strict";
  //check if the domain is fflogs.com
  if (/fflogs\.com/.test(location.hostname)) {
    // Adblocker
    $("#top-banner, .side-rail-ads, #bottom-banner, #subscription-message-tile-container, #playwire-video-container, #right-ad-box, #right-vertical-banner").remove();
    $("#table-container").css("margin", "0 0 0 0");

    if (/\/reports\/.+/.test(location.pathname)) {
      // Add XIV Analysis Button
      $("#filter-analyze-tab").before(
        `<a href="https://xivanalysis.com/report-redirect/${location.href}" target="_blank" class="big-tab view-type-tab" id="xivanalysis-tab"><span class="zmdi zmdi-time-interval"></span> <span class="big-tab-text"><br>xivanalysis</span></a>`
      );
      $("#xivanalysis-tab").click(() => {
        $("#xivanalysis-tab").attr("href", `https://xivanalysis.com/report-redirect/${location.href}`);
      });

      let jobs;
      const rankOnes = {};
      const tableContainer = document.querySelector("#table-container");

      const getHash = () => {
        return location.hash
          .replace("#", "")
          .split("&")
          .reduce((res, item) => {
            const parts = item.split("=");
            res[parts[0]] = parts[1];
            return res;
          }, {});
      };

      const characterAllStar = (rank, outOf, rDPS, rankOneRDPS) => {
        return Math.min(Math.max(100 * (rDPS / rankOneRDPS), 100 - (rank / outOf) * 100) + 20 * (rDPS / rankOneRDPS), 120);
      };

      const addASPToTables = () => {
        if (getHash().view === "rankings") {
          if (!GM_getValue("apiKey")) return;
          const rows = [];
          if (!jobs) {
            fetch(`https://www.fflogs.com/v1/classes?api_key=${GM_getValue("apiKey")}`)
              .then((res) => res.json())
              .then((data) => {
                jobs = data[0].specs;
                rows.forEach((row) => {
                  updatePoints(row);
                });
              })
              .catch((err) => console.error(err));
          } else {
            setTimeout(() => {
              rows.forEach((row) => {
                updatePoints(row);
              });
            }, 0);
          }

          const updatePoints = async (row) => {
            const rank = Number($(row).find("td:nth-child(2)").text().replace("~", ""));
            const outOf = Number($(row).find("td:nth-child(3)").text().replace(",", ""));
            const dps = Number($(row).find("td:nth-child(6)").text().replace(",", ""));
            const jobName = $(row).find("td:nth-child(5) > a").attr("class") || "";
            const jobName2 = $(row).find("td:nth-child(5) > a:nth-last-child(1)").attr("class") || "";
            const playerMetric = getHash().playermetric || "rdps";

            if (jobName2 !== "players-table-realm") {
              $(row)
                .find("td:nth-child(7)")
                .html(`<center><img src="https://cdn.7tv.app/emote/62523dbbbab59cfd1b8b889d/1x.webp" title="no api endpoint for combind damage" style="height: 15px;"></center>`);
              return;
            }

            const updateCharecterAllStar = async () => {
              $(row).find("td:nth-child(7)").html(characterAllStar(rank, outOf, dps, rankOnes[jobName][playerMetric]).toFixed(2));
            };

            if (!rankOnes[jobName]) {
              rankOnes[jobName] = {};
            }

            if (!rankOnes[jobName][playerMetric]) {
              const url = `https://www.fflogs.com/v1/rankings/encounter/${reportsCache.filterFightBoss}?metric=${playerMetric}&spec=${
                jobs.find((job) => job.name.replace(" ", "") === jobName)?.id
              }&api_key=${GM_getValue("apiKey")}`;
              fetch(url)
                .then((res) => res.json())
                .then((data) => {
                  rankOnes[jobName][playerMetric] = Number(data.rankings[0].total.toFixed(1));
                  updateCharecterAllStar();
                })
                .catch((err) => console.error(err));
            } else {
              updateCharecterAllStar();
            }
          };

          $(".player-table").each((_i, table) => {
            $(table)
              .find("thead tr th:nth-child(6)")
              .after(
                `<th class="sorting ui-state-default" tabindex="0" aria-controls="DataTables_Table_0" rowspan="1" colspan="1" aria-label="Patch: activate to sort column ascending"><div class="DataTables_sort_wrapper">Points<span class="DataTables_sort_icon css_right ui-icon ui-icon-caret-2-n-s"></span></div></th>`
              );
            $(table)
              .find("tbody tr")
              .each((_i, row) => {
                $(row)
                  .find("td:nth-child(6)")
                  .after(`<td class="rank-per-second primary main-table-number"><center><span class="zmdi zmdi-spinner zmdi-hc-spin" style="color:white font-size:24px"></center></span></td>`);
                rows.push(row);
              });
          });
        }
      };

      if (tableContainer) {
        const observer = new MutationObserver(addASPToTables);
        observer.observe(tableContainer, { attributes: true, characterData: true, childList: true });
      }
    }

    if (/\/zone\/.+/.test(location.pathname)) {
      const verticalContent = document.querySelector(".vertical-content");
      const contentAndAds = document.querySelector(".content--full-width");
      const pageContainer = document.querySelector(".progress-race-page-container");

      const removeAdds = () => {
        const elements = document.querySelectorAll(".content-and-ads__ads");
        const resize = document.querySelector(".content-and-ads--with-ads");
        const resize2 = document.querySelector(".content-and-ads__content");
        if (resize) {
          resize.style = "grid-template-columns: 1fr !important;";
          resize2.style = "grid-template-columns: 1fr !important;";
        }
        for (const element of elements) {
          if (element) {
            element.remove();
          }
        }
      };

      if (verticalContent) {
        const observer = new MutationObserver(removeAdds);
        observer.observe(verticalContent, { attributes: true, characterData: true, childList: true });
      }

      if (contentAndAds) {
        const observer = new MutationObserver(removeAdds);
        observer.observe(contentAndAds, { attributes: true, characterData: true, childList: true });
      }

      if (pageContainer) {
        const observer = new MutationObserver(removeAdds);
        observer.observe(pageContainer, { attributes: true, characterData: true, childList: true });
      }
    }

    if (/\/character\/.+/.test(location.pathname)) {
      $(".table-icon").removeAttr("alt");
      const jobOrder = [
        "Paladin",
        "Warrior",
        "DarkKnight",
        "Gunbreaker",
        "WhiteMage",
        "Scholar",
        "Astrologian",
        "Sage",
        "Monk",
        "Dragoon",
        "Ninja",
        "Samurai",
        "Reaper",
        "Bard",
        "Machinist",
        "Dancer",
        "BlackMage",
        "Summoner",
        "RedMage",
      ];
      const jobList = $("#jobs-header-icons").children();
      const jobListSortedNumbers = [];
      const jobListSorted = [];

      jobOrder.forEach((job) =>
        jobList
          .children()
          .toArray()
          .forEach((jobElement, i) => {
            if (jobElement.alt === job) jobListSortedNumbers.push(i);
          })
      );
      jobListSortedNumbers.forEach((i) => jobListSorted.push(jobList[i]));
      jobList.remove();
      $("#jobs-header-icons").append(jobListSorted);

      // Chad Bradly's Profile Customization
      if (/\/character\/id\/12781922/.test(location.pathname)) {
        $("#character-portrait-image").attr("src", "https://media.tenor.com/epNMHGvRyHcAAAAd/gigachad-chad.gif");
        $("#portrait-and-basics, #character-header-customize-action-box, #update-box").addClass("slightly-transparent-box");
        $("#character-portrait-box").css("background-image", 'url("https://i.imgur.com/dbwqHIt.png")').addClass("with-banner");
      }
    }

    if (/\/profile/.test(location.pathname)) {
      let apiKey = GM_getValue("apiKey");

      $("#api").after(`<div id="extension" class="dialog-block"></div>`);
      $("#extension").append(`<div id="extension-title" class="dialog-title">FF Progs</div>`);
      $("#extension").append(`<div id="extension-content" style="margin:1em"></div>`);

      const addApiInput = () => {
        $("#extension-content").append(`<div id="extension-contents" style="margin:1em">Enter your FFLogs API Key</div>`);
        $("#extension-content").append(`<input type=text id="apiKeyInput" style="margin-left: 10px">`);
        $("#extension-content").append(`<input type=button id="apiButton" style="margin-left: 10px" value="Save API Key">`);
        $("#apiButton").click(() => {
          apiKey = $("#apiKeyInput").val();
          GM_setValue("apiKey", apiKey);
          $("#apiButton").remove();
          $("#apiKeyInput").remove();
          $("#extension-content").append(`<div id="resloved-text" style="margin:1em">API Key Saved</div>`);
          setTimeout(() => {
            $("#extension-contents").remove();
            $("#resloved-text").remove();
            removeApiInput();
          }, 2000);
        });
      };

      const removeApiInput = () => {
        $("#extension-content").append(`<input type=button id="apiDeleteButton" style="margin-left: 10px" value="Remove API Key">`);
        $("#apiDeleteButton").click(() => {
          GM_deleteValue("apiKey");
          $("#apiDeleteButton").remove();
          $("#extension-content").append(`<div id="resloved-text" style="margin:1em">API Key Removed</div>`);
          setTimeout(() => {
            $("#resloved-text").remove();
            addApiInput();
          }, 2000);
        });
      };
      if (!apiKey) {
        addApiInput();
      } else {
        removeApiInput();
      }
    }
  }

  if (/etro\.gg/.test(location.hostname)) {
    document.querySelector(".mantine-Header-root").style.marginBottom = "0px";
    const body = document.querySelector(".mantine-AppShell-main");
    const removeAdGearset = () => {
      const elements = document.querySelectorAll('[id^="etro-ad"]');

      for (const element of elements) {
        if (element) {
          element.remove();
        }
      }
    };
    if (body) {
      const observer = new MutationObserver(removeAdGearset);
      observer.observe(body, { attributes: true, characterData: true, childList: true });
    }
  }
})();