NGA Fast View Post Fix

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

当前为 2023-12-12 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        NGA Fast View Post Fix
// @namespace   https://greasyfork.org/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);