NGA Filter

troll must die

当前为 2021-11-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name NGA Filter
  3. // @namespace https://greasyfork.org/users/263018
  4. // @version 1.4.2
  5. // @author snyssss
  6. // @description troll must die
  7.  
  8. // @match *://bbs.nga.cn/*
  9. // @match *://ngabbs.com/*
  10. // @match *://nga.178.com/*
  11.  
  12. // @grant GM_addStyle
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15.  
  16. // @noframes
  17. // ==/UserScript==
  18.  
  19. ((n, self) => {
  20. if (n === undefined) return;
  21.  
  22. const key = "NGAFilter";
  23.  
  24. // 过滤提示
  25. const FILTER_TIPS =
  26. "过滤顺序:用户 &gt; 标记 &gt; 关键字<br/>过滤级别:隐藏 &gt; 遮罩 &gt; 标记 &gt; 继承 &gt; 显示<br/>多个标记或者关键字按最高级别过滤";
  27.  
  28. // 过滤方式
  29. const FILTER_MODE = ["继承", "标记", "遮罩", "隐藏", "显示"];
  30.  
  31. // 切换过滤方式
  32. const switchFilterMode = (value) => {
  33. const next = FILTER_MODE.indexOf(value) + 1;
  34.  
  35. if (next >= FILTER_MODE.length) {
  36. return FILTER_MODE[0];
  37. }
  38.  
  39. return FILTER_MODE[next];
  40. };
  41.  
  42. // 数据
  43. const data = (() => {
  44. const d = {
  45. tags: {},
  46. users: {},
  47. keywords: {},
  48. options: {
  49. filterRegdateLimit: 0,
  50. filterPostnumLimit: 0,
  51. filterMode: "隐藏",
  52. },
  53. };
  54.  
  55. const v = GM_getValue(key);
  56.  
  57. if (typeof v !== "object") {
  58. return d;
  59. }
  60.  
  61. return Object.assign(d, v);
  62. })();
  63.  
  64. // 保存数据
  65. const saveData = () => {
  66. GM_setValue(key, data);
  67. };
  68.  
  69. // 增加标记
  70. const addTag = (name) => {
  71. const tag = Object.values(data.tags).find((item) => item.name === name);
  72.  
  73. if (tag) return tag.id;
  74.  
  75. const id =
  76. Math.max(...Object.values(data.tags).map((item) => item.id), 0) + 1;
  77.  
  78. const hash = (() => {
  79. let h = 5381;
  80. for (var i = 0; i < name.length; i++) {
  81. h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
  82. }
  83. return h;
  84. })();
  85.  
  86. const hex = Math.abs(hash).toString(16) + "000000";
  87.  
  88. const hsv = [
  89. `0x${hex.substring(2, 4)}` / 255,
  90. `0x${hex.substring(2, 4)}` / 255 / 2 + 0.25,
  91. `0x${hex.substring(4, 6)}` / 255 / 2 + 0.25,
  92. ];
  93.  
  94. const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
  95.  
  96. const color = ["#", ...rgb].reduce((a, b) => {
  97. return a + ("0" + b.toString(16)).slice(-2);
  98. });
  99.  
  100. data.tags[id] = {
  101. id,
  102. name,
  103. color,
  104. filterMode: FILTER_MODE[0],
  105. };
  106.  
  107. saveData();
  108.  
  109. return id;
  110. };
  111.  
  112. // 增加用户
  113. const addUser = (id, name = null, tags = [], filterMode = FILTER_MODE[0]) => {
  114. if (data.users[id]) return data.users[id];
  115.  
  116. data.users[id] = {
  117. id,
  118. name,
  119. tags,
  120. filterMode,
  121. };
  122.  
  123. saveData();
  124.  
  125. return data.users[id];
  126. };
  127.  
  128. // 增加关键字
  129. const addKeyword = (
  130. keyword,
  131. filterMode = FILTER_MODE[0],
  132. filterLevel = 0
  133. ) => {
  134. const id =
  135. Math.max(...Object.values(data.keywords).map((item) => item.id), 0) + 1;
  136.  
  137. data.keywords[id] = {
  138. id,
  139. keyword,
  140. filterMode,
  141. filterLevel,
  142. };
  143.  
  144. saveData();
  145.  
  146. return id;
  147. };
  148.  
  149. // 旧版本数据迁移
  150. {
  151. const dataKey = "troll_data";
  152. const modeKey = "troll_mode";
  153. const keywordKey = "troll_keyword";
  154.  
  155. if (localStorage.getItem(dataKey)) {
  156. let trollMap = (function () {
  157. try {
  158. return JSON.parse(localStorage.getItem(dataKey)) || {};
  159. } catch (e) {}
  160.  
  161. return {};
  162. })();
  163.  
  164. let filterMode = ~~localStorage.getItem(modeKey);
  165.  
  166. let filterKeyword = localStorage.getItem(keywordKey) || "";
  167.  
  168. // 整理标签
  169. [...new Set(Object.values(trollMap).flat())].forEach((item) =>
  170. addTag(item)
  171. );
  172.  
  173. // 整理用户
  174. Object.keys(trollMap).forEach((item) => {
  175. addUser(
  176. item,
  177. null,
  178. (typeof trollMap[item] === "object" ? trollMap[item] : []).map(
  179. (tag) => addTag(tag)
  180. )
  181. );
  182. });
  183.  
  184. data.options.filterMode = filterMode ? "隐藏" : "标记";
  185. data.options.keyword = filterKeyword;
  186.  
  187. localStorage.removeItem(dataKey);
  188. localStorage.removeItem(modeKey);
  189. localStorage.removeItem(keywordKey);
  190.  
  191. saveData();
  192. }
  193.  
  194. // v1.1.0 -> v1.1.1
  195. {
  196. Object.values(data.users).forEach(({ id, name, tags, enabled }) => {
  197. if (enabled !== undefined) {
  198. data.users[id] = {
  199. id,
  200. name,
  201. tags,
  202. filterMode: enabled ? "继承" : "显示",
  203. };
  204. }
  205. });
  206.  
  207. Object.values(data.tags).forEach(({ id, name, color, enabled }) => {
  208. if (enabled !== undefined) {
  209. data.tags[id] = {
  210. id,
  211. name,
  212. color,
  213. filterMode: enabled ? "继承" : "显示",
  214. };
  215. }
  216. });
  217.  
  218. if (data.options.filterMode === 0) {
  219. data.options.filterMode = "隐藏";
  220. } else if (data.options.filterMode === 1) {
  221. data.options.filterMode = "标记";
  222. }
  223.  
  224. saveData();
  225. }
  226.  
  227. // v1.2.x -> v1.3.0
  228. {
  229. if (data.options.keyword) {
  230. addKeyword(data.options.keyword);
  231.  
  232. delete data.options.keyword;
  233.  
  234. saveData();
  235. }
  236. }
  237. }
  238.  
  239. // 编辑用户标记
  240. const editUser = (() => {
  241. let window;
  242. return (uid, name, callback) => {
  243. if (window === undefined) {
  244. window = n.createCommmonWindow();
  245. }
  246.  
  247. const user = data.users[uid];
  248.  
  249. const content = document.createElement("div");
  250.  
  251. const size = Math.floor((screen.width * 0.8) / 200);
  252.  
  253. const items = Object.values(data.tags).map(
  254. (tag, index) => `
  255. <td class="c1">
  256. <label for="s-tag-${index}" style="display: block; cursor: pointer;">
  257. <b class="block_txt nobr" style="background:${
  258. tag.color
  259. }; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>
  260. </label>
  261. </td>
  262. <td class="c2" width="1">
  263. <input id="s-tag-${index}" type="checkbox" value="${tag.id}" ${
  264. user && user.tags.find((item) => item === tag.id) && "checked"
  265. }/>
  266. </td>
  267. `
  268. );
  269.  
  270. const rows = [...new Array(Math.ceil(items.length / size))].map(
  271. (item, index) =>
  272. `
  273. <tr class="row${(index % 2) + 1}">
  274. ${items.slice(size * index, size * (index + 1)).join("")}
  275. </tr>
  276. `
  277. );
  278.  
  279. content.className = "w100";
  280. content.innerHTML = `
  281. <div class="filter-table-wrapper" style="width: 80vw;">
  282. <table class="filter-table forumbox">
  283. <tbody>
  284. ${rows.join("")}
  285. </tbody>
  286. </table>
  287. </div>
  288. <div style="margin: 10px 0;">
  289. <input placeholder="一次性添加多个标记用&quot;|&quot;隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
  290. </div>
  291. <div style="margin: 10px 0;">
  292. <span>过滤方式:</span>
  293. <button>${(user && user.filterMode) || FILTER_MODE[0]}</button>
  294. <div class="right_">
  295. <button>删除</button>
  296. <button>保存</button>
  297. </div>
  298. </div>
  299. <div class="silver" style="margin-top: 5px;">${FILTER_TIPS}</div>
  300. `;
  301.  
  302. const actions = content.getElementsByTagName("button");
  303.  
  304. actions[0].onclick = () => {
  305. actions[0].innerText = switchFilterMode(
  306. actions[0].innerText || FILTER_MODE[0]
  307. );
  308. };
  309.  
  310. actions[1].onclick = () => {
  311. if (confirm("是否确认?")) {
  312. delete data.users[uid];
  313.  
  314. saveData();
  315.  
  316. callback && callback();
  317.  
  318. window._.hide();
  319. }
  320. };
  321.  
  322. actions[2].onclick = () => {
  323. if (confirm("是否确认?")) {
  324. const values = [...content.getElementsByTagName("input")];
  325. const newTags = values[values.length - 1].value
  326. .split("|")
  327. .filter((item) => item.length)
  328. .map((item) => addTag(item));
  329. const tags = [
  330. ...new Set(
  331. values
  332. .filter((item) => item.type === "checkbox" && item.checked)
  333. .map((item) => ~~item.value)
  334. .concat(newTags)
  335. ),
  336. ].sort();
  337.  
  338. if (user) {
  339. user.tags = tags;
  340. user.filterMode = actions[0].innerText;
  341. } else {
  342. addUser(uid, name, tags, actions[0].innerText);
  343. }
  344.  
  345. saveData();
  346.  
  347. callback && callback();
  348.  
  349. window._.hide();
  350. }
  351. };
  352.  
  353. if (user === undefined) {
  354. actions[1].style = "display: none;";
  355. }
  356.  
  357. window._.addContent(null);
  358. window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
  359. window._.addContent(content);
  360. window._.show();
  361. };
  362. })();
  363.  
  364. // 判断过滤方式
  365. const getFilterMode = async (uid, content, level) => {
  366. let result = -1;
  367.  
  368. const filterRegdateLimit = data.options.filterRegdateLimit || 0;
  369.  
  370. const filterPostnumLimit = data.options.filterPostnumLimit || 0;
  371.  
  372. const user = data.users[uid];
  373.  
  374. const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];
  375.  
  376. const keywords = Object.values(data.keywords);
  377.  
  378. if (filterRegdateLimit > 0 || filterPostnumLimit > 0) {
  379. if (uid > 0) {
  380. if (n.userInfo.users[uid] === undefined) {
  381. n.userInfo.users[uid] = await new Promise((resolve) => {
  382. fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${uid}`)
  383. .then((res) => res.blob())
  384. .then((blob) => {
  385. const reader = new FileReader();
  386.  
  387. reader.onload = () => {
  388. const text = reader.result;
  389. const result = JSON.parse(
  390. text.replace("window.script_muti_get_var_store=", "")
  391. );
  392.  
  393. resolve(result.data[0]);
  394. };
  395.  
  396. reader.readAsText(blob, "GBK");
  397. })
  398. .catch(() => {
  399. resolve();
  400. });
  401. });
  402. }
  403.  
  404. const userInfo = n.userInfo.users[uid];
  405.  
  406. if (
  407. userInfo.regdate * 1000 > new Date() - filterRegdateLimit ||
  408. userInfo.postnum < filterPostnumLimit
  409. ) {
  410. return FILTER_MODE.indexOf("隐藏");
  411. }
  412. }
  413. }
  414.  
  415. if (user) {
  416. const filterMode = FILTER_MODE.indexOf(user.filterMode);
  417.  
  418. if (filterMode > 0) {
  419. return filterMode;
  420. }
  421.  
  422. result = filterMode;
  423. }
  424.  
  425. if (tags.length) {
  426. const filterMode = (() => {
  427. if (tags.some((tag) => tag.filterMode !== "显示")) {
  428. return tags
  429. .filter((tag) => tag.filterMode !== "显示")
  430. .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
  431. .sort((a, b) => b - a)[0];
  432. }
  433.  
  434. return FILTER_MODE.indexOf("显示");
  435. })();
  436.  
  437. if (filterMode > 0) {
  438. return filterMode;
  439. }
  440.  
  441. result = filterMode;
  442. }
  443.  
  444. if (keywords.length) {
  445. const filterMode = (() => {
  446. const r = keywords
  447. .filter((item) => item.keyword && item.filterMode !== "显示")
  448. .filter((item) => (item.filterLevel || 0) >= level)
  449. .sort(
  450. (a, b) =>
  451. FILTER_MODE.indexOf(b.filterMode) -
  452. FILTER_MODE.indexOf(a.filterMode)
  453. )
  454. .find((item) => content.search(item.keyword) >= 0);
  455.  
  456. if (r) {
  457. return FILTER_MODE.indexOf(r.filterMode);
  458. }
  459.  
  460. return result;
  461. })();
  462.  
  463. if (filterMode > 0) {
  464. return filterMode;
  465. }
  466.  
  467. result = filterMode;
  468. }
  469.  
  470. return result;
  471. };
  472.  
  473. // 处理引用
  474. const handleQuote = async (content) => {
  475. const quotes = content.querySelectorAll(".quote");
  476.  
  477. await Promise.all(
  478. [...quotes].map(async (quote) => {
  479. const uid = (() => {
  480. const ele = quote.querySelector("a[href^='/nuke.php']");
  481.  
  482. if (ele) {
  483. const res = ele.getAttribute("href").match(/uid=(\S+)/);
  484.  
  485. if (res) {
  486. return res[1];
  487. }
  488. }
  489.  
  490. return 0;
  491. })();
  492.  
  493. if (uid) {
  494. const filterMode = await new Promise(async (resolve) => {
  495. const mode = await getFilterMode(uid, quote.innerText, 1);
  496.  
  497. if (mode === 0) {
  498. resolve(data.options.filterMode);
  499. }
  500.  
  501. if (mode > 0) {
  502. resolve(FILTER_MODE[mode]);
  503. }
  504.  
  505. resolve("");
  506. });
  507.  
  508. if (filterMode === "标记") {
  509. quote.innerHTML = `
  510. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
  511. <span class="crimson">Troll must die.</span>
  512. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
  513. <div style="display: none;" name="troll_${uid}">
  514. ${quote.innerHTML}
  515. </div>
  516. </div>`;
  517. } else if (filterMode === "遮罩") {
  518. const source = document.createElement("DIV");
  519.  
  520. source.innerHTML = quote.innerHTML;
  521. source.style.display = "none";
  522.  
  523. const caption = document.createElement("CAPTION");
  524.  
  525. caption.className = "filter-mask filter-mask-block";
  526.  
  527. caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
  528. caption.onclick = () => {
  529. quote.removeChild(caption);
  530.  
  531. source.style.display = "";
  532. };
  533.  
  534. quote.innerHTML = "";
  535. quote.appendChild(source);
  536. quote.appendChild(caption);
  537. } else if (filterMode === "隐藏") {
  538. quote.innerHTML = "";
  539. }
  540. }
  541. })
  542. );
  543. };
  544.  
  545. // 过滤
  546. const reFilter = (() => {
  547. let hasNext = false;
  548. let isRunning = false;
  549.  
  550. const func = async () => {
  551. const tPage = location.pathname === "/thread.php";
  552. const pPage = location.pathname === "/read.php";
  553. const uPage = location.pathname === "/nuke.php";
  554.  
  555. if (tPage && new RegExp(`authorid=${self}`).test(location.search)) {
  556. return;
  557. }
  558.  
  559. if (tPage) {
  560. const tData = n.topicArg.data;
  561.  
  562. await Promise.all(
  563. Object.values(tData).map(async (item) => {
  564. if (item.containerC) return;
  565.  
  566. const uid =
  567. item[2].search.match(/uid=(\S+)/) &&
  568. item[2].search.match(/uid=(\S+)/)[1];
  569.  
  570. const filterMode = await new Promise(async (resolve) => {
  571. const mode = await getFilterMode(uid, item[1].innerText, 0);
  572.  
  573. if (mode === 0) {
  574. resolve(data.options.filterMode);
  575. }
  576.  
  577. if (mode > 0) {
  578. resolve(FILTER_MODE[mode]);
  579. }
  580.  
  581. resolve("");
  582. });
  583.  
  584. item.contentC = item[1];
  585.  
  586. item.contentB = item.contentB || item.contentC.innerHTML;
  587.  
  588. item.containerC =
  589. item.containerC || item.contentC.parentNode.parentNode;
  590.  
  591. item.containerC.style = "";
  592. item.contentC.style = "";
  593. item[1].className = item[1].className.replace(" filter-mask", "");
  594. item[2].className = item[2].className.replace(" filter-mask", "");
  595.  
  596. if (filterMode === "标记") {
  597. item.contentC.style = "text-decoration: line-through;";
  598. } else if (filterMode === "遮罩") {
  599. item[1].className += " filter-mask";
  600. item[2].className += " filter-mask";
  601. } else if (filterMode === "隐藏") {
  602. item.containerC.style = "display: none;";
  603. }
  604. })
  605. );
  606. } else if (pPage) {
  607. const pData = n.postArg.data;
  608.  
  609. await Promise.all(
  610. Object.values(pData).map(async (item) => {
  611. if (~~item.pAid === self) return;
  612. if (item.containerC) return;
  613.  
  614. if (typeof item.i === "number") {
  615. item.actionC =
  616. item.actionC ||
  617. (() => {
  618. const ele = item.uInfoC.querySelector('[name="uid"]');
  619.  
  620. ele.onclick = null;
  621.  
  622. return ele;
  623. })();
  624.  
  625. item.tagC =
  626. item.tagC ||
  627. (() => {
  628. const tc = document.createElement("div");
  629.  
  630. tc.className = "filter-tags";
  631.  
  632. item.uInfoC.appendChild(tc);
  633.  
  634. return tc;
  635. })();
  636. }
  637.  
  638. item.pName =
  639. item.pName ||
  640. item.uInfoC.getElementsByClassName("author")[0].innerText;
  641.  
  642. item.reFilter =
  643. item.reFilter ||
  644. (async () => {
  645. const uid = item.pAid;
  646.  
  647. const filterMode = await new Promise(async (resolve) => {
  648. const mode = await getFilterMode(
  649. uid,
  650. item.contentC.innerText,
  651. 1
  652. );
  653.  
  654. if (mode === 0) {
  655. resolve(data.options.filterMode);
  656. }
  657.  
  658. if (mode > 0) {
  659. resolve(FILTER_MODE[mode]);
  660. }
  661.  
  662. resolve("");
  663. });
  664.  
  665. item.avatarC =
  666. item.avatarC ||
  667. (() => {
  668. const tc = document.createElement("div");
  669.  
  670. const avatar = document.getElementById(
  671. `posteravatar${item.i}`
  672. );
  673.  
  674. if (avatar) {
  675. avatar.parentNode.insertBefore(tc, avatar.nextSibling);
  676.  
  677. tc.appendChild(avatar);
  678. }
  679.  
  680. return tc;
  681. })();
  682.  
  683. item.contentB = item.contentB || item.contentC.innerHTML;
  684.  
  685. item.containerC =
  686. item.containerC ||
  687. (() => {
  688. let temp = item.contentC;
  689.  
  690. if (item.i >= 0) {
  691. while (temp.nodeName !== "TBODY") {
  692. temp = temp.parentNode;
  693. }
  694. } else {
  695. while (temp.nodeName !== "DIV") {
  696. temp = temp.parentNode;
  697. }
  698. }
  699.  
  700. return temp;
  701. })();
  702.  
  703. item.avatarC.style.display = "";
  704. item.containerC.style.display = "";
  705. item.contentC.innerHTML = item.contentB;
  706.  
  707. if (item.actionC) {
  708. item.actionC.style = "background: #aaa;";
  709. }
  710.  
  711. if (filterMode === "标记") {
  712. item.avatarC.style.display = "none";
  713. item.contentC.innerHTML = `
  714. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
  715. <span class="crimson">Troll must die.</span>
  716. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
  717. <div style="display: none;" name="troll_${uid}">
  718. ${item.contentB}
  719. </div>
  720. </div>`;
  721.  
  722. if (item.actionC && data.users[uid]) {
  723. item.actionC.style = "background: #cb4042;";
  724. }
  725. } else if (filterMode === "遮罩") {
  726. const caption = document.createElement("CAPTION");
  727.  
  728. if (item.i >= 0) {
  729. caption.className = "filter-mask filter-mask-block";
  730. } else {
  731. caption.className = "filter-mask filter-mask-block left";
  732. caption.style = "width: 47%;";
  733. }
  734.  
  735. caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
  736. caption.onclick = () => {
  737. item.containerC.parentNode.removeChild(caption);
  738. item.containerC.style.display = "";
  739. };
  740.  
  741. item.containerC.parentNode.insertBefore(
  742. caption,
  743. item.containerC
  744. );
  745. item.containerC.style.display = "none";
  746.  
  747. if (item.actionC && data.users[uid]) {
  748. item.actionC.style = "background: #cb4042;";
  749. }
  750. } else if (filterMode === "隐藏") {
  751. item.containerC.style.display = "none";
  752. } else {
  753. await handleQuote(item.contentC);
  754. }
  755.  
  756. if (item.tagC) {
  757. const tags = data.users[uid]
  758. ? data.users[uid].tags.map((tag) => data.tags[tag]) || []
  759. : [];
  760.  
  761. item.tagC.style.display = tags.length ? "" : "none";
  762. item.tagC.innerHTML = tags
  763. .map(
  764. (tag) =>
  765. `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
  766. )
  767. .join("");
  768. }
  769. });
  770.  
  771. if (item.actionC) {
  772. item.actionC.onclick =
  773. item.actionC.onclick ||
  774. ((e) => {
  775. if (item.pAid < 0) return;
  776.  
  777. const user = data.users[item.pAid];
  778.  
  779. if (e.ctrlKey === false) {
  780. editUser(item.pAid, item.pName, item.reFilter);
  781. } else {
  782. if (user) {
  783. delete data.users[user.id];
  784. } else {
  785. addUser(item.pAid, item.pName);
  786. }
  787.  
  788. saveData();
  789. item.reFilter();
  790. }
  791. });
  792. }
  793.  
  794. await item.reFilter();
  795. })
  796. );
  797. } else if (uPage) {
  798. const container = document.getElementById("ucp_block");
  799.  
  800. if (container.firstChild) {
  801. const uid = container.innerText.match(/用户ID\s*:\s*(\S+)/)[1];
  802.  
  803. const name = container.innerText.match(/用户名\s*:\s*(\S+)/)[1];
  804.  
  805. container.tagC =
  806. container.tagC ||
  807. (() => {
  808. const c = document.createElement("span");
  809.  
  810. c.innerHTML = `
  811. <h2 class="catetitle">:: ${name} 的标记 ::</h2>
  812. <div class="cateblock" style="text-align: left; line-height: 1.8em;">
  813. <div class="contentBlock" style="padding: 5px 10px;">
  814. <span>
  815. <ul class="actions" style="padding: 0px; margin: 0px;">
  816. <li style="padding-right: 5px;">
  817. <span>
  818. <a href="javascript: void(0);">[编辑 ${name} 的标记]</a>
  819. </span>
  820. </li>
  821. <div class="clear"></div>
  822. </ul>
  823. </span>
  824. <div class="filter-tags"></div>
  825. <div class="clear"></div>
  826. </div>
  827. </div>
  828. `;
  829.  
  830. c.getElementsByTagName("a")[0].onclick = () => {
  831. editUser(uid, name, container.refresh);
  832. };
  833.  
  834. container.firstChild.insertBefore(
  835. c,
  836. container.firstChild.childNodes[1]
  837. );
  838.  
  839. return c.getElementsByClassName("filter-tags")[0];
  840. })();
  841.  
  842. container.refresh = () => {
  843. container.tagC.innerHTML = data.users[uid].tags
  844. .map(
  845. (tag) =>
  846. `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`
  847. )
  848. .join("");
  849. };
  850.  
  851. container.refresh();
  852. }
  853. }
  854. };
  855.  
  856. const execute = () =>
  857. func().finally(() => {
  858. if (hasNext) {
  859. hasNext = false;
  860.  
  861. execute();
  862. } else {
  863. isRunning = false;
  864. }
  865. });
  866.  
  867. return async () => {
  868. if (isRunning) {
  869. hasNext = true;
  870. } else {
  871. isRunning = true;
  872.  
  873. await execute();
  874. }
  875. };
  876. })();
  877.  
  878. // STYLE
  879. GM_addStyle(`
  880. .filter-table-wrapper {
  881. max-height: 80vh;
  882. overflow-y: auto;
  883. }
  884. .filter-table {
  885. margin: 0;
  886. }
  887. .filter-table th,
  888. .filter-table td {
  889. position: relative;
  890. white-space: nowrap;
  891. }
  892. .filter-table th {
  893. position: sticky;
  894. top: 2px;
  895. z-index: 1;
  896. }
  897. .filter-table input:not([type]), .filter-table input[type="text"] {
  898. margin: 0;
  899. box-sizing: border-box;
  900. height: 100%;
  901. width: 100%;
  902. }
  903. .filter-input-wrapper {
  904. position: absolute;
  905. top: 6px;
  906. right: 6px;
  907. bottom: 6px;
  908. left: 6px;
  909. }
  910. .filter-text-ellipsis {
  911. display: flex;
  912. }
  913. .filter-text-ellipsis > * {
  914. flex: 1;
  915. width: 1px;
  916. overflow: hidden;
  917. text-overflow: ellipsis;
  918. }
  919. .filter-button-group {
  920. margin: -.1em -.2em;
  921. }
  922. .filter-tags {
  923. margin: 2px -0.2em 0;
  924. text-align: left;
  925. }
  926. .filter-mask {
  927. margin: 1px;
  928. color: #81C7D4;
  929. background: #81C7D4;
  930. }
  931. .filter-mask-block {
  932. display: block;
  933. border: 1px solid #66BAB7;
  934. text-align: center !important;
  935. }
  936. .filter-input-wrapper {
  937. position: absolute;
  938. top: 6px;
  939. right: 6px;
  940. bottom: 6px;
  941. left: 6px;
  942. }
  943. `);
  944.  
  945. // UI
  946. const u = (() => {
  947. const modules = {};
  948.  
  949. const tabContainer = (() => {
  950. const c = document.createElement("div");
  951.  
  952. c.className = "w100";
  953. c.innerHTML = `
  954. <div class="right_" style="margin-bottom: 5px;">
  955. <table class="stdbtn" cellspacing="0">
  956. <tbody>
  957. <tr></tr>
  958. </tbody>
  959. </table>
  960. </div>
  961. <div class="clear"></div>
  962. `;
  963.  
  964. return c;
  965. })();
  966.  
  967. const tabPanelContainer = (() => {
  968. const c = document.createElement("div");
  969.  
  970. c.style = "width: 80vw;";
  971.  
  972. return c;
  973. })();
  974.  
  975. const content = (() => {
  976. const c = document.createElement("div");
  977.  
  978. c.append(tabContainer);
  979. c.append(tabPanelContainer);
  980.  
  981. return c;
  982. })();
  983.  
  984. const addModule = (() => {
  985. const tc = tabContainer.getElementsByTagName("tr")[0];
  986. const cc = tabPanelContainer;
  987.  
  988. return (module) => {
  989. const tabBox = document.createElement("td");
  990.  
  991. tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
  992.  
  993. const tab = tabBox.childNodes[0];
  994.  
  995. const toggle = () => {
  996. Object.values(modules).forEach((item) => {
  997. if (item.tab === tab) {
  998. item.tab.className = "nobr";
  999. item.content.style = "display: block";
  1000. item.refresh();
  1001. } else {
  1002. item.tab.className = "nobr silver";
  1003. item.content.style = "display: none";
  1004. }
  1005. });
  1006. };
  1007.  
  1008. tc.append(tabBox);
  1009. cc.append(module.content);
  1010.  
  1011. tab.onclick = toggle;
  1012.  
  1013. modules[module.name] = {
  1014. ...module,
  1015. tab,
  1016. toggle,
  1017. };
  1018.  
  1019. return modules[module.name];
  1020. };
  1021. })();
  1022.  
  1023. return {
  1024. content,
  1025. modules,
  1026. addModule,
  1027. };
  1028. })();
  1029.  
  1030. // 用户
  1031. const userModule = (() => {
  1032. const content = (() => {
  1033. const c = document.createElement("div");
  1034.  
  1035. c.style = "display: none";
  1036. c.innerHTML = `
  1037. <div class="filter-table-wrapper">
  1038. <table class="filter-table forumbox">
  1039. <thead>
  1040. <tr class="block_txt_c0">
  1041. <th class="c1" width="1">昵称</th>
  1042. <th class="c2">标记</th>
  1043. <th class="c3" width="1">过滤方式</th>
  1044. <th class="c4" width="1">操作</th>
  1045. </tr>
  1046. </thead>
  1047. <tbody></tbody>
  1048. </table>
  1049. </div>
  1050. `;
  1051.  
  1052. return c;
  1053. })();
  1054.  
  1055. const refresh = (() => {
  1056. const container = content.getElementsByTagName("tbody")[0];
  1057.  
  1058. const func = () => {
  1059. container.innerHTML = "";
  1060.  
  1061. Object.values(data.users).forEach((item) => {
  1062. const tc = document.createElement("tr");
  1063.  
  1064. tc.className = `row${
  1065. (container.querySelectorAll("TR").length % 2) + 1
  1066. }`;
  1067.  
  1068. tc.refresh = () => {
  1069. if (data.users[item.id]) {
  1070. tc.innerHTML = `
  1071. <td class="c1">
  1072. <a href="/nuke.php?func=ucp&uid=${
  1073. item.id
  1074. }" class="b nobr">[${
  1075. item.name ? "@" + item.name : "#" + item.id
  1076. }]</a>
  1077. </td>
  1078. <td class="c2">
  1079. ${item.tags
  1080. .map((tag) => {
  1081. if (data.tags[tag]) {
  1082. return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
  1083. }
  1084. })
  1085. .join("")}
  1086. </td>
  1087. <td class="c3">
  1088. <div class="filter-table-button-group">
  1089. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1090. </div>
  1091. </td>
  1092. <td class="c4">
  1093. <div class="filter-table-button-group">
  1094. <button>编辑</button>
  1095. <button>删除</button>
  1096. </div>
  1097. </td>
  1098. `;
  1099.  
  1100. const actions = tc.getElementsByTagName("button");
  1101.  
  1102. actions[0].onclick = () => {
  1103. data.users[item.id].filterMode = switchFilterMode(
  1104. data.users[item.id].filterMode || FILTER_MODE[0]
  1105. );
  1106.  
  1107. actions[0].innerHTML = data.users[item.id].filterMode;
  1108.  
  1109. saveData();
  1110. reFilter();
  1111. };
  1112.  
  1113. actions[1].onclick = () => {
  1114. editUser(item.id, item.name, tc.refresh);
  1115. };
  1116.  
  1117. actions[2].onclick = () => {
  1118. if (confirm("是否确认?")) {
  1119. delete data.users[item.id];
  1120. container.removeChild(tc);
  1121.  
  1122. saveData();
  1123. reFilter();
  1124. }
  1125. };
  1126. } else {
  1127. tc.remove();
  1128. }
  1129. };
  1130.  
  1131. tc.refresh();
  1132.  
  1133. container.appendChild(tc);
  1134. });
  1135. };
  1136.  
  1137. return func;
  1138. })();
  1139.  
  1140. return {
  1141. name: "用户",
  1142. content,
  1143. refresh,
  1144. };
  1145. })();
  1146.  
  1147. // 标记
  1148. const tagModule = (() => {
  1149. const content = (() => {
  1150. const c = document.createElement("div");
  1151.  
  1152. c.style = "display: none";
  1153. c.innerHTML = `
  1154. <div class="filter-table-wrapper">
  1155. <table class="filter-table forumbox">
  1156. <thead>
  1157. <tr class="block_txt_c0">
  1158. <th class="c1" width="1">标记</th>
  1159. <th class="c2">列表</th>
  1160. <th class="c3" width="1">过滤方式</th>
  1161. <th class="c4" width="1">操作</th>
  1162. </tr>
  1163. </thead>
  1164. <tbody></tbody>
  1165. </table>
  1166. </div>
  1167. `;
  1168.  
  1169. return c;
  1170. })();
  1171.  
  1172. const refresh = (() => {
  1173. const container = content.getElementsByTagName("tbody")[0];
  1174.  
  1175. const func = () => {
  1176. container.innerHTML = "";
  1177.  
  1178. Object.values(data.tags).forEach((item) => {
  1179. const tc = document.createElement("tr");
  1180.  
  1181. tc.className = `row${
  1182. (container.querySelectorAll("TR").length % 2) + 1
  1183. }`;
  1184.  
  1185. tc.innerHTML = `
  1186. <td class="c1">
  1187. <b class="block_txt nobr" style="background:${
  1188. item.color
  1189. }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
  1190. </td>
  1191. <td class="c2">
  1192. <button>${
  1193. Object.values(data.users).filter((user) =>
  1194. user.tags.find((tag) => tag === item.id)
  1195. ).length
  1196. }
  1197. </button>
  1198. <div style="white-space: normal; display: none;">
  1199. ${Object.values(data.users)
  1200. .filter((user) =>
  1201. user.tags.find((tag) => tag === item.id)
  1202. )
  1203. .map(
  1204. (user) =>
  1205. `<a href="/nuke.php?func=ucp&uid=${
  1206. user.id
  1207. }" class="b nobr">[${
  1208. user.name ? "@" + user.name : "#" + user.id
  1209. }]</a>`
  1210. )
  1211. .join("")}
  1212. </div>
  1213. </td>
  1214. <td class="c3">
  1215. <div class="filter-table-button-group">
  1216. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1217. </div>
  1218. </td>
  1219. <td class="c4">
  1220. <div class="filter-table-button-group">
  1221. <button>删除</button>
  1222. </div>
  1223. </td>
  1224. `;
  1225.  
  1226. const actions = tc.getElementsByTagName("button");
  1227.  
  1228. actions[0].onclick = (() => {
  1229. let hide = true;
  1230. return () => {
  1231. hide = !hide;
  1232. actions[0].nextElementSibling.style.display = hide
  1233. ? "none"
  1234. : "block";
  1235. };
  1236. })();
  1237.  
  1238. actions[1].onclick = () => {
  1239. data.tags[item.id].filterMode = switchFilterMode(
  1240. data.tags[item.id].filterMode || FILTER_MODE[0]
  1241. );
  1242.  
  1243. actions[1].innerHTML = data.tags[item.id].filterMode;
  1244.  
  1245. saveData();
  1246. reFilter();
  1247. };
  1248.  
  1249. actions[2].onclick = () => {
  1250. if (confirm("是否确认?")) {
  1251. delete data.tags[item.id];
  1252.  
  1253. Object.values(data.users).forEach((user) => {
  1254. const index = user.tags.findIndex((tag) => tag === item.id);
  1255. if (index >= 0) {
  1256. user.tags.splice(index, 1);
  1257. }
  1258. });
  1259.  
  1260. container.removeChild(tc);
  1261.  
  1262. saveData();
  1263. reFilter();
  1264. }
  1265. };
  1266.  
  1267. container.appendChild(tc);
  1268. });
  1269. };
  1270.  
  1271. return func;
  1272. })();
  1273.  
  1274. return {
  1275. name: "标记",
  1276. content,
  1277. refresh,
  1278. };
  1279. })();
  1280.  
  1281. // 关键字
  1282. const keywordModule = (() => {
  1283. const content = (() => {
  1284. const c = document.createElement("div");
  1285.  
  1286. c.style = "display: none";
  1287. c.innerHTML = `
  1288. <div class="filter-table-wrapper">
  1289. <table class="filter-table forumbox">
  1290. <thead>
  1291. <tr class="block_txt_c0">
  1292. <th class="c1">列表</th>
  1293. <th class="c2" width="1">过滤方式</th>
  1294. <th class="c3" width="1">包括内容</th>
  1295. <th class="c4" width="1">操作</th>
  1296. </tr>
  1297. </thead>
  1298. <tbody></tbody>
  1299. </table>
  1300. </div>
  1301. <div class="silver" style="margin-top: 10px;">支持正则表达式。比如同类型的可以写在一条规则内用&quot;|&quot;隔开,&quot;ABC|DEF&quot;即为屏蔽带有ABC或者DEF的内容。</div>
  1302. `;
  1303.  
  1304. return c;
  1305. })();
  1306.  
  1307. const refresh = (() => {
  1308. const container = content.getElementsByTagName("tbody")[0];
  1309.  
  1310. const func = () => {
  1311. container.innerHTML = "";
  1312.  
  1313. Object.values(data.keywords).forEach((item) => {
  1314. const tc = document.createElement("tr");
  1315.  
  1316. tc.className = `row${
  1317. (container.querySelectorAll("TR").length % 2) + 1
  1318. }`;
  1319.  
  1320. tc.innerHTML = `
  1321. <td class="c1">
  1322. <div class="filter-input-wrapper">
  1323. <input value="${item.keyword || ""}" />
  1324. </div>
  1325. </td>
  1326. <td class="c2">
  1327. <div class="filter-table-button-group">
  1328. <button>${item.filterMode || FILTER_MODE[0]}</button>
  1329. </div>
  1330. </td>
  1331. <td class="c3">
  1332. <div style="text-align: center;">
  1333. <input type="checkbox" ${
  1334. item.filterLevel ? `checked="checked"` : ""
  1335. } />
  1336. </div>
  1337. </td>
  1338. <td class="c4">
  1339. <div class="filter-table-button-group">
  1340. <button>保存</button>
  1341. <button>删除</button>
  1342. </div>
  1343. </td>
  1344. `;
  1345.  
  1346. const inputElement = tc.querySelector("INPUT");
  1347. const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
  1348. const actions = tc.getElementsByTagName("button");
  1349.  
  1350. actions[0].onclick = () => {
  1351. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1352. };
  1353.  
  1354. actions[1].onclick = () => {
  1355. if (inputElement.value) {
  1356. data.keywords[item.id] = {
  1357. id: item.id,
  1358. keyword: inputElement.value,
  1359. filterMode: actions[0].innerHTML,
  1360. filterLevel: levelElement.checked ? 1 : 0,
  1361. };
  1362.  
  1363. saveData();
  1364. refresh();
  1365. }
  1366. };
  1367.  
  1368. actions[2].onclick = () => {
  1369. if (confirm("是否确认?")) {
  1370. delete data.keywords[item.id];
  1371.  
  1372. saveData();
  1373. refresh();
  1374. }
  1375. };
  1376.  
  1377. container.appendChild(tc);
  1378. });
  1379.  
  1380. {
  1381. const tc = document.createElement("tr");
  1382.  
  1383. tc.className = `row${
  1384. (container.querySelectorAll("TR").length % 2) + 1
  1385. }`;
  1386.  
  1387. tc.innerHTML = `
  1388. <td class="c1">
  1389. <div class="filter-input-wrapper">
  1390. <input value="" />
  1391. </div>
  1392. </td>
  1393. <td class="c2">
  1394. <div class="filter-table-button-group">
  1395. <button>${FILTER_MODE[0]}</button>
  1396. </div>
  1397. </td>
  1398. <td class="c3">
  1399. <div style="text-align: center;">
  1400. <input type="checkbox" />
  1401. </div>
  1402. </td>
  1403. <td class="c4">
  1404. <div class="filter-table-button-group">
  1405. <button>添加</button>
  1406. </div>
  1407. </td>
  1408. `;
  1409.  
  1410. const inputElement = tc.querySelector("INPUT");
  1411. const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
  1412. const actions = tc.getElementsByTagName("button");
  1413.  
  1414. actions[0].onclick = () => {
  1415. actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
  1416. };
  1417.  
  1418. actions[1].onclick = () => {
  1419. if (inputElement.value) {
  1420. addKeyword(
  1421. inputElement.value,
  1422. actions[0].innerHTML,
  1423. levelElement.checked ? 1 : 0
  1424. );
  1425.  
  1426. saveData();
  1427. refresh();
  1428. }
  1429. };
  1430.  
  1431. container.appendChild(tc);
  1432. }
  1433. };
  1434.  
  1435. return func;
  1436. })();
  1437.  
  1438. return {
  1439. name: "关键字",
  1440. content,
  1441. refresh,
  1442. };
  1443. })();
  1444.  
  1445. // 通用设置
  1446. const commonModule = (() => {
  1447. const content = (() => {
  1448. const c = document.createElement("div");
  1449.  
  1450. c.style = "display: none";
  1451.  
  1452. return c;
  1453. })();
  1454.  
  1455. const refresh = (() => {
  1456. const container = content;
  1457.  
  1458. const func = () => {
  1459. container.innerHTML = "";
  1460.  
  1461. // 默认过滤方式
  1462. {
  1463. const tc = document.createElement("div");
  1464.  
  1465. tc.innerHTML += `
  1466. <div>默认过滤方式</div>
  1467. <div></div>
  1468. <div class="silver" style="margin-top: 10px;">${FILTER_TIPS}</div>
  1469. `;
  1470.  
  1471. ["标记", "遮罩", "隐藏"].forEach((item, index) => {
  1472. const ele = document.createElement("SPAN");
  1473.  
  1474. ele.innerHTML += `
  1475. <input id="s-fm-${index}" type="radio" name="filterType" ${
  1476. data.options.filterMode === item && "checked"
  1477. }>
  1478. <label for="s-fm-${index}" style="cursor: pointer;">${item}</label>
  1479. `;
  1480.  
  1481. const inp = ele.querySelector("input");
  1482.  
  1483. inp.onchange = () => {
  1484. if (inp.checked) {
  1485. data.options.filterMode = item;
  1486. saveData();
  1487. reFilter();
  1488. }
  1489. };
  1490.  
  1491. tc.querySelectorAll("div")[1].append(ele);
  1492. });
  1493.  
  1494. container.appendChild(tc);
  1495. }
  1496.  
  1497. // 小号过滤(时间)
  1498. {
  1499. const tc = document.createElement("div");
  1500.  
  1501. tc.innerHTML += `
  1502. <br/>
  1503. <div>
  1504. 隐藏注册时间小于<input value="${
  1505. (data.options.filterRegdateLimit || 0) / 86400000
  1506. }" maxLength="4" style="width: 48px;" />天的用户
  1507. <button>确认</button>
  1508. </div>
  1509. `;
  1510.  
  1511. const actions = tc.getElementsByTagName("button");
  1512.  
  1513. actions[0].onclick = () => {
  1514. const v = actions[0].previousElementSibling.value;
  1515.  
  1516. const n = Number(v) || 0;
  1517.  
  1518. data.options.filterRegdateLimit = n < 0 ? 0 : n * 86400000;
  1519.  
  1520. saveData();
  1521. reFilter();
  1522. };
  1523.  
  1524. container.appendChild(tc);
  1525. }
  1526.  
  1527. // 小号过滤(发帖数)
  1528. {
  1529. const tc = document.createElement("div");
  1530.  
  1531. tc.innerHTML += `
  1532. <br/>
  1533. <div>
  1534. 隐藏发帖数量小于<input value="${
  1535. data.options.filterPostnumLimit || 0
  1536. }" maxLength="5" style="width: 48px;" />贴的用户
  1537. <button>确认</button>
  1538. </div>
  1539. `;
  1540.  
  1541. const actions = tc.getElementsByTagName("button");
  1542.  
  1543. actions[0].onclick = () => {
  1544. const v = actions[0].previousElementSibling.value;
  1545.  
  1546. const n = Number(v) || 0;
  1547.  
  1548. data.options.filterPostnumLimit = n < 0 ? 0 : n;
  1549.  
  1550. saveData();
  1551. reFilter();
  1552. };
  1553.  
  1554. container.appendChild(tc);
  1555. }
  1556.  
  1557. // 删除没有标记的用户
  1558. {
  1559. const tc = document.createElement("div");
  1560.  
  1561. tc.innerHTML += `
  1562. <br/>
  1563. <div>
  1564. <button>删除没有标记的用户</button>
  1565. </div>
  1566. `;
  1567.  
  1568. const actions = tc.getElementsByTagName("button");
  1569.  
  1570. actions[0].onclick = () => {
  1571. if (confirm("是否确认?")) {
  1572. Object.values(data.users).forEach((item) => {
  1573. if (item.tags.length === 0) {
  1574. delete data.users[item.id];
  1575. }
  1576. });
  1577.  
  1578. saveData();
  1579. reFilter();
  1580. }
  1581. };
  1582.  
  1583. container.appendChild(tc);
  1584. }
  1585.  
  1586. // 删除没有用户的标记
  1587. {
  1588. const tc = document.createElement("div");
  1589.  
  1590. tc.innerHTML += `
  1591. <br/>
  1592. <div>
  1593. <button>删除没有用户的标记</button>
  1594. </div>
  1595. `;
  1596.  
  1597. const actions = tc.getElementsByTagName("button");
  1598.  
  1599. actions[0].onclick = () => {
  1600. if (confirm("是否确认?")) {
  1601. Object.values(data.tags).forEach((item) => {
  1602. if (
  1603. Object.values(data.users).filter((user) =>
  1604. user.tags.find((tag) => tag === item.id)
  1605. ).length === 0
  1606. ) {
  1607. delete data.tags[item.id];
  1608. }
  1609. });
  1610.  
  1611. saveData();
  1612. reFilter();
  1613. }
  1614. };
  1615.  
  1616. container.appendChild(tc);
  1617. }
  1618. };
  1619.  
  1620. return func;
  1621. })();
  1622.  
  1623. return {
  1624. name: "通用设置",
  1625. content,
  1626. refresh,
  1627. };
  1628. })();
  1629.  
  1630. u.addModule(userModule).toggle();
  1631. u.addModule(tagModule);
  1632. u.addModule(keywordModule);
  1633. u.addModule(commonModule);
  1634.  
  1635. // 增加菜单项
  1636. (() => {
  1637. const title = "过滤设置";
  1638.  
  1639. let window;
  1640.  
  1641. n.mainMenu.addItemOnTheFly(title, null, () => {
  1642. if (window === undefined) {
  1643. window = n.createCommmonWindow();
  1644. }
  1645.  
  1646. window._.addContent(null);
  1647. window._.addTitle(title);
  1648. window._.addContent(u.content);
  1649. window._.show();
  1650. });
  1651. })();
  1652.  
  1653. // 执行过滤
  1654. (() => {
  1655. const hookFunction = (object, functionName, callback) => {
  1656. ((originalFunction) => {
  1657. object[functionName] = function () {
  1658. const returnValue = originalFunction.apply(this, arguments);
  1659.  
  1660. callback.apply(this, [returnValue, originalFunction, arguments]);
  1661.  
  1662. return returnValue;
  1663. };
  1664. })(object[functionName]);
  1665. };
  1666.  
  1667. const initialized = {
  1668. topicArg: false,
  1669. postArg: false,
  1670. };
  1671.  
  1672. hookFunction(n, "eval", () => {
  1673. if (Object.values(initialized).findIndex((item) => item === false) < 0) {
  1674. return;
  1675. }
  1676.  
  1677. if (n.topicArg && initialized.topicArg === false) {
  1678. hookFunction(n.topicArg, "add", reFilter);
  1679.  
  1680. initialized.topicArg = true;
  1681. }
  1682.  
  1683. if (n.postArg && initialized.postArg === false) {
  1684. hookFunction(n.postArg, "proc", reFilter);
  1685.  
  1686. initialized.postArg = true;
  1687. }
  1688. });
  1689.  
  1690. if (n.ucp) {
  1691. hookFunction(n.ucp, "_echo", reFilter);
  1692. }
  1693.  
  1694. reFilter();
  1695. })();
  1696. })(commonui, __CURRENT_UID);