NGA Fast View Post Fix

修复论坛“快速浏览这个帖子”功能

目前为 2023-12-12 提交的版本。查看 最新版本

// ==UserScript==
// @name        NGA Fast View Post Fix
// @namespace   https://gf.qytechs.cn/users/263018
// @version     1.0.0
// @author      snyssss
// @description 修复论坛“快速浏览这个帖子”功能
// @license     MIT

// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*

// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_registerMenuCommand

// @noframes
// ==/UserScript==

((ui, ubbcode) => {
  if (!ui) return;
  if (!ubbcode) return;

  // KEY
  const USER_AGENT_KEY = "USER_AGENT";
  const IS_SHOW_SCORE_KEY = "IS_SHOW_SCORE";

  // User Agent
  const USER_AGENT = (() => {
    const data = GM_getValue(USER_AGENT_KEY) || "Nga_Official";

    GM_registerMenuCommand(`修改UA:${data}`, () => {
      const value = prompt("修改UA", data);

      if (value) {
        GM_setValue(USER_AGENT_KEY, value);

        location.reload();
      }
    });

    return data;
  })();

  // 是否显示评分
  const showScore = (() => {
    const data = GM_getValue(IS_SHOW_SCORE_KEY) || false;

    GM_registerMenuCommand(`显示评分:${data ? "是" : "否"}`, () => {
      GM_setValue(IS_SHOW_SCORE_KEY, !data);

      location.reload();
    });

    return data;
  })();

  // 简单的统一请求
  const request = (url, config = {}) =>
    fetch(url, {
      headers: {
        "X-User-Agent": USER_AGENT,
      },
      ...config,
    });

  // 快速浏览
  const fastViewPost = (() => {
    const window = ui.createCommmonWindow();

    const container = document.createElement("DIV");

    container.className = `fastViewPost`;
    container.innerHTML = `
      <div class="forumbox">
        <div class="postrow"></div>
      </div>
    `;

    const list = container.querySelector(".postrow");

    window._.addContent(null);
    window._.addContent(container);

    return async (_, tid, pid = 0, opt) => {
      // 帖子地址,移除了 opt 参数
      // 泥潭会在快速浏览窗口里再次点击快速浏览时,把整个相关对话列出来,反而找不到当前帖子
      const url = `/read.php?tid=${tid}&pid=${pid}`;

      // 如果 opt 是 16,则说明是快速浏览窗口中的快速浏览,追加楼层
      // 反之清空楼层
      if (opt !== 16) {
        list.innerHTML = "";
      }

      // 如果已加载过,直接返回
      if (list && list.querySelector(`[data-pid="${pid}"]`)) {
        return;
      }

      // 请求内容
      const data = await new Promise((resolve) =>
        request(url)
          .then((res) => res.blob())
          .then((res) => {
            const getLastIndex = (
              content,
              position,
              start = "{",
              end = "}"
            ) => {
              if (position >= 0) {
                let nextIndex = position + 1;

                while (nextIndex < content.length) {
                  if (content[nextIndex] === end) {
                    return nextIndex;
                  }

                  if (content[nextIndex] === start) {
                    nextIndex = getLastIndex(content, nextIndex, start, end);

                    if (nextIndex < 0) {
                      break;
                    }
                  }

                  nextIndex = nextIndex + 1;
                }
              }

              return -1;
            };

            // 读取内容
            const reader = new FileReader();

            reader.onload = () => {
              const parser = new DOMParser();

              const doc = parser.parseFromString(reader.result, "text/html");

              const html = doc.body.innerHTML;

              // 验证帖子正常
              const verify = doc.querySelector("#m_posts");

              if (verify === null) {
                throw new Error();
              }

              // 取得顶楼 UID
              const uid = (() => {
                const ele = doc.querySelector("#postauthor0");

                if (ele) {
                  const res = ele.getAttribute("href").match(/uid=((-?)(\S+))/);

                  if (res) {
                    return res[1];
                  }
                }

                return 0;
              })();

              // 取得顶楼标题
              const subject = doc.querySelector("#postsubject0").innerHTML;

              // 取得顶楼内容
              const content = doc.querySelector("#postcontent0").innerHTML;

              // 取得用户信息
              const user = (() => {
                // 起始JSON
                const str = `"${uid}":{"uid":`;

                // 起始下标
                const index = html.indexOf(str) + str.length;

                // 结尾下标
                const lastIndex = getLastIndex(html, index);

                if (lastIndex >= 0) {
                  try {
                    return JSON.parse(
                      `{"uid":${html.substring(index, lastIndex)}}`
                    );
                  } catch {}
                }

                return null;
              })();

              // 取得额外信息
              const extra = (() => {
                // 起始JSON
                const str = `commonui.postArg.proc(`;

                // 起始下标
                const index = html.indexOf(str) + str.length;

                // 结尾下标
                const lastIndex = getLastIndex(html, index, "(", ")");

                if (lastIndex >= 0) {
                  return html.substring(index, lastIndex);
                }

                return null;
              })();

              // 返回结果
              resolve({
                subject,
                content,
                user,
                extra,
              });
            };

            reader.readAsText(res, "GBK");
          })
          .catch(() => {
            resolve(null);
          })
      );

      // 没有内容
      if (data === null) {
        return;
      }

      // 解析数据
      const { content, user, extra } = data;

      // 发帖人姓名
      const username = ui.htmlName(user.username);

      // 加载楼层
      const row = document.createElement("DIV");

      row.className = `row${
        2 - (list.querySelectorAll(":scope > div").length % 2)
      }`;
      row.innerHTML = `
        <div class="c2" data-pid="${pid}">
          <div class="posterInfoLine b">${username}</div>
          <span class="postcontent ubbcode">${content}</span>
        </div>
      `;

      list.insertBefore(row, list.firstChild);

      // 格式转换完毕后显示窗口
      // 但泥潭这样做实际毫无意义,如果窗口开着的情况下进行格式转换,照样会短暂显示乱码
      ubbcode.bbsCode({
        c: row.querySelector(".ubbcode"),
        tId: tid,
        pId: pid,
        opt: 8,
        authorId: user.uid,
        noImg: 1,
        isNukePost: 0,
        callBack: () => {
          window._.show();
        },
      });

      // 显示评分
      if (showScore) {
        // 取得评分数据
        const recommend = extra.match(/'(\d+),(\d+),(\d+)'/);

        if (recommend) {
          const score_1 = recommend[2];
          const score_2 = recommend[3];

          const ele = document.createElement("DIV");

          ele.className = `right_`;
          ele.innerHTML = `
            <div style="display: inline-block; font-weight: normal;">
              <span class="small_colored_text_btn stxt block_txt_c2 vertmod">
                <span class="white">
                  <a class="white" href="javascript: void(0);" title="支持" style="text-decoration: none;">
                    <span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em; padding: 0 0.1em;">⯅</span>
                  </a>
                  <span class="recommendvalue"></span>
                  <a class="white" href="javascript: void(0);" title="反对" style="text-decoration: none;">
                    <span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em; padding: 0 0.1em;">⯆</span>
                  </a>
                </span>
              </span>
            </div>
          `;

          ele.querySelector(".recommendvalue").innerHTML =
            score_1 - score_2 || "&nbsp;";

          row.querySelector(".posterInfoLine").appendChild(ele);

          // 绑定事件
          (() => {
            const bindEvent = () => {
              const like = ele.querySelector("a:first-child");

              like.onclick = () => {
                ui.postScoreAdd(like, {
                  tid,
                  pid,
                });
              };

              const unlike = ele.querySelector("a:last-child");

              unlike.onclick = () => {
                ui.postScoreAdd(
                  unlike,
                  {
                    tid,
                    pid,
                  },
                  1
                );
              };
            };

            if (ui.postScoreAdd) {
              bindEvent();
            } else {
              __SCRIPTS.asyncLoad("read", () => {
                // 强行触发一次 eval,以兼容 NGA Likes Support 脚本的更新点赞数
                ui.eval({});

                // 绑定事件
                bindEvent();
              });
            }
          })();
        }
      }
    };
  })();

  // 加载脚本
  (() => {
    ubbcode.fastViewPost = fastViewPost;
  })();
})(commonui, ubbcode);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址