NGA Filter

troll must die

当前为 2022-01-23 提交的版本,查看 最新版本

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