NGA Likes Support

显示被点赞和粉丝数量

Tính đến 21-07-2022. Xem phiên bản mới nhất.

// ==UserScript==
// @name        NGA Likes Support
// @namespace   https://greasyfork.org/users/263018
// @version     1.2.0
// @author      snyssss
// @description 显示被点赞和粉丝数量

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

// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @noframes
// ==/UserScript==

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

  // KEY
  const SHOW_POSTNUM_ENABLE_KEY = "SHOW_POSTNUM_ENABLE";
  const SHOW_IPLOC_ENABLE_KEY = "SHOW_IPLOC_ENABLE";

  // 显示发帖数
  const showPostnumEnable = GM_getValue(SHOW_POSTNUM_ENABLE_KEY) || false;

  // 显示IP属地
  const showIpLocEnable = GM_getValue(SHOW_IPLOC_ENABLE_KEY) || false;

  // 钩子
  const hookFunction = (object, functionName, callback) => {
    ((originalFunction) => {
      object[functionName] = function () {
        const returnValue = originalFunction.apply(this, arguments);

        callback.apply(this, [returnValue, originalFunction, arguments]);

        return returnValue;
      };
    })(object[functionName]);
  };

  class UserInfo {
    execute(task) {
      task().finally(() => {
        if (this.waitingQueue.length) {
          const next = this.waitingQueue.shift();

          this.execute(next);
        } else {
          this.isRunning = false;
        }
      });
    }

    enqueue(task) {
      if (this.isRunning) {
        this.waitingQueue.push(task);
      } else {
        this.isRunning = true;

        this.execute(task);
      }
    }

    rearrange() {
      if (this.data) {
        const list = Object.values(this.children);

        for (let i = 0; i < list.length; i++) {
          if (list[i].source === undefined) {
            list[i].create(this.data);
          }

          Object.entries(this.container).forEach((item) => {
            list[i].clone(this.data, item);
          });
        }
      }
    }

    reload() {
      this.enqueue(async () => {
        this.data = await new Promise((resolve) => {
          fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${this.uid}`)
            .then((res) => res.blob())
            .then((blob) => {
              const reader = new FileReader();

              reader.onload = () => {
                const text = reader.result;
                const result = JSON.parse(
                  text.replace("window.script_muti_get_var_store=", "")
                );

                resolve(result.data[0]);
              };

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

        Object.values(this.children).forEach((item) => item.destroy());

        this.rearrange();
      });
    }

    constructor(id) {
      this.uid = id;

      this.waitingQueue = [];
      this.isRunning = false;

      this.container = {};
      this.children = {};

      this.reload();
    }
  }

  class UserInfoWidget {
    destroy() {
      if (this.source) {
        this.source = undefined;
      }

      if (this.target) {
        Object.values(this.target).forEach((item) => {
          if (item.parentNode) {
            item.parentNode.removeChild(item);
          }
        });
      }
    }

    clone(data, [argid, container]) {
      if (this.source) {
        if (this.target[argid] === undefined) {
          this.target[argid] = this.source.cloneNode(true);

          if (this.callback) {
            this.callback(data, this.target[argid]);
          }
        }

        container.appendChild(this.target[argid]);
      }
    }

    constructor(func, callback) {
      this.create = (data) => {
        this.destroy();

        this.source = func(data);
        this.target = {};
      };

      this.callback = callback;
    }
  }

  ui.sn = ui.sn || {};
  ui.sn.userInfo = ui.sn.userInfo || {};

  ((info) => {
    const execute = (argid) => {
      const args = ui.postArg.data[argid];

      if (args.comment) return;

      const uid = +args.pAid;

      if (uid > 0) {
        if (info[uid] === undefined) {
          info[uid] = new UserInfo(uid);
        }

        if (document.contains(info[uid].container[argid]) === false) {
          if (args.vsmall) {
            info[uid].container[argid] =
              args.pC.querySelector("[name=uid]").parentNode;
          } else {
            info[uid].container[argid] =
              args.uInfoC.querySelector("[name=uid]").parentNode;
          }
        }

        info[uid].enqueue(async () => {
          if (info[uid].children[8] === undefined) {
            info[uid].children[8] = new UserInfoWidget((data) => {
              const value =
                Object.values(data.more_info || {}).find(
                  (item) => item.type === 8
                )?.data || 0;

              const element = document.createElement("SPAN");

              element.className =
                "small_colored_text_btn stxt block_txt_c2 vertmod";
              element.style.cursor = "default";
              element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">⯅</span>&nbsp;${value}</span>`;

              return element;
            });
          }

          if (info[uid].children[16] === undefined) {
            info[uid].children[16] = new UserInfoWidget((data) => {
              const value = data.follow_by_num || 0;

              const element = document.createElement("SPAN");

              element.className =
                "small_colored_text_btn stxt block_txt_c2 vertmod";
              element.style.cursor = "default";
              element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">★</span>&nbsp;${value}</span>`;

              return element;
            });
          }

          info[uid].rearrange();

          if (showPostnumEnable) {
            if (ui._w.__GP.admincheck) {
              return;
            }

            if (args.vsmall) {
              const anchor = [
                ...args.pC.querySelectorAll(".posterInfoLine > span.gray"),
              ].pop();

              const uInfo = ui.userInfo.users[uid];

              if (anchor && uInfo) {
                const element = document.createElement("SPAN");

                element.className = "gray nobr";
                element.innerHTML = `
                    <span>发帖: <span class="${
                      uInfo.postnum > 9999 ? "numeric" : "numericl"
                    } silver">${uInfo.postnum}</span></span>`;

                anchor.parentNode.insertBefore(element, anchor);
              }
            } else {
              const anchor = args.uInfoC.querySelector(
                '.stat div[class="clear"]'
              );

              const uInfo = ui.userInfo.users[uid];

              if (anchor && uInfo) {
                const element = document.createElement("DIV");

                element.style =
                  "float:left;margin-right:3px;min-width:49%;*width:49%";
                element.innerHTML = `
                      <nobr>
                      <span>发帖: <span class="${
                        uInfo.postnum > 9999 ? "numeric" : "numericl"
                      } silver">${uInfo.postnum}</span></span>
                      </nobr>`;

                anchor.parentNode.insertBefore(element, anchor);
              }
            }
          }

          if (showIpLocEnable) {
            if (ui._w.__GP.admincheck) {
              return;
            }

            if (args.vsmall) {
              const anchor = [
                ...args.pC.querySelectorAll(".posterInfoLine > span.gray"),
              ].pop();

              const uInfo = info[uid].data;

              if (anchor && uInfo) {
                const element = document.createElement("SPAN");

                element.className = "gray nobr";
                element.innerHTML = `
                    <span>属地: <span class="silver">${uInfo.ipLoc}</span></span>`;

                anchor.parentNode.insertBefore(element, anchor);
              }
            } else {
              const anchor = args.uInfoC.querySelector(
                '.stat div[class="clear"]'
              );

              const uInfo = info[uid].data;

              if (anchor && uInfo) {
                const element = document.createElement("DIV");

                element.style =
                  "float:left;margin-right:3px;min-width:49%;*width:49%";
                element.innerHTML = `
                    <nobr>
                        <span>属地: <span class="silver">${uInfo.ipLoc}</span></span>
                    </nobr>`;

                anchor.parentNode.insertBefore(element, anchor);
              }
            }
          }
        });
      }
    };

    if (ui.postArg) {
      Object.keys(ui.postArg.data).forEach((i) => execute(i));
    }

    let initialized = false;

    hookFunction(ui, "eval", () => {
      if (initialized) return;

      if (ui.postDisp) {
        hookFunction(
          ui,
          "postDisp",
          (returnValue, originalFunction, arguments) => execute(arguments[0])
        );

        initialized = true;
      }
    });
  })(ui.sn.userInfo);

  // 菜单项
  (() => {
    // 显示发帖数
    if (showPostnumEnable) {
      GM_registerMenuCommand("显示发帖数:启用", () => {
        GM_setValue(SHOW_POSTNUM_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("显示发帖数:禁用", () => {
        GM_setValue(SHOW_POSTNUM_ENABLE_KEY, true);
        location.reload();
      });
    }

    // 显示IP属地
    if (showIpLocEnable) {
      GM_registerMenuCommand("显示属地:启用", () => {
        GM_setValue(SHOW_IPLOC_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("显示属地:禁用", () => {
        GM_setValue(SHOW_IPLOC_ENABLE_KEY, true);
        location.reload();
      });
    }
  })();
})(commonui);