fxxk-tree-hole

优化北大树洞无限滚动,收藏搜索,页面展示等

// ==UserScript==
// @name         fxxk-tree-hole
// @namespace    xiaotianxt/fxxk-tree-hole
// @version      0.0.2
// @author       monkey
// @description  优化北大树洞无限滚动,收藏搜索,页面展示等
// @icon         https://treehole.pku.edu.cn/web/favicon.ico
// @match        *://treehole.pku.edu.cn/web/*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(o=>{if(typeof GM_addStyle=="function"){GM_addStyle(o);return}const e=document.createElement("style");e.textContent=o,document.head.append(e)})(" #copy-indicator{display:none;color:#8ae}code:has(#copy-indicator){position:relative;cursor:pointer;text-decoration:underline;color:#9bf;-webkit-user-select:none;user-select:none}code:has(#copy-indicator):hover #copy-indicator{display:block;background:#fff;padding:1px;border-radius:2px;position:absolute;top:-1.2rem;z-index:10000}.box-header-top-icon:has(#copy-indicator){overflow:unset} ");

(function () {
  'use strict';

  var __defProp = Object.defineProperty;
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  var __publicField = (obj, key, value) => {
    __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
    return value;
  };
  const c = (t, e) => (e ?? document).querySelector(t), S = async (t, e) => c(t, e) ? c(t, e) : new Promise((n) => {
    const o = new MutationObserver(() => {
      c(t, e) && (o.disconnect(), n(c(t, e)));
    });
    o.observe(document, { childList: true, subtree: true });
  });
  const LS_KEY = "NEVERGONNAGIVEYOUUP";
  class Log {
    constructor() {
      __publicField(this, "map");
      __publicField(this, "maxPid", -1);
      const jsonData = localStorage.getItem(LS_KEY) || "[]";
      this.map = new Map(JSON.parse(jsonData));
    }
    replace(data, pid) {
      var _a;
      const threads = ((_a = data == null ? void 0 : data.data) == null ? void 0 : _a.data) || [];
      if (!threads.length) {
        if (pid && this.map.has(Number(pid))) {
          threads.splice(0, 0, this.map.get(Number(pid)));
        }
        return data;
      }
      threads.forEach((t) => this.map.set(t.pid, t));
      this.save();
      let i = 0;
      let j = threads[0].pid;
      while (i < threads.length) {
        const thread = threads[i];
        if (thread.pid != j) {
          if (this.map.has(j)) {
            const recoveredThread = this.map.get(j);
            recoveredThread.text = "[⚠️被删除的树洞]" + recoveredThread.text;
            threads.splice(i, 0, recoveredThread);
          } else {
            threads.splice(i, 0, {
              ...thread,
              pid: j,
              text: "⚠️ 本树洞被删除了",
              reply: "NaN",
              likenum: "NaN"
            });
          }
          j -= 1;
          i += 1;
          continue;
        }
        j -= 1;
        i += 1;
      }
      return data;
    }
    async save() {
      console.log(JSON.stringify(Array.from(this.map)));
      localStorage.setItem(LS_KEY, JSON.stringify(Array.from(this.map)));
    }
  }
  const log = new Log();
  (async () => {
    function registerInfiniteScrollInjection() {
      const observer = new MutationObserver((mutations, _) => {
        var _a, _b;
        const drawer = (_b = (_a = mutations.find(
          (item) => {
            var _a2;
            return ((_a2 = item.addedNodes) == null ? void 0 : _a2[0]) && item.addedNodes[0].isSameNode(app.childNodes[2]);
          }
        )) == null ? void 0 : _a.addedNodes) == null ? void 0 : _b[0];
        if (!drawer)
          return;
        const infiniteScrollContainer = drawer.querySelector(".sidebar-content");
        if (!infiniteScrollContainer)
          return;
        infiniteScrollContainer.setAttribute("infinite-scroll-distance", "1000");
      });
      observer.observe(app, {
        childList: true
      });
      const originalGetById = document.getElementById;
      document.getElementById = (elementId) => {
        if (elementId == "table_list") {
          const tableList = originalGetById.apply(document, [
            elementId
          ]);
          return new Proxy(tableList, {
            get(obj, p) {
              if (p === "offsetHeight")
                return obj.offsetHeight - 950;
              return obj[p];
            }
          });
        }
        return originalGetById.apply(document, [elementId]);
      };
    }
    function registerCopyThread() {
      const copyLabel = document.createElement("div");
      copyLabel.id = "copy-indicator";
      copyLabel.innerText = "复制树洞";
      const observer = new MutationObserver(async (mutations, _) => {
        var _a, _b;
        const drawer = (_b = (_a = mutations.find(
          (item) => {
            var _a2;
            return ((_a2 = item.addedNodes) == null ? void 0 : _a2[0]) && item.addedNodes[0].isSameNode(app.childNodes[2]);
          }
        )) == null ? void 0 : _a.addedNodes) == null ? void 0 : _b[0];
        if (!drawer)
          return;
        const element = await S(".box-id", drawer);
        element.appendChild(copyLabel);
        element.addEventListener("click", () => {
        });
      });
      observer.observe(app, {
        childList: true
      });
    }
    function undoDelete() {
      const originalSend = XMLHttpRequest.prototype.send;
      Object.defineProperty(XMLHttpRequest.prototype, "_response", {
        writable: true
      });
      XMLHttpRequest.prototype.send = function(body) {
        this.onreadystatechange = () => {
          const url = new URL(this.responseURL, window.location.href);
          if (!/\/api\/pku_hole/.test(url.pathname))
            return;
          if (this.readyState === 4 && this.status === 200) {
            Object.defineProperty(this, "responseText", {
              get: function() {
                let modifiedData;
                try {
                  const originalData = JSON.parse(this._response);
                  modifiedData = log.replace(
                    originalData,
                    url.searchParams.get("pid")
                  );
                } catch (e) {
                  console.error("解析JSON数据出错:", e);
                  modifiedData = this._response;
                }
                return JSON.stringify(modifiedData);
              }
            });
          }
        };
        const originalOnload = this.onload;
        this.onload = (e) => {
          if (this.response) {
            this._response = this.response;
          }
          if (originalOnload) {
            originalOnload.call(this, e);
          }
        };
        originalSend.call(this, body);
      };
    }
    undoDelete();
    const app = await S("#eagleMapContainer");
    (await S(".icon-refresh")).click();
    registerCopyThread();
    registerInfiniteScrollInjection();
  })();

})();