// ==UserScript==
// @name FF Progs
// @name:en FF Progs
// @namespace k_fizzel
// @version 1.0.1
// @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-fallback, #right-ad-box, #bottom-banner-fallback, #subscription-message-tile-container, #right-vertical-banner, #playwire-video-container").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(`(͠≖ ͜ʖ͠≖)`);
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 (/\/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 removeAdGearset = () => {
const elements = document.querySelectorAll('[id^="etro-ad"]');
for (const element of elements) {
if (element) {
element.remove();
}
}
}
const observer = new MutationObserver(removeAdGearset);
observer.observe(document.querySelector(".mantine-AppShell-main"), { attributes: true, characterData: true, childList: true });
}
})();