t66y助手

优化t66y网页使用体验

Pada tanggal 26 November 2023. Lihat %(latest_version_link).

// ==UserScript==
// @name         t66y助手
// @namespace    com.t66y.jujufatu
// @author       jujufatu
// @version      0.0.1
// @description  优化t66y网页使用体验
// @license      CC BY-NC-ND 4.0
// @match        http*://*t66y.com/*
// @grant        GM_setClipboard
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

//code start
(function () {
  "use strict";
  // 访问页面的原始window对象
  var pageWindow = unsafeWindow;

  // 去除无效图床,定义需要检查的域名列表
  const BLOCK_IMG_LIST = [
    "23img.com",
    "bdimg.com",
    "sinaimg.cn",
    "ovkwiz.xyz",
    "huluxia.com",
    "imgs.moe",
  ];
  // 替换显示图片:svg:过期图床
  const BAD_IMG_SVG =
    "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB3aWR0aD0iMjAwcHgiIGhlaWdodD0iMjAwcHgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICAgICAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMTBiMDE4IiAvPgogICAgICAgICAgPHRleHQgeD0iNTAlIiB5PSI3MS40IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjZjdjNTAwIiBmb250LXNpemU9IjUyIiBmb250LWZhbWlseT0iQXJpYWwiPjx0c3BhbiB4PSI1MCUiIGR5PSIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ov4fmnJ88L3RzcGFuPjx0c3BhbiB4PSI1MCUiIGR5PSI1Ny4yIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7lm77luoo8L3RzcGFuPjwvdGV4dD48L3N2Zz4=";
  // dom刚加载完成,图片脚本等其他资源还在加载中
  document.addEventListener("DOMContentLoaded", function () {
    // 热门异常图床图片替换,通常是头像
    replaceImg();
    // 找到每个楼层的磁力并替换
    let floors = [...document.querySelectorAll(".tpc_content")];
    floors.forEach((floor) => {
      floor.innerHTML = createMagnetLink(floor.innerHTML);
    });
    // 初始评论,后续点击加载更多评论在loadComment中处理
    let initComments = [...document.querySelectorAll(".post_cont")];
    initComments.forEach((c) => {
      c.innerHTML = createMagnetLink(c.innerHTML);
    });
    // 处理加载评论
    if (typeof pageWindow.loadComment === "function") {
      const originalLoadComment = pageWindow.loadComment;
      pageWindow.loadComment = function () {
        var data = pageWindow["comm" + arguments[0]];
        for (var key in data) {
          //----------修改的内容--------------
          data[key].c = createMagnetLink(data[key].c);
          //----------修改的内容--------------
        }
        originalLoadComment.apply(this, arguments); // 调用原始函数
      };
    }
  });

  // 将传入的字符中的磁力替换为a标签地址
  function createMagnetLink(str) {
    str = decodeURIComponent(str);
    str = decodeHtmlEntities(str);
    // 正则表达式匹配哈希值、可选的dn和tr参数
    const magnetRegex =
      /(?<!<[^>]*)(?:magnet:\?xt=urn:btih:)?([a-fA-F0-9]{40})(?:&dn=([^&\s]+))?(?:&tr=([^&\s]+))?/gi;

    // 替换函数,用于生成a标签
    function replaceFunc(match, hash, dn, tr, offset, string) {
      const magnetPrefix = "magnet:?xt=urn:btih:";
      const decodedDn = dn
        ? `磁力链接-${decodeURIComponent(dn)}`
        : `磁力链接-${hash}`;
      const trParam = tr ? `&tr=${encodeURIComponent(tr)}` : "";
      const fullMatch = string.substring(
        offset,
        string.indexOf(" ", offset) > -1
          ? string.indexOf(" ", offset)
          : string.length
      );

      // 判断并添加磁力链接前缀
      let href = match.startsWith(magnetPrefix)
        ? fullMatch
        : `${magnetPrefix}${hash}${
            dn ? `&dn=${encodeURIComponent(dn)}` : ""
          }${trParam}`;
      href = href.replace(/\s+/g, "");
      return `<a href="${href}">${decodedDn}</a>&nbsp;|&nbsp;<a style="cursor: pointer;" onclick="copy('${href}')">复制</a>`;
    }

    // 使用正则表达式替换符合条件的部分
    return str.replace(magnetRegex, replaceFunc);
  }
  // 将失效图床的图片替换成默认头像
  function replaceImg() {
    // 初次进入页面直接遍历修改
    // 获取所有img标签
    const images = document.getElementsByTagName("img");
    // 遍历每个img标签
    for (const img of images) {
      // 如果src符合条件
      if (BLOCK_IMG_LIST.some((domain) => img.src.includes(domain))) {
        img.src = BAD_IMG_SVG;
      }
    }
    // 后续监听dom
    const isBlockedSrc = (src) =>
      BLOCK_IMG_LIST.some((blockedDomain) => src.includes(blockedDomain));
    const replaceSrc = (node) => {
      if (node.tagName === "IMG" && isBlockedSrc(node.src)) {
        node.src = BAD_IMG_SVG;
      }
    };

    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === "childList") {
          mutation.addedNodes.forEach((node) => {
            if (node.nodeType === Node.ELEMENT_NODE) {
              // 检查和替换 node 自身(如果它是 img)
              replaceSrc(node);
              // 遍历所有子节点
              node.querySelectorAll("img").forEach(replaceSrc);
            }
          });
        } else if (
          mutation.type === "attributes" &&
          mutation.attributeName === "src"
        ) {
          replaceSrc(mutation.target);
        }
      });
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ["src"],
    });
  }

  // 解码html实体符号
  function decodeHtmlEntities(str) {
    return str
      .replace(/&quot;/g, '"')
      .replace(/&apos;/g, "'")
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&nbsp;/g, " ")
      .replace(/&iexcl;/g, "¡")
      .replace(/&cent;/g, "¢")
      .replace(/&pound;/g, "£")
      .replace(/&curren;/g, "¤")
      .replace(/&yen;/g, "¥")
      .replace(/&brvbar;/g, "¦")
      .replace(/&sect;/g, "§")
      .replace(/&uml;/g, "¨")
      .replace(/&copy;/g, "©")
      .replace(/&ordf;/g, "ª")
      .replace(/&laquo;/g, "«")
      .replace(/&not;/g, "¬")
      .replace(/&shy;/g, "­")
      .replace(/&reg;/g, "®")
      .replace(/&macr;/g, "¯")
      .replace(/&deg;/g, "°")
      .replace(/&plusmn;/g, "±")
      .replace(/&sup2;/g, "²")
      .replace(/&sup3;/g, "³")
      .replace(/&acute;/g, "´")
      .replace(/&micro;/g, "µ")
      .replace(/&para;/g, "¶")
      .replace(/&middot;/g, "·")
      .replace(/&cedil;/g, "¸")
      .replace(/&sup1;/g, "¹")
      .replace(/&ordm;/g, "º")
      .replace(/&raquo;/g, "»")
      .replace(/&frac14;/g, "¼")
      .replace(/&frac12;/g, "½")
      .replace(/&frac34;/g, "¾")
      .replace(/&iquest;/g, "¿")
      .replace(/&amp;/g, "&");
    // 添加更多实体替换规则
    // 注意: 如果增加新的替换规则,请确保不会互相冲突
    // 例如,&amp; 必须最后替换,以免在替换其他实体时干扰 & 符号
  }

  // 复制文本到剪切板
  pageWindow.copy = function (str) {
    GM_setClipboard(str);
    Message({ msg: "复制成功", duration: 666 });
  };

  // Message 工具函数
  function Message({ msg, duration }) {
    // 创建消息元素
    const messageElement = document.createElement("div");

    // 设置消息文本
    messageElement.textContent = msg;

    // 设置消息样式
    messageElement.style.position = "fixed";
    messageElement.style.top = "66px";
    messageElement.style.left = "50%";
    messageElement.style.transform = "translate(-50%, -50%)";
    messageElement.style.backgroundColor = "#10b018";
    messageElement.style.color = "#f7c500";
    messageElement.style.fontSize = "3rem";
    messageElement.style.padding = "20px";
    messageElement.style.boxShadow = "0 4px 8px rgba(0,0,0,0.1)";
    messageElement.style.borderRadius = "10px";
    messageElement.style.zIndex = "1000";
    messageElement.style.transition = "opacity 0.5s, transform 0.5s";
    messageElement.style.opacity = "0";

    // 将消息元素添加到文档中
    document.body.appendChild(messageElement);

    // 动画效果:淡入
    setTimeout(() => {
      messageElement.style.opacity = "1";
      messageElement.style.transform = "translate(-50%, -50%) scale(1)";
    }, 10); // 使用setTimeout来确保元素已经插入DOM

    // 设置显示时长
    setTimeout(() => {
      // 动画效果:淡出
      messageElement.style.opacity = "0";
      messageElement.style.transform = "translate(-50%, -50%) scale(0.9)";

      // 动画完成后从文档中移除消息元素
      setTimeout(() => {
        document.body.removeChild(messageElement);
      }, 500); // 与transition的时间一致
    }, duration);
  }
  /*************************** 自定义样式 ***************************/
  // 创建并插入样式
  let style = document.createElement("style");
  document.head.appendChild(style);
  style.sheet.insertRule(
    `.bad_bg { background-image: url("${BAD_IMG_SVG}") !important; }`,
    0
  );
})();