Greasy Fork

Greasy Fork is available in English.

NGA Filter

troll must die

当前为 2021-04-09 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        NGA Filter
// @namespace   http://greasyfork.icu/users/263018
// @version     1.2.1
// @author      snyssss
// @description troll must die

// @match       *://bbs.nga.cn/thread.php?fid=*
// @match       *://bbs.nga.cn/read.php?tid=*
// @match       *://bbs.nga.cn/nuke.php?*
// @match       *://ngabbs.com/thread.php?fid=*
// @match       *://ngabbs.com/read.php?tid=*
// @match       *://ngabbs.com/nuke.php?*

// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue

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

((n, self) => {
  if (n === undefined) return;

  const key = "NGAFilter";

  // 过滤方式
  const FILTER_MODE = ["继承", "标记", "遮罩", "隐藏", "显示"];

  // 切换过滤方式
  const switchFilterMode = (value) => {
    const next = FILTER_MODE.indexOf(value) + 1;

    if (next >= FILTER_MODE.length) {
      return FILTER_MODE[0];
    }

    return FILTER_MODE[next];
  };

  // 数据
  const data = (() => {
    const d = {
      tags: {},
      users: {},
      options: {
        filterMode: "隐藏",
        keyword: "",
      },
    };

    const v = GM_getValue(key);

    if (typeof v !== "object") {
      return d;
    }

    return Object.assign(d, v);
  })();

  // 保存数据
  const saveData = () => {
    GM_setValue(key, data);
  };

  // 增加标记
  const addTag = (name) => {
    const tag = Object.values(data.tags).find((item) => item.name === name);

    if (tag) return tag.id;

    const id =
      Math.max(...Object.values(data.tags).map((item) => item.id), 0) + 1;

    const hash = (() => {
      let h = 5381;
      for (var i = 0; i < name.length; i++) {
        h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
      }
      return h;
    })();

    const hex = Math.abs(hash).toString(16) + "000000";

    const hsv = [
      `0x${hex.substr(2, 2)}` / 255,
      `0x${hex.substr(2, 2)}` / 255 / 2 + 0.25,
      `0x${hex.substr(4, 2)}` / 255 / 2 + 0.25,
    ];

    const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);

    const color = ["#", ...rgb].reduce((a, b) => {
      return a + ("0" + b.toString(16)).slice(-2);
    });

    data.tags[id] = {
      id,
      name,
      color,
      filterMode: FILTER_MODE[0],
    };

    saveData();

    return id;
  };

  // 增加用户
  const addUser = (id, name = null, tags = [], filterMode = FILTER_MODE[0]) => {
    if (data.users[id]) return data.users[id];

    data.users[id] = {
      id,
      name,
      tags,
      filterMode,
    };

    saveData();

    return data.users[id];
  };

  // 旧版本数据迁移
  {
    const dataKey = "troll_data";
    const modeKey = "troll_mode";
    const keywordKey = "troll_keyword";

    if (localStorage.getItem(dataKey)) {
      let trollMap = (function () {
        try {
          return JSON.parse(localStorage.getItem(dataKey)) || {};
        } catch (e) {}

        return {};
      })();

      let filterMode = ~~localStorage.getItem(modeKey);

      let filterKeyword = localStorage.getItem(keywordKey) || "";

      // 整理标签
      [...new Set(Object.values(trollMap).flat())].forEach((item) =>
        addTag(item)
      );

      // 整理用户
      Object.keys(trollMap).forEach((item) => {
        addUser(
          item,
          null,
          (typeof trollMap[item] === "object"
            ? trollMap[item]
            : []
          ).map((tag) => addTag(tag))
        );
      });

      data.options.filterMode = filterMode ? "隐藏" : "标记";
      data.options.keyword = filterKeyword;

      localStorage.removeItem(dataKey);
      localStorage.removeItem(modeKey);
      localStorage.removeItem(keywordKey);

      saveData();
    }

    // v1.1.0 -> v1.1.1
    {
      Object.values(data.users).forEach(({ id, name, tags, enabled }) => {
        if (enabled !== undefined) {
          data.users[id] = {
            id,
            name,
            tags,
            filterMode: enabled ? "继承" : "显示",
          };
        }
      });

      Object.values(data.tags).forEach(({ id, name, color, enabled }) => {
        if (enabled !== undefined) {
          data.tags[id] = {
            id,
            name,
            color,
            filterMode: enabled ? "继承" : "显示",
          };
        }
      });

      if (data.options.filterMode === 0) {
        data.options.filterMode = "隐藏";
      } else if (data.options.filterMode === 1) {
        data.options.filterMode = "标记";
      }

      saveData();
    }
  }

  // 编辑用户标记
  const editUser = (() => {
    let window;
    return (uid, name, callback) => {
      if (window === undefined) {
        window = n.createCommmonWindow();
      }

      const user = data.users[uid];

      const content = document.createElement("div");

      const size = Math.floor((screen.width * 0.8) / 200);

      const items = Object.values(data.tags).map(
        (tag, index) => `
          <td class="c1">
            <label for="s-tag-${index}" style="display: block; cursor: pointer;">
              <b class="block_txt nobr" style="background:${
                tag.color
              }; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>
            </label>
          </td>
          <td class="c2" width="1">
              <input id="s-tag-${index}" type="checkbox" value="${tag.id}" ${
          user && user.tags.find((item) => item === tag.id) && "checked"
        }/>
          </td>
        `
      );

      const rows = [...new Array(Math.ceil(items.length / size))].map(
        (item, index) =>
          `
          <tr class="row${(index % 2) + 1}">
            ${items.slice(size * index, size * (index + 1)).join("")}
          </tr>
          `
      );

      content.className = "w100";
      content.innerHTML = `
        <div class="filter-table-wrapper" style="width: 80vw;">
          <table class="filter-table forumbox">
            <tbody>
              ${rows.join("")}
            </tbody>
          </table>
        </div>
        <div style="margin: 10px 0;">
            <input placeholder="一次性添加多个标记用&quot;|&quot;隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
        </div>
        <div style="margin: 10px 0;">
            <span>过滤方式:</span>
            <button>${(user && user.filterMode) || FILTER_MODE[0]}</button>
            <div class="right_">
                <button>删除</button>
                <button>保存</button>
            </div>
        </div>
        <div class="silver" style="margin-top: 5px;">过滤优先级:用户 > 标记 > 全局</div>
    `;

      const actions = content.getElementsByTagName("button");

      actions[0].onclick = () => {
        actions[0].innerText = switchFilterMode(
          actions[0].innerText || FILTER_MODE[0]
        );
      };

      actions[1].onclick = () => {
        if (confirm("是否确认?")) {
          delete data.users[uid];

          saveData();

          callback && callback();

          window._.hide();
        }
      };

      actions[2].onclick = () => {
        if (confirm("是否确认?")) {
          const values = [...content.getElementsByTagName("input")];
          const newTags = values[values.length - 1].value
            .split("|")
            .filter((item) => item.length)
            .map((item) => addTag(item));
          const tags = [
            ...new Set(
              values
                .filter((item) => item.type === "checkbox" && item.checked)
                .map((item) => ~~item.value)
                .concat(newTags)
            ),
          ].sort();

          if (user) {
            user.tags = tags;
            user.filterMode = actions[0].innerText;
          } else {
            addUser(uid, name, tags, actions[0].innerText);
          }

          saveData();

          callback && callback();

          window._.hide();
        }
      };

      if (user === undefined) {
        actions[1].style = "display: none;";
      }

      window._.addContent(null);
      window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
      window._.addContent(content);
      window._.show();
    };
  })();

  // 过滤
  const reFilter = () => {
    const tPage = location.pathname === "/thread.php";
    const pPage = location.pathname === "/read.php";
    const uPage = location.pathname === "/nuke.php";

    if (tPage) {
      const tData = n.topicArg.data;

      Object.values(tData).forEach((item) => {
        if (item.containerC) return;

        const uid =
          item[2].search.match(/uid=(\S+)/) &&
          item[2].search.match(/uid=(\S+)/)[1];

        const user = data.users[uid];

        const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];

        // 过滤方式,优先程度 用户 > 标记 > 全局
        const filterMode = (() => {
          if (user) {
            if (user.filterMode !== FILTER_MODE[0]) {
              return user.filterMode;
            }

            const tagsFilterMode = tags
              .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
              .sort((a, b) => b - a)[0];

            if (tagsFilterMode > 0) {
              return FILTER_MODE[tagsFilterMode];
            }

            return data.options.filterMode;
          }

          if (
            data.options.keyword.length &&
            item[1].innerText.search(data.options.keyword) >= 0
          ) {
            return data.options.filterMode;
          }
        })();

        item.contentC = item[1];

        item.contentB = item.contentB || item.contentC.innerHTML;

        item.containerC =
          item.containerC || item.contentC.parentNode.parentNode;

        item.containerC.style = "";
        item.contentC.style = "";
        item[1].className = item[1].className.replace(" filter-mask", "");
        item[2].className = item[2].className.replace(" filter-mask", "");

        if (filterMode === "标记") {
          item.contentC.style = "text-decoration: line-through;";
        } else if (filterMode === "遮罩") {
          item[1].className += " filter-mask";
          item[2].className += " filter-mask";
        } else if (filterMode === "隐藏") {
          item.containerC.style = "display: none;";
        }
      });
    } else if (pPage) {
      const pData = n.postArg.data;

      Object.values(pData).forEach((item) => {
        if (~~item.pAid === self) return;
        if (item.containerC) return;

        if (typeof item.i === "number") {
          item.actionC =
            item.actionC ||
            (() => {
              const ele = item.uInfoC.querySelector('[name="uid"]');

              ele.onclick = null;

              return ele;
            })();

          item.tagC =
            item.tagC ||
            (() => {
              const tc = document.createElement("div");

              tc.className = "filter-tags";

              item.uInfoC.appendChild(tc);

              return tc;
            })();
        }

        item.pName =
          item.pName ||
          item.uInfoC.getElementsByClassName("author")[0].innerText;

        item.reFilter =
          item.reFilter ||
          (() => {
            const user = data.users[item.pAid];

            const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];

            // 过滤方式,优先程度 用户 > 标记 > 全局
            const filterMode = (() => {
              if (user) {
                if (user.filterMode !== FILTER_MODE[0]) {
                  return user.filterMode;
                }

                const tagsFilterMode = tags
                  .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
                  .sort((a, b) => b - a)[0];

                if (tagsFilterMode > 0) {
                  return FILTER_MODE[tagsFilterMode];
                }

                return data.options.filterMode;
              }
            })();

            item.avatarC =
              item.avatarC ||
              (() => {
                const tc = document.createElement("div");

                const avatar = document.getElementById(`posteravatar${item.i}`);

                if (avatar) {
                  avatar.parentNode.insertBefore(tc, avatar.nextSibling);

                  tc.appendChild(avatar);
                }

                return tc;
              })();

            item.contentB = item.contentB || item.contentC.innerHTML;

            item.containerC =
              item.containerC ||
              (() => {
                let temp = item.contentC;

                while (temp.nodeName !== "TBODY") {
                  temp = temp.parentNode;
                }

                return temp;
              })();

            item.avatarC.style.display = "";
            item.containerC.style.display = "";
            item.contentC.innerHTML = item.contentB;

            if (item.containerC.previousElementSibling) {
              item.containerC.parentNode.removeChild(
                item.containerC.previousElementSibling
              );
            }

            if (item.actionC) {
              item.actionC.style = "background: #aaa;";
            }

            if (filterMode === "标记") {
              item.avatarC.style.display = "none";
              item.contentC.innerHTML = `
                <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7;">
                    <span class="crimson">Troll must die.</span>
                    <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${user.id}')].forEach(item => item.style.display = '')">点击查看</a>
                    <div style="display: none;" name="troll_${user.id}">
                        ${item.contentB}
                    </div>
                </div>`;

              if (item.actionC) {
                item.actionC.style = "background: #cb4042;";
              }
            } else if (filterMode === "遮罩") {
              const caption = document.createElement("CAPTION");

              caption.className = "filter-mask";
              caption.style =
                "text-align: center; border: 1px solid; margin: 1px;";
              caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
              caption.onclick = () => {
                item.containerC.parentNode.removeChild(caption);
                item.containerC.style.display = "";
              };

              item.containerC.parentNode.insertBefore(caption, item.containerC);
              item.containerC.style.display = "none";
              
              if (item.actionC) {
                item.actionC.style = "background: #cb4042;";
              }
            } else if (filterMode === "隐藏") {
              item.containerC.style.display = "none";
            }

            if (item.tagC) {
              item.tagC.style.display = tags.length ? "" : "none";
              item.tagC.innerHTML = tags
                .map(
                  (tag) =>
                    `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
                )
                .join("");
            }
          });

        if (item.actionC) {
          item.actionC.onclick =
            item.actionC.onclick ||
            ((e) => {
              if (item.pAid < 0) return;

              const user = data.users[item.pAid];

              if (e.ctrlKey === false) {
                editUser(item.pAid, item.pName, item.reFilter);
              } else {
                if (user) {
                  delete data.users[user.id];
                } else {
                  addUser(item.pAid, item.pName);
                }

                saveData();
                item.reFilter();
              }
            });
        }

        item.reFilter();
      });
    } else if (uPage) {
      const container = document.getElementById("ucp_block");

      if (container.firstChild) {
        const uid = container.innerText.match(/用户ID\s*:\s*(\S+)/)[1];

        const name = container.innerText.match(/用户名\s*:\s*(\S+)/)[1];

        container.tagC =
          container.tagC ||
          (() => {
            const c = document.createElement("span");

            c.innerHTML = `
                    <h2 class="catetitle">:: ${name} 的标记 ::</h2>
                    <div class="cateblock" style="text-align: left; line-height: 1.8em;">
                        <div class="contentBlock" style="padding: 5px 10px;">
                            <span>
                                <ul class="actions" style="padding: 0px; margin: 0px;">
                                    <li style="padding-right: 5px;">
                                        <span>
                                            <a href="javascript: void(0);">[编辑 ${name} 的标记]</a>
                                        </span>
                                    </li>
                                    <div class="clear"></div>
                                </ul>
                            </span>
                            <div class="filter-tags"></div>
                            <div class="clear"></div>
                        </div>
                    </div>
                `;

            c.getElementsByTagName("a")[0].onclick = () => {
              editUser(uid, name, container.refresh);
            };

            container.firstChild.insertBefore(
              c,
              container.firstChild.childNodes[1]
            );

            return c.getElementsByClassName("filter-tags")[0];
          })();

        container.refresh = () => {
          container.tagC.innerHTML = data.users[uid].tags
            .map(
              (tag) =>
                `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`
            )
            .join("");
        };

        container.refresh();
      }
    }
  };

  // STYLE
  GM_addStyle(`
    .filter-table-wrapper {
        max-height: 80vh;
        overflow-y: auto;
    }
    .filter-table {
        margin: 0;
    }
    .filter-table th,
    .filter-table td {
        position: relative;
        white-space: nowrap;
    }
    .filter-table th {
        position: sticky;
        top: 2px;
        z-index: 1;
    }
    .filter-table input:not([type]), .filter-table input[type="text"] {
        margin: 0;
        box-sizing: border-box;
        height: 100%;
        width: 100%;
    }
    .filter-input-wrapper {
        position: absolute;
        top: 6px;
        right: 6px;
        bottom: 6px;
        left: 6px;
    }
    .filter-text-ellipsis {
        display: flex;
    }
    .filter-text-ellipsis > * {
        flex: 1;
        width: 1px;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .filter-button-group {
        margin: -.1em -.2em;
    }
    .filter-tags {
        margin: 2px -0.2em 0;
        text-align: left;
    }
    .filter-mask {
        color: #81C7D4;
        background: #81C7D4;
        border-color: #66BAB7;
    }
  `);

  // UI
  const u = (() => {
    const modules = {};

    const tabContainer = (() => {
      const c = document.createElement("div");

      c.className = "w100";
      c.innerHTML = `
          <div class="right_" style="margin-bottom: 5px;">
              <table class="stdbtn" cellspacing="0">
                  <tbody>
                      <tr></tr>
                  </tbody>
              </table>
          </div>
          <div class="clear"></div>
          `;

      return c;
    })();

    const tabPanelContainer = (() => {
      const c = document.createElement("div");

      c.style = "width: 80vw;";

      return c;
    })();

    const content = (() => {
      const c = document.createElement("div");

      c.append(tabContainer);
      c.append(tabPanelContainer);

      return c;
    })();

    const addModule = (() => {
      const tc = tabContainer.getElementsByTagName("tr")[0];
      const cc = tabPanelContainer;

      return (module) => {
        const tabBox = document.createElement("td");

        tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;

        const tab = tabBox.childNodes[0];

        const toggle = () => {
          Object.values(modules).forEach((item) => {
            if (item.tab === tab) {
              item.tab.className = "nobr";
              item.content.style = "display: block";
              item.refresh();
            } else {
              item.tab.className = "nobr silver";
              item.content.style = "display: none";
            }
          });
        };

        tc.append(tabBox);
        cc.append(module.content);

        tab.onclick = toggle;

        modules[module.name] = {
          ...module,
          tab,
          toggle,
        };

        return modules[module.name];
      };
    })();

    return {
      content,
      modules,
      addModule,
    };
  })();

  // 屏蔽列表
  const blockModule = (() => {
    const content = (() => {
      const c = document.createElement("div");

      c.style = "display: none";
      c.innerHTML = `
        <div class="filter-table-wrapper">
          <table class="filter-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1" width="1">昵称</th>
                <th class="c2">标记</th>
                <th class="c3" width="1">过滤方式</th>
                <th class="c4" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
      `;

      return c;
    })();

    const refresh = (() => {
      const container = content.getElementsByTagName("tbody")[0];

      const func = () => {
        container.innerHTML = "";

        Object.values(data.users).forEach((item) => {
          const tc = document.createElement("tr");

          tc.className = `row${
            (container.querySelectorAll("TR").length % 2) + 1
          }`;

          tc.refresh = () => {
            if (data.users[item.id]) {
              tc.innerHTML = `
                <td class="c1">
                    <a href="/nuke.php?func=ucp&uid=${
                      item.id
                    }" class="b nobr">[${
                item.name ? "@" + item.name : "#" + item.id
              }]</a>
                </td>
                <td class="c2">
                    ${item.tags
                      .map((tag) => {
                        if (data.tags[tag]) {
                          return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
                        }
                      })
                      .join("")}
                </td>
                <td class="c3">
                    <div class="filter-table-button-group">
                      <button>${item.filterMode || FILTER_MODE[0]}</button>
                    </div>
                </td>
                <td class="c4">
                    <div class="filter-table-button-group">
                      <button>编辑</button>
                      <button>删除</button>
                    </div>
                </td>
              `;

              const actions = tc.getElementsByTagName("button");

              actions[0].onclick = () => {
                data.users[item.id].filterMode = switchFilterMode(
                  data.users[item.id].filterMode || FILTER_MODE[0]
                );

                actions[0].innerHTML = data.users[item.id].filterMode;

                saveData();
                reFilter();
              };

              actions[1].onclick = () => {
                editUser(item.id, item.name, tc.refresh);
              };

              actions[2].onclick = () => {
                if (confirm("是否确认?")) {
                  delete data.users[item.id];
                  container.removeChild(tc);

                  saveData();
                  reFilter();
                }
              };
            } else {
              tc.remove();
            }
          };

          tc.refresh();

          container.appendChild(tc);
        });
      };

      return func;
    })();

    return {
      name: "屏蔽列表",
      content,
      refresh,
    };
  })();

  // 标记设置
  const tagModule = (() => {
    const content = (() => {
      const c = document.createElement("div");

      c.style = "display: none";
      c.innerHTML = `
        <div class="filter-table-wrapper">
          <table class="filter-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1" width="1">标记</th>
                <th class="c2">列表</th>
                <th class="c3" width="1">过滤方式</th>
                <th class="c4" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
      `;

      return c;
    })();

    const refresh = (() => {
      const container = content.getElementsByTagName("tbody")[0];

      const func = () => {
        container.innerHTML = "";

        Object.values(data.tags).forEach((item) => {
          const tc = document.createElement("tr");

          tc.className = `row${
            (container.querySelectorAll("TR").length % 2) + 1
          }`;

          tc.innerHTML = `
            <td class="c1">
                <b class="block_txt nobr" style="background:${
                  item.color
                }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
            </td>
            <td class="c2">
                <button>${
                  Object.values(data.users).filter((user) =>
                    user.tags.find((tag) => tag === item.id)
                  ).length
                }
                </button>
                <div style="white-space: normal; display: none;">
                    ${Object.values(data.users)
                      .filter((user) =>
                        user.tags.find((tag) => tag === item.id)
                      )
                      .map(
                        (user) =>
                          `<a href="/nuke.php?func=ucp&uid=${
                            user.id
                          }" class="b nobr">[${
                            user.name ? "@" + user.name : "#" + user.id
                          }]</a>`
                      )
                      .join("")}
                </div>
            </td>
            <td class="c3">
                <div class="filter-table-button-group">
                  <button>${item.filterMode || FILTER_MODE[0]}</button>
                </div>
            </td>
            <td class="c3">
                <div class="filter-table-button-group">
                  <button>删除</button>
                </div>
            </td>
          `;

          const actions = tc.getElementsByTagName("button");

          actions[0].onclick = (() => {
            let hide = true;
            return () => {
              hide = !hide;
              actions[0].nextElementSibling.style.display = hide
                ? "none"
                : "block";
            };
          })();

          actions[1].onclick = () => {
            data.tags[item.id].filterMode = switchFilterMode(
              data.tags[item.id].filterMode || FILTER_MODE[0]
            );

            actions[1].innerHTML = data.tags[item.id].filterMode;

            saveData();
            reFilter();
          };

          actions[2].onclick = () => {
            if (confirm("是否确认?")) {
              delete data.tags[item.id];

              Object.values(data.users).forEach((user) => {
                const index = user.tags.findIndex((tag) => tag === item.id);
                if (index >= 0) {
                  user.tags.splice(index, 1);
                }
              });

              container.removeChild(tc);

              saveData();
              reFilter();
            }
          };

          container.appendChild(tc);
        });
      };

      return func;
    })();

    return {
      name: "标记设置",
      content,
      refresh,
    };
  })();

  // 通用设置
  const commonModule = (() => {
    const content = (() => {
      const c = document.createElement("div");

      c.style = "display: none";

      return c;
    })();

    const refresh = (() => {
      const container = content;

      const func = () => {
        container.innerHTML = "";

        // 屏蔽关键词
        {
          const tc = document.createElement("div");

          tc.innerHTML += `
            <div>过滤关键词,用"|"隔开</div>
            <div>
                <textarea rows="3" style="box-sizing: border-box; width: 100%;">${data.options.keyword}</textarea>
                <button>确认</button>
            </div>
          `;

          const actions = tc.getElementsByTagName("button");

          actions[0].onclick = () => {
            const v = actions[0].previousElementSibling.value;

            data.options.keyword = v;

            saveData();
            reFilter();
          };

          container.appendChild(tc);
        }

        // 全局过滤方式
        {
          const tc = document.createElement("div");

          tc.innerHTML += `
            <br/>
            <div>全局过滤方式</div>
            <div></div>
            <div class="silver" style="margin-top: 5px;">过滤优先级:用户 &gt; 标记 &gt; 全局</div>
          `;

          ["标记", "遮罩", "隐藏"].forEach((item, index) => {
            const ele = document.createElement("SPAN");

            ele.innerHTML += `
            <input id="s-fm-${index}" type="radio" name="filterType" ${
              data.options.filterMode === item && "checked"
            }>
            <label for="s-fm-${index}" style="cursor: pointer;">${item}</label>
            `;

            const inp = ele.querySelector("input");

            inp.onchange = () => {
              if (inp.checked) {
                data.options.filterMode = item;
                saveData();
                reFilter();
              }
            };

            tc.querySelectorAll("div")[1].append(ele);
          });

          container.appendChild(tc);
        }

        // 删除没有标记的用户
        {
          const tc = document.createElement("div");

          tc.innerHTML += `
            <br/>
            <div>
                <button>删除没有标记的用户</button>
            </div>
          `;

          const actions = tc.getElementsByTagName("button");

          actions[0].onclick = () => {
            if (confirm("是否确认?")) {
              Object.values(data.users).forEach((item) => {
                if (item.tags.length === 0) {
                  delete data.users[item.id];
                }
              });

              saveData();
              reFilter();
            }
          };

          container.appendChild(tc);
        }

        // 删除没有用户的标记
        {
          const tc = document.createElement("div");

          tc.innerHTML += `
            <br/>
            <div>
                <button>删除没有用户的标记</button>
            </div>
          `;

          const actions = tc.getElementsByTagName("button");

          actions[0].onclick = () => {
            if (confirm("是否确认?")) {
              Object.values(data.tags).forEach((item) => {
                if (
                  Object.values(data.users).filter((user) =>
                    user.tags.find((tag) => tag === item.id)
                  ).length === 0
                ) {
                  delete data.tags[item.id];
                }
              });

              saveData();
              reFilter();
            }
          };

          container.appendChild(tc);
        }
      };

      return func;
    })();

    return {
      name: "通用设置",
      content,
      refresh,
    };
  })();

  u.addModule(blockModule).toggle();
  u.addModule(tagModule);
  u.addModule(commonModule);

  // 增加菜单项
  (() => {
    const title = "屏蔽/标记";
    let window;

    n.mainMenu.addItemOnTheFly(title, null, () => {
      if (window === undefined) {
        window = n.createCommmonWindow();
      }

      window._.addContent(null);
      window._.addTitle(title);
      window._.addContent(u.content);
      window._.show();
    });
  })();

  // 执行过滤
  (() => {
    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]);
    };

    const initialized = {
      topicArg: false,
      postArg: false,
    };

    hookFunction(n, "eval", () => {
      if (Object.values(initialized).findIndex((item) => item === false) < 0) {
        return;
      }

      if (n.topicArg && initialized.topicArg === false) {
        hookFunction(
          n.topicArg,
          "add",
          (returnValue, originalFunction, arguments) => reFilter()
        );

        initialized.topicArg = true;
      }

      if (n.postArg && initialized.postArg === false) {
        hookFunction(
          n.postArg,
          "proc",
          (returnValue, originalFunction, arguments) => reFilter()
        );

        initialized.postArg = true;
      }
    });

    if (n.ucp) {
      hookFunction(n.ucp, "_echo", (returnValue, originalFunction, arguments) =>
        reFilter()
      );
    }

    reFilter();
  })();
})(commonui, __CURRENT_UID);