FFProgs

Minor improvements to FFlogs and progression tools.

目前為 2022-04-04 提交的版本,檢視 最新版本

// ==UserScript==
// @name         FFProgs
// @namespace    http://tampermonkey.net/
// @version      0.1.0
// @description  Minor improvements to FFlogs and progression tools.
// @author       You
// @match        https://www.fflogs.com/*
// @icon         https://assets.rpglogs.com/img/ff/favicon.png?v=2
// @grant        none
// ==/UserScript==

(function () {
  if (/(^|\.)fflogs\.com$/.test(document.location.hostname) === false) { return; };

  // Helper functions
  function elements(arr) {
    return arr.map(e => document.getElementById(e));
  }

  function addGlobalEventListener(type, selector, callaBack) {
    document.addEventListener(type, e => {
      if (e.target.matches(selector)) callaBack(e);
    })
  }

  function retrieveWindowVariables(variables) {
    const ret = {};

    let scriptContent = "";
    for (let i = 0; i < variables.length; i++) {
      const currVariable = variables[i];
      scriptContent += `if (typeof ${currVariable} !== \"undefined\") $(\"body\").attr(\"tmp_${currVariable}\", JSON.stringify(${currVariable}));\n`
    }

    const script = document.createElement("script");
    script.id = "tmpScript";
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);

    for (let i = 0; i < variables.length; i++) {
      const currVariable = variables[i];
      ret[currVariable] = $.parseJSON($("body").attr(`tmp_${currVariable}`));
      $("body").removeAttr(`tmp_${currVariable}`);
    }

    $("#tmpScript").remove();

    return ret;
  }

  // Easter Egg/Credits
  const characterName = document.querySelector("#character-name > .character-name-link");
  if (characterName) {
    if (characterName.innerHTML === "Chad Bradly") {
      // GIGACHAD
      const characterPortrait = document.getElementById("character-portrait-image");
      characterPortrait.src = "https://c.tenor.com/epNMHGvRyHcAAAAd/gigachad-chad.gif";

      // Adds background to your own page
      const addBackgroundList = ["portrait-and-basics", "character-header-customize-action-box", "update-box"];
      elements(addBackgroundList).forEach((e) => {
        if (e) {
          e.className = "slightly-transparent-box";
        }
      })
      const banner = document.getElementById("character-portrait-box");
      if (banner) {
        banner.style = "background-image: url(\"https://cdn.discordapp.com/attachments/613521566185029642/925189465868218368/ffxiv_dx11_sH6dfhzjue.jpg\");";
        banner.className = "with-banner";
      }
    }
  }

  // Adblock
  const deleteList = ["top-banner", "bottom-banner", "playwire-video-container", "patron-box", "gear-box-ad"];
  elements(deleteList).forEach(e => {
    if (e) {
      e.outerHTML = "";
    }
  });

  // Removes alt-text from item images.
  const imgs = document.getElementsByClassName("gear-img-cell");
  for (let i = 0; i < imgs.length; i++) {
    const img = imgs[i].firstChild;
    if (img) {
      img.alt = "";
    }
  }

  // XIVAnalysis button
  const tabs = document.getElementById("top-level-view-tabs");
  if (tabs) {
    function getReportUrl() {
      const reportURL = document.location.href.split("/reports/")[1];
      const [report, reportInfo] = reportURL.split("#");
      let fight, job;
      if (reportInfo) {
        reportInfo.split("&").forEach((e) => {
          const [key, value] = e.split("=")
          if (key === "fight") {
            fight = value;
          }
          if (fight && key === "source") {
            job = value;
          }
        })
      }
      let url = "https://xivanalysis.com/fflogs";
      [report, fight, job].forEach((urlElement) => {
        if (urlElement) {
          url += `/${urlElement}`;
        }
      });
      return url;
    }

    function refreshAnalysis() {
      const xivanalysisButton = document.getElementById("xivanalysis-tab");
      const url = getReportUrl();
      xivanalysisButton.href = url;
    }

    const url = getReportUrl();
    const xivanalysisButton = document.createElement("a");
    xivanalysisButton.href = url;
    xivanalysisButton.target = "_blank";
    xivanalysisButton.classList.add("big-tab", "view-type-tab");
    xivanalysisButton.id = "xivanalysis-tab";

    const icon = document.createElement("span");
    icon.classList.add("zmdi", "zmdi-time-interval");
    xivanalysisButton.appendChild(icon);

    const text = document.createElement("span");
    text.classList.add("big-tab-text");
    text.innerHTML = "<br>xivanalysis";
    xivanalysisButton.appendChild(text);

    tabs.firstChild.before(xivanalysisButton);

    tabs.addEventListener("click", (e) => { refreshAnalysis(); });
  }

  //Video Player Stuff
  const videoButton = document.querySelector(".replay-video");
  if (videoButton) {
    const streams = {};
    videoButton.addEventListener("click", (e) => {
      const selectVideoButton = document.getElementById("select-video");
      if (selectVideoButton) {
        // Functions for the multistream buttons.
        const videoFrame = document.getElementById("video-frame-inner");

        function showMultistreamFrame() {
          const platform = "youTube"
          const iframe = document.createElement("iframe");
          iframe.id = "player";
          iframe.style = "border: none; width:100%; height: 100%;";
          iframe.allowFullscreen = "1";
          iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture;";
          iframe.title = "YouTube video player";

          videoFrame.innerHTML = iframe.outerHTML;
        }

        function showMultistreamOptions() {
          const div = document.createElement("div");
          div.style = "text-align: center;";

          const form = document.createElement("form");
          form.style = "margin: 0;";
          form.acceptCharset = "utf-8";
          form.method = "GET";
          form.action = "javascript:void(0);";

          const table = document.createElement("table");
          table.style = "border-collapse: separate; border-spacing: 8px; margin: auto; text-align: left;";

          //Table Infromation
          const infoRow = document.createElement("tr");
          const nameInfo = document.createElement("td");
          const URLInfo = document.createElement("td");
          const offsetInfo = document.createElement("td");
          nameInfo.innerHTML = "Name:";
          URLInfo.innerHTML = "Stream URL:";
          offsetInfo.innerHTML = "Offset:";
          offsetInfo.style = "max-width: 60px;";

          infoRow.append(nameInfo, URLInfo, offsetInfo);
          table.appendChild(infoRow);

          const raidFrames = document.querySelectorAll(".raid-frame-contents");
          const raiders = [];
          raidFrames.forEach((frame) => {
            raiders.push(frame.innerHTML);
          })

          raiders.forEach((person) => {
            const id = person.replace(/ /g, "_");

            const tableRow = document.createElement("tr");
            const nameData = document.createElement("td");
            nameData.innerHTML = person;
            tableRow.appendChild(nameData);
            const streamData = document.createElement("td");
            const streamUrl = document.createElement("input");
            streamUrl.style = "min-width: 260px;"
            streamUrl.type = "text";
            streamUrl.id = `${id}-stream_url`;
            streamUrl.name = `${id}-stream_url`;
            streamUrl.placeholder = "youtube.com/watch?v= or twitch.tv/videos/";
            if (streams[id] && streams[id].url) {
              streamUrl.setAttribute("value", streams[id].url);
            }
            streamUrl.classList.add("url-table-row");
            streamData.appendChild(streamUrl);
            tableRow.appendChild(streamData);

            const offsetData = document.createElement("td");
            const offsetTime = document.createElement("input");
            offsetTime.size = "3";
            offsetTime.id = `${id}-stream_offset`;
            offsetTime.name = `${id}-stream_offset`;
            offsetTime.placeholder = "# in sec";
            if (streams[id] && streams[id].offset) {
              offsetTime.setAttribute("value", streams[id].offset)
            }
            offsetTime.classList.add("url-table-row");

            offsetData.appendChild(offsetTime);
            tableRow.appendChild(offsetData);

            table.appendChild(tableRow);
          })

          form.appendChild(table);
          div.appendChild(form);
          videoFrame.innerHTML = div.outerHTML;
        }

        addGlobalEventListener("input", ".url-table-row", (e) => {
          const [user, action] = e.target.id.split("-");
          const value = e.target.value;
          if (action === "stream_url") {
            if (!streams[user]) streams[user] = {};
            streams[user].url = value;
          }
          if (action === "stream_offset") {
            if (!streams[user]) streams[user] = {};
            streams[user].offset = value;
          }
        })

        // Creates Menu Below Video Player
        const multiStreamOptions = document.getElementById("multistream-options");
        if (!multiStreamOptions) {

          const videoFrameControls = document.getElementById("video-frame-controls");
          const multiStreamO = document.createElement("span");
          multiStreamO.style = "float: right; margin-right: 10px;";
          multiStreamO.id = "multistream-options";
          multiStreamO.classList.add("graph-legend-button");
          multiStreamO.onclick = showMultistreamOptions;
          multiStreamO.innerText = "Multistream options"
          videoFrameControls.appendChild(multiStreamO);

          const multiStreamV = document.createElement("span");
          multiStreamV.style = "margin-right: -1px; float: right;";
          multiStreamV.id = "multistream-view";
          multiStreamV.classList.add("graph-legend-button");
          multiStreamV.onclick = showMultistreamFrame;
          multiStreamV.innerText = "Multistream View";
          videoFrameControls.appendChild(multiStreamV);
        }

        // Save Video URL (OLD)
        selectVideoButton.addEventListener("click", (e) => {
          const windowVariables = retrieveWindowVariables(["videoID", "videoOffset"]);
          if (windowVariables.videoID !== "none") {
            const videoURLInput = document.getElementById("video_url");
            videoURLInput.value = `https://www.youtube.com/watch?v=${windowVariables.videoID}`;
          }
          if (windowVariables.videoOffset) {
            const videoOffsetInput = document.getElementById("video_offset");
            videoOffsetInput.value = windowVariables.videoOffset;
          }
        })
      }
    })
  }



  /*
  Backlog:
    Make youtube/twitch livestream work
    Streams work across multiple fights in one log.
    <- Upload to greasy fork here 0.1.0 ->
    Multiple POV"s depending on selected player.
    Add way to share log viewer and save current streams to a log.

  Doing:
    Make youtube videos work.
    Make Twitch VOD"s work.
    Import/export settings
    Use my Own I frames


    Done:
    Save video inputted into video player,
    Make settings be able to add streams to players
    Save Stream/Offset to session storage on edit for the zones so no need for submit

  */
})();