GreasyFork显示优化

链接改为图标按钮

// ==UserScript==
// @name         GreasyFork显示优化
// @namespace    http://tampermonkey.net/
// @version      2.5
// @description  链接改为图标按钮
// @author       ssnangua
// @match        https://greasyfork.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  const $ = (selector) => document.querySelector(selector);
  const $$ = (selector) => [...document.querySelectorAll(selector)];

  const R = (pattern) => new RegExp(pattern);
  const iconList = [
    { icon: "🎨", pathname: "/script_versions/new", search: "language=css" },
    { icon: "👨‍💻", pathname: "/script_versions/new" },
    { icon: "🗃️", pathname: R`/users/.*/sets/new$` },
    { icon: "📥", pathname: "/import" },
    { icon: "🛠️", pathname: "/webhook-info" },
    { icon: "🙍🏻‍♂️", pathname: "/users/edit" },
    { icon: "🔏", pathname: "/users/edit_sign_in" },
    { icon: "💬", pathname: "/notifications" },
    { icon: "🗨️", pathname: "/notification_settings" },
    { icon: "🗑️", pathname: "/users/delete_info" },
    { icon: "🏃", pathname: "/users/sign_out" },

    { icon: "📅", pathname: "/discussions", search: "user=" },

    { icon: "💬", pathname: R`/users/.*/conversations/new$` },

    { icon: "⭐", pathname: "/scripts", search: "set=" },
    { icon: "✏️", pathname: R`/users/.*/sets/.*/edit$` },
    { icon: "✏️", pathname: R`/users/.*/sets/new$`, search: "fav=1" },

    { icon: "🔔", pathname: R`/scripts/.*/discussions/.*/subscribe$` },
    { icon: "🔕", pathname: R`/scripts/.*/discussions/.*/unsubscribe$` },
  ];

  function findIcon($a) {
    const item = iconList.find((item) => {
      // pathname
      if (item.pathname instanceof RegExp) {
        if (!item.pathname.test($a.pathname)) return false;
      } else if (typeof item.pathname === "string") {
        if (!$a.pathname.endsWith(item.pathname)) return false;
      }
      // search
      if (item.search instanceof RegExp) {
        if (!item.search.test($a.search)) return false;
      } else if (typeof item.search === "string") {
        if (!$a.search.includes(item.search)) return false;
      }
      return true;
    });
    return item?.icon || "";
  }

  function createIconButton($a, icon) {
    const $button = document.createElement("button");
    icon = icon || findIcon($a);
    const text = $a.textContent.trim();
    $button.textContent = `${icon} ${text}`.trim();
    $button.dataset.href = $a.getAttribute("href");
    $button.addEventListener("click", () => (location.href = $a.href));
    return $button;
  }

  function replaceLinkToIconButton($a, icon) {
    if (!$a) return;
    const $button = createIconButton($a, icon);
    $button.className = $a.className;
    Object.assign($button.dataset, $a.dataset);
    $a.replaceWith($button);
  }

  function replaceLinkListToIconButtonList($p) {
    if (!$p) return;
    const $new = document.createElement("p");
    $new.className = "icon-button-list";
    $p.querySelectorAll("a").forEach(($a) => $new.appendChild(createIconButton($a)));
    if ($new.children.length > 0) $p.replaceWith($new);
  }

  function linkAddIcon($a, icon) {
    if (!$a) return;
    icon = icon || findIcon($a);
    $a.textContent = `${icon} ${$a.textContent}`.trim();
  }

  function apply() {
    replaceLinkListToIconButtonList($$("#about-user>section, #about-user>p").pop());
    replaceLinkListToIconButtonList($$("#user-discussions>section>p").pop());
    replaceLinkToIconButton($("#user-conversations a"));

    linkAddIcon($(".discussion-subscribe"));
    linkAddIcon($(".discussion-unsubscribe"));
    $$("a.quote-comment").forEach(($a) => linkAddIcon($a, "❝"));
    $$("a.report-link").forEach(($a) => linkAddIcon($a, "⚠️"));

    const $ul = $("ul#user-script-sets");
    if ($ul) {
      const $new = document.createElement("p");
      $new.className = "icon-button-list";
      $ul.querySelectorAll("a").forEach(($a) => $new.appendChild(createIconButton($a)));
      $ul.replaceWith($new);
    }
  }
  apply();

  const observer = new MutationObserver(function (mutationsList, observer) {
    for (let mutation of mutationsList) {
      if (mutation.addedNodes[0]?.tagName === "BODY") apply();
    }
  });
  observer.observe(document.body.parentElement, { childList: true });

  GM_addStyle(`
    a {
      text-decoration: none;
      &:hover {
        text-decoration: underline;
      }
    }

    .icon-button-list {
      display: flex;
      flex-flow: row wrap;
      gap: 4px;
    }
  `);
})();