NGA Filter

troll must die

当前为 2020-02-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name NGA Filter
  3. // @namespace https://greasyfork.org/users/263018
  4. // @version 1.0.2
  5. // @author snyssss
  6. // @description troll must die
  7.  
  8. // @match *bbs.nga.cn/thread.php?fid=*
  9. // @match *bbs.nga.cn/read.php?tid=*
  10. // @match *bbs.nga.cn/nuke.php?*
  11. // @match *ngabbs.com/thread.php?fid=*
  12. // @match *ngabbs.com/read.php?tid=*
  13. // @match *ngabbs.com/nuke.php?*
  14.  
  15. // @grant GM_addStyle
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18.  
  19. // @noframes
  20. // ==/UserScript==
  21.  
  22. (n => {
  23. "use strict";
  24.  
  25. if (n === undefined) return;
  26.  
  27. const key = "NGAFilter";
  28.  
  29. // 数据
  30. const data = (() => {
  31. const d = {
  32. tags: {},
  33. users: {},
  34. options: {
  35. filterMode: 0,
  36. keyword: ""
  37. }
  38. };
  39. const v = GM_getValue(key);
  40. if (typeof v !== "object") {
  41. return d;
  42. }
  43. return Object.assign(d, v);
  44. })();
  45.  
  46. // 保存数据
  47. const saveData = () => {
  48. GM_setValue(key, data);
  49. };
  50.  
  51. // 增加标记
  52. const addTag = name => {
  53. const tag = Object.values(data.tags).find(item => item.name === name);
  54.  
  55. if (tag) return tag.id;
  56.  
  57. const id =
  58. Math.max(...Object.values(data.tags).map(item => item.id), 0) + 1;
  59.  
  60. const hash = (() => {
  61. let h = 5381;
  62. for (var i = 0; i < name.length; i++) {
  63. h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
  64. }
  65. return h;
  66. })();
  67.  
  68. const hex = Math.abs(hash).toString(16) + "000000";
  69.  
  70. const hsv = [
  71. `0x${hex.substr(2, 2)}` / 255,
  72. `0x${hex.substr(2, 2)}` / 255 / 2 + 0.25,
  73. `0x${hex.substr(4, 2)}` / 255 / 2 + 0.25
  74. ];
  75.  
  76. const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
  77.  
  78. const color = ["#", ...rgb].reduce((a, b) => {
  79. return a + ("0" + b.toString(16)).slice(-2);
  80. });
  81.  
  82. data.tags[id] = {
  83. id,
  84. name,
  85. color,
  86. enabled: true
  87. };
  88.  
  89. saveData();
  90.  
  91. return id;
  92. };
  93.  
  94. // 增加用户
  95. const addUser = (id, name = null, tags = [], isEnabled = true) => {
  96. if (data.users[id]) return data.users[id];
  97.  
  98. data.users[id] = {
  99. id,
  100. name,
  101. tags,
  102. enabled: isEnabled
  103. };
  104.  
  105. saveData();
  106.  
  107. return data.users[id];
  108. };
  109.  
  110. // 旧版本数据迁移
  111. {
  112. const dataKey = "troll_data";
  113. const modeKey = "troll_mode";
  114. const keywordKey = "troll_keyword";
  115.  
  116. if (localStorage.getItem(dataKey)) {
  117. let trollMap = (function() {
  118. try {
  119. return JSON.parse(localStorage.getItem(dataKey)) || {};
  120. } catch (e) {}
  121.  
  122. return {};
  123. })();
  124.  
  125. let filterMode = ~~localStorage.getItem(modeKey);
  126.  
  127. let filterKeyword = localStorage.getItem(keywordKey) || "";
  128.  
  129. // 整理标签
  130. [...new Set(Object.values(trollMap).flat())].forEach(item =>
  131. addTag(item)
  132. );
  133.  
  134. // 整理用户
  135. Object.keys(trollMap).forEach(item => {
  136. addUser(
  137. item,
  138. null,
  139. trollMap[item].map(tag => addTag(tag))
  140. );
  141. });
  142.  
  143. data.options.filterMode = filterMode ? 0 : 1;
  144. data.options.keyword = filterKeyword;
  145.  
  146. localStorage.removeItem(dataKey);
  147. localStorage.removeItem(modeKey);
  148. localStorage.removeItem(keywordKey);
  149.  
  150. saveData();
  151. }
  152. }
  153.  
  154. // 编辑用户标记
  155. const editUser = (() => {
  156. let window;
  157. return (uid, name, callback) => {
  158. if (window === undefined) {
  159. window = n.createCommmonWindow();
  160. }
  161.  
  162. const user = data.users[uid];
  163.  
  164. const content = document.createElement("div");
  165.  
  166. content.className = "w100";
  167. content.innerHTML = `
  168. <table class="filter-table" style="min-width: 400px;">
  169. <tbody>
  170. ${Object.values(data.tags)
  171. .map(
  172. tag => `
  173. <tr>
  174. <td>
  175. <b class="block_txt nobr" style="background:${
  176. tag.color
  177. }; color:#fff; margin: 0.1em 0.2em;">${
  178. tag.name
  179. }</b>
  180. </td>
  181. <td>
  182. <input type="checkbox" value="${
  183. tag.id
  184. }" ${user &&
  185. user.tags.find(item => item === tag.id) &&
  186. "checked"}/>
  187. </td>
  188. </tr>
  189. `
  190. )
  191. .join("")}
  192. </tbody>
  193. <tfoot>
  194. <tr>
  195. <td colspan="2">
  196. <input placeholder="一次性添加多个标记用&quot;|&quot;隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
  197. </td>
  198. </tr>
  199. </tfoot>
  200. </table>
  201. <div style="margin: 10px 0;">
  202. <button>${user && user.enabled === false ? "启用" : "禁用"}</button>
  203. <div class="right_">
  204. <button>删除</button>
  205. <button>保存</button>
  206. </div>
  207. </div>
  208. `;
  209.  
  210. const actions = content.getElementsByTagName("button");
  211.  
  212. actions[0].onclick = () => {
  213. actions[0].innerText =
  214. actions[0].innerText === "禁用" ? "启用" : "禁用";
  215. };
  216.  
  217. actions[1].onclick = () => {
  218. if (confirm("是否确认?")) {
  219. delete data.users[uid];
  220.  
  221. saveData();
  222.  
  223. callback && callback();
  224.  
  225. window._.hide();
  226. }
  227. };
  228.  
  229. actions[2].onclick = () => {
  230. if (confirm("是否确认?")) {
  231. const values = [...content.getElementsByTagName("input")];
  232. const newTags = values[values.length - 1].value
  233. .split("|")
  234. .filter(item => item.length)
  235. .map(item => addTag(item));
  236. const tags = [
  237. ...new Set(
  238. values
  239. .filter(item => item.type === "checkbox" && item.checked)
  240. .map(item => ~~item.value)
  241. .concat(newTags)
  242. )
  243. ].sort();
  244.  
  245. if (user) {
  246. user.tags = tags;
  247. user.enabled = actions[0].innerText === "禁用";
  248. } else {
  249. addUser(uid, name, tags, actions[0].innerText === "禁用");
  250. }
  251.  
  252. saveData();
  253.  
  254. callback && callback();
  255.  
  256. window._.hide();
  257. }
  258. };
  259.  
  260. if (user === undefined) {
  261. actions[1].style = "display: none;";
  262. }
  263.  
  264. window._.addContent(null);
  265. window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
  266. window._.addContent(content);
  267. window._.show();
  268. };
  269. })();
  270.  
  271. // 过滤
  272. const reFilter = (() => {
  273. const tPage = location.pathname === "/thread.php";
  274. const pPage = location.pathname === "/read.php";
  275. const uPage = location.pathname === "/nuke.php";
  276.  
  277. const func = (() => {
  278. if (tPage) {
  279. return () => {
  280. const tData = n.topicArg.data;
  281.  
  282. Object.values(tData).forEach(item => {
  283. const uid =
  284. item[2].search.match(/uid=(\S+)/) &&
  285. item[2].search.match(/uid=(\S+)/)[1];
  286.  
  287. const user = data.users[uid];
  288.  
  289. const tags = user ? user.tags.map(tag => data.tags[tag]) : [];
  290.  
  291. const isBlock =
  292. user &&
  293. user.enabled &&
  294. (tags.length === 0 || tags.filter(tag => tag.enabled).length);
  295.  
  296. item.contentC = item[1];
  297.  
  298. item.contentB = item.contentB || item.contentC.innerHTML;
  299.  
  300. item.containerC =
  301. item.containerC || item.contentC.parentNode.parentNode;
  302.  
  303. item.containerC.style =
  304. isBlock && data.options.filterMode === 0 ? "display: none;" : "";
  305.  
  306. item.contentC.style =
  307. isBlock && data.options.filterMode === 1
  308. ? "text-decoration: line-through;"
  309. : "";
  310. });
  311. };
  312. } else if (pPage) {
  313. return () => {
  314. const pData = n.postArg.data;
  315.  
  316. Object.values(pData).forEach(item => {
  317. if (typeof item.i === "number") {
  318. item.actionC =
  319. item.actionC ||
  320. (() => {
  321. const ele = item.uInfoC.firstElementChild.lastElementChild;
  322.  
  323. ele.onclick = null;
  324.  
  325. return ele;
  326. })();
  327.  
  328. item.tagC =
  329. item.tagC ||
  330. (() => {
  331. const tc = document.createElement("div");
  332.  
  333. tc.className = "filter-tags";
  334.  
  335. item.uInfoC.appendChild(tc);
  336.  
  337. return tc;
  338. })();
  339. }
  340.  
  341. item.pName =
  342. item.pName ||
  343. item.uInfoC.getElementsByClassName("author")[0].innerText;
  344.  
  345. item.reFilter =
  346. item.reFilter ||
  347. (() => {
  348. const user = data.users[item.pAid];
  349.  
  350. const tags = user ? user.tags.map(tag => data.tags[tag]) : [];
  351.  
  352. const isBlock =
  353. user &&
  354. user.enabled &&
  355. (tags.length === 0 || tags.filter(tag => tag.enabled).length);
  356.  
  357. item.avatarC =
  358. item.avatarC ||
  359. (() => {
  360. const tc = document.createElement("div");
  361.  
  362. const avatar = document.getElementById(
  363. `posteravatar${item.i}`
  364. );
  365.  
  366. if (avatar) {
  367. avatar.parentNode.insertBefore(tc, avatar.nextSibling);
  368.  
  369. tc.appendChild(avatar);
  370. }
  371.  
  372. return tc;
  373. })();
  374.  
  375. item.contentB = item.contentB || item.contentC.innerHTML;
  376.  
  377. item.containerC =
  378. item.containerC ||
  379. (() => {
  380. let temp = item.contentC;
  381.  
  382. while (
  383. temp.className !== "forumbox postbox" &&
  384. temp.className !== "comment_c left"
  385. ) {
  386. temp = temp.parentNode;
  387. }
  388.  
  389. return temp;
  390. })();
  391.  
  392. item.containerC.style.display =
  393. isBlock && data.options.filterMode === 0 ? "none" : "";
  394.  
  395. item.contentC.innerHTML =
  396. isBlock && data.options.filterMode === 1
  397. ? `
  398. <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7;">
  399. <span class="crimson">Troll must die.</span>
  400. <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${user.id}')].forEach(item => item.style.display = '')">点击查看</a>
  401. <div style="display: none;" name="troll_${user.id}">
  402. ${item.contentB}
  403. </div>
  404. </div>`
  405. : item.contentB;
  406.  
  407. item.avatarC.style.display = isBlock ? "none" : "";
  408.  
  409. if (item.actionC) {
  410. item.actionC.style =
  411. user && user.enabled
  412. ? "background: #cb4042;"
  413. : "background: #aaa;";
  414. }
  415.  
  416. if (item.tagC) {
  417. item.tagC.style.display = tags.length ? "" : "none";
  418. item.tagC.innerHTML = tags
  419. .map(
  420. tag =>
  421. `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
  422. )
  423. .join("");
  424. }
  425. });
  426.  
  427. if (item.actionC) {
  428. item.actionC.onclick =
  429. item.actionC.onclick ||
  430. (e => {
  431. if (item.pAid < 0) return;
  432.  
  433. const user = data.users[item.pAid];
  434.  
  435. if (e.ctrlKey) {
  436. editUser(item.pAid, item.pName, item.reFilter);
  437. } else {
  438. if (user) {
  439. if (user.tags.length) {
  440. user.enabled = !user.enabled;
  441. user.name = item.pName;
  442. } else {
  443. delete data.users[user.id];
  444. }
  445. } else {
  446. addUser(item.pAid, item.pName);
  447. }
  448.  
  449. saveData();
  450. item.reFilter();
  451. }
  452. });
  453. }
  454.  
  455. item.reFilter();
  456. });
  457. };
  458. } else if (uPage) {
  459. return () => {
  460. const container = document.getElementById("ucp_block");
  461.  
  462. if (container.firstChild) {
  463. const uid = container.innerText.match(/用户ID\s*:\s*(\S+)/)[1];
  464.  
  465. const name = container.innerText.match(/用户名\s*:\s*(\S+)/)[1];
  466.  
  467. container.tagC =
  468. container.tagC ||
  469. (() => {
  470. const c = document.createElement("span");
  471.  
  472. c.innerHTML = `
  473. <h2 class="catetitle">:: ${name} 的标记 ::</h2>
  474. <div class="cateblock" style="text-align: left; line-height: 1.8em;">
  475. <div class="contentBlock" style="padding: 5px 10px;">
  476. <span>
  477. <ul class="actions" style="padding: 0px; margin: 0px;">
  478. <li style="padding-right: 5px;">
  479. <span>
  480. <a href="javascript: void(0);">[编辑 ${name} 的标记]</a>
  481. </span>
  482. </li>
  483. <div class="clear"></div>
  484. </ul>
  485. </span>
  486. <div class="filter-tags"></div>
  487. <div class="clear"></div>
  488. </div>
  489. </div>
  490. `;
  491.  
  492. c.getElementsByTagName("a")[0].onclick = () => {
  493. editUser(uid, name, container.refresh);
  494. };
  495.  
  496. container.firstChild.insertBefore(
  497. c,
  498. container.firstChild.childNodes[1]
  499. );
  500.  
  501. return c.getElementsByClassName("filter-tags")[0];
  502. })();
  503.  
  504. container.refresh = () => {
  505. container.tagC.innerHTML = data.users[uid].tags
  506. .map(
  507. tag =>
  508. `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`
  509. )
  510. .join("");
  511. };
  512.  
  513. container.refresh();
  514. }
  515. };
  516. }
  517.  
  518. return () => {};
  519. })();
  520.  
  521. const observer = new MutationObserver(mutations => {
  522. if (mutations.find(mutation => mutation.addedNodes.length)) {
  523. func();
  524. }
  525. });
  526.  
  527. if (tPage) {
  528. observer.observe(document.getElementById("topicrows"), {
  529. childList: true
  530. });
  531. } else if (pPage) {
  532. observer.observe(document.getElementById("m_posts_c"), {
  533. childList: true
  534. });
  535. } else if (uPage) {
  536. observer.observe(document.getElementById("ucp_block"), {
  537. childList: true
  538. });
  539. }
  540.  
  541. func();
  542.  
  543. return func;
  544. })();
  545.  
  546. // STYLE
  547. GM_addStyle(`
  548. .filter-tags {
  549. margin: 2px -0.2em 0;
  550. text-align: left;
  551. }
  552. .filter-table {
  553. border: 1px solid #ead5bc;
  554. border-left: none;
  555. border-bottom: none;
  556. width: 99.95%;
  557. }
  558. .filter-table thead {
  559. background-color: #591804;
  560. color: #fff8e7;
  561. }
  562. .filter-table tbody tr {
  563. background-color: #fff0cd;
  564. }
  565. .filter-table tbody tr:nth-of-type(odd) {
  566. background-color: #fff8e7;
  567. }
  568. .filter-table td {
  569. border: 1px solid #ead5bc;
  570. border-top: none;
  571. border-right: none;
  572. padding: 6px;
  573. }
  574. `);
  575.  
  576. // UI
  577. const u = (() => {
  578. const modules = {};
  579.  
  580. const tabContainer = (() => {
  581. const c = document.createElement("div");
  582.  
  583. c.className = "w100";
  584. c.innerHTML = `
  585. <div class="right_" style="margin-bottom: 5px;">
  586. <table class="stdbtn" cellspacing="0">
  587. <tbody>
  588. <tr></tr>
  589. </tbody>
  590. </table>
  591. </div>
  592. <div class="clear"></div>
  593. `;
  594.  
  595. return c;
  596. })();
  597.  
  598. const tabPanelContainer = (() => {
  599. const c = document.createElement("div");
  600.  
  601. c.style =
  602. "min-width: 20vw; max-width: 80vw; max-height: 80vh; overflow: auto;";
  603. c.innerHTML = `
  604. `;
  605.  
  606. return c;
  607. })();
  608.  
  609. const content = (() => {
  610. const c = document.createElement("div");
  611.  
  612. c.append(tabContainer);
  613. c.append(tabPanelContainer);
  614.  
  615. return c;
  616. })();
  617.  
  618. const addModule = (() => {
  619. const tc = tabContainer.getElementsByTagName("tr")[0];
  620. const cc = tabPanelContainer;
  621.  
  622. return module => {
  623. const tabBox = document.createElement("td");
  624.  
  625. tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
  626.  
  627. const tab = tabBox.childNodes[0];
  628.  
  629. const toggle = () => {
  630. Object.values(modules).forEach(item => {
  631. if (item.tab === tab) {
  632. item.tab.className = "nobr";
  633. item.content.style = "display: block";
  634. item.refresh();
  635. } else {
  636. item.tab.className = "nobr silver";
  637. item.content.style = "display: none";
  638. }
  639. });
  640. };
  641.  
  642. tc.append(tabBox);
  643. cc.append(module.content);
  644.  
  645. tab.onclick = toggle;
  646.  
  647. modules[module.name] = {
  648. ...module,
  649. tab,
  650. toggle
  651. };
  652.  
  653. return modules[module.name];
  654. };
  655. })();
  656.  
  657. return {
  658. content,
  659. modules,
  660. addModule
  661. };
  662. })();
  663.  
  664. // 屏蔽列表
  665. const blockModule = (() => {
  666. const content = (() => {
  667. const c = document.createElement("div");
  668.  
  669. c.style = "display: none";
  670. c.innerHTML = `
  671. <table class="filter-table">
  672. <thead>
  673. <tr>
  674. <td>
  675. <b style="margin: 0.1em 0.2em;">昵称</b>
  676. </td>
  677. <td>
  678. <b style="margin: 0.1em 0.2em;">标记</b>
  679. </td>
  680. <td>
  681. <b style="margin: 0.1em 0.2em;">操作</b>
  682. </td>
  683. </tr>
  684. </thead>
  685. <tbody></tbody>
  686. </table>
  687. `;
  688.  
  689. return c;
  690. })();
  691.  
  692. const refresh = (() => {
  693. const container = content.getElementsByTagName("tbody")[0];
  694.  
  695. const func = () => {
  696. container.innerHTML = "";
  697.  
  698. Object.values(data.users).forEach(item => {
  699. const tc = document.createElement("tr");
  700.  
  701. tc.refresh = () => {
  702. if (data.users[item.id]) {
  703. tc.innerHTML = `
  704. <tr>
  705. <td>
  706. <a href="/nuke.php?func=ucp&uid=${
  707. item.id
  708. }" class="b nobr">[${
  709. item.name ? "@" + item.name : "#" + item.id
  710. }]</a>
  711. </td>
  712. <td>
  713. ${item.tags
  714. .map(tag => {
  715. if (data.tags[tag]) {
  716. return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
  717. }
  718. })
  719. .join("")}
  720. </td>
  721. <td class="nobr">
  722. <button>编辑</button>
  723. <button>${item.enabled ? "禁用" : "启用"}</button>
  724. <button>删除</button>
  725. </td>
  726. </tr>
  727. `;
  728.  
  729. const actions = tc.getElementsByTagName("button");
  730.  
  731. actions[0].onclick = () => {
  732. editUser(item.id, item.name, tc.refresh);
  733. };
  734.  
  735. actions[1].onclick = () => {
  736. data.users[item.id].enabled = !data.users[item.id].enabled;
  737. actions[1].innerHTML = data.users[item.id].enabled
  738. ? "禁用"
  739. : "启用";
  740.  
  741. saveData();
  742. reFilter();
  743. };
  744.  
  745. actions[2].onclick = () => {
  746. if (confirm("是否确认?")) {
  747. delete data.users[item.id];
  748. container.removeChild(tc);
  749.  
  750. saveData();
  751. reFilter();
  752. }
  753. };
  754. } else {
  755. tc.remove();
  756. }
  757. };
  758.  
  759. tc.refresh();
  760.  
  761. container.appendChild(tc);
  762. });
  763. };
  764.  
  765. return func;
  766. })();
  767.  
  768. return {
  769. name: "屏蔽列表",
  770. content,
  771. refresh
  772. };
  773. })();
  774.  
  775. // 标记设置
  776. const tagModule = (() => {
  777. const content = (() => {
  778. const c = document.createElement("div");
  779.  
  780. c.style = "display: none";
  781. c.innerHTML = `
  782. <table class="filter-table">
  783. <thead>
  784. <tr>
  785. <td>
  786. <b style="margin: 0.1em 0.2em;">标记</b>
  787. </td>
  788. <td>
  789. <b style="margin: 0.1em 0.2em;">列表</b>
  790. </td>
  791. <td>
  792. <b style="margin: 0.1em 0.2em;">操作</b>
  793. </td>
  794. </tr>
  795. </thead>
  796. <tbody></tbody>
  797. </table>
  798. `;
  799.  
  800. return c;
  801. })();
  802.  
  803. const refresh = (() => {
  804. const container = content.getElementsByTagName("tbody")[0];
  805.  
  806. const func = () => {
  807. container.innerHTML = "";
  808.  
  809. Object.values(data.tags).forEach(item => {
  810. const tc = document.createElement("tr");
  811.  
  812. tc.innerHTML = `
  813. <tr>
  814. <td>
  815. <b class="block_txt nobr" style="background:${
  816. item.color
  817. }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
  818. </td>
  819. <td>
  820. <button>${
  821. Object.values(data.users).filter(user =>
  822. user.tags.find(tag => tag === item.id)
  823. ).length
  824. }
  825. </button>
  826. <div style="display: none;">
  827. ${Object.values(data.users)
  828. .filter(user =>
  829. user.tags.find(tag => tag === item.id)
  830. )
  831. .map(
  832. user =>
  833. `<a href="/nuke.php?func=ucp&uid=${
  834. user.id
  835. }" class="b nobr">[${
  836. user.name ? "@" + user.name : "#" + user.id
  837. }]</a>`
  838. )
  839. .join("")}
  840. </div>
  841. </td>
  842. <td class="nobr">
  843. <button>${item.enabled ? "禁用" : "启用"}</button>
  844. <button>删除</button>
  845. </td>
  846. </tr>
  847. `;
  848.  
  849. const actions = tc.getElementsByTagName("button");
  850.  
  851. actions[0].onclick = (() => {
  852. let hide = true;
  853. return () => {
  854. hide = !hide;
  855. actions[0].nextElementSibling.style = `display: ${
  856. hide ? "none" : "block"
  857. };`;
  858. };
  859. })();
  860.  
  861. actions[1].onclick = () => {
  862. data.tags[item.id].enabled = !data.tags[item.id].enabled;
  863. actions[1].innerHTML = data.tags[item.id].enabled ? "禁用" : "启用";
  864.  
  865. saveData();
  866. reFilter();
  867. };
  868.  
  869. actions[2].onclick = () => {
  870. if (confirm("是否确认?")) {
  871. delete data.tags[item.id];
  872.  
  873. Object.values(data.users).forEach(user => {
  874. const index = user.tags.findIndex(tag => tag === item.id);
  875. if (index >= 0) {
  876. user.tags.splice(index, 1);
  877. }
  878. });
  879.  
  880. container.removeChild(tc);
  881.  
  882. saveData();
  883. reFilter();
  884. }
  885. };
  886.  
  887. container.appendChild(tc);
  888. });
  889. };
  890.  
  891. return func;
  892. })();
  893.  
  894. return {
  895. name: "标记设置",
  896. content,
  897. refresh
  898. };
  899. })();
  900.  
  901. // 通用设置
  902. const commonModule = (() => {
  903. const content = (() => {
  904. const c = document.createElement("div");
  905.  
  906. c.style = "display: none";
  907.  
  908. return c;
  909. })();
  910.  
  911. const refresh = (() => {
  912. const container = content;
  913.  
  914. const func = () => {
  915. container.innerHTML = "";
  916.  
  917. // 屏蔽关键词
  918. {
  919. const tc = document.createElement("div");
  920.  
  921. tc.innerHTML += `
  922. <div>过滤关键词,用"|"隔开</div>
  923. <div>
  924. <input value="${data.options.keyword}"/>
  925. <button>确认</button>
  926. </div>
  927. `;
  928.  
  929. const actions = tc.getElementsByTagName("button");
  930.  
  931. actions[0].onclick = () => {
  932. const v = actions[0].previousElementSibling.value;
  933.  
  934. data.options.keyword = v;
  935.  
  936. saveData();
  937. reFilter();
  938. };
  939.  
  940. container.appendChild(tc);
  941. }
  942.  
  943. // 过滤方式
  944. {
  945. const tc = document.createElement("div");
  946.  
  947. tc.innerHTML += `
  948. <br/>
  949. <div>过滤方式</div>
  950. <div>
  951. <input type="radio" name="filterType" ${data.options
  952. .filterMode === 0 && "checked"}>
  953. <span>隐藏</span>
  954. <input type="radio" name="filterType" ${data.options
  955. .filterMode === 1 && "checked"}>
  956. <span>标记</span>
  957. <button>确认</button>
  958. </div>
  959. `;
  960.  
  961. const actions = tc.getElementsByTagName("button");
  962.  
  963. actions[0].onclick = () => {
  964. const values = document.getElementsByName("filterType");
  965.  
  966. for (let i = 0, length = values.length; i < length; i++) {
  967. if (values[i].checked) {
  968. data.options.filterMode = i;
  969. break;
  970. }
  971. }
  972.  
  973. saveData();
  974. reFilter();
  975. };
  976.  
  977. container.appendChild(tc);
  978. }
  979.  
  980. // 删除没有标记的用户
  981. {
  982. const tc = document.createElement("div");
  983.  
  984. tc.innerHTML += `
  985. <br/>
  986. <div>
  987. <button>删除没有标记的用户</button>
  988. </div>
  989. `;
  990.  
  991. const actions = tc.getElementsByTagName("button");
  992.  
  993. actions[0].onclick = () => {
  994. if (confirm("是否确认?")) {
  995. Object.values(data.users).forEach(item => {
  996. if (item.tags.length === 0) {
  997. delete data.users[item.id];
  998. }
  999. });
  1000.  
  1001. saveData();
  1002. reFilter();
  1003. }
  1004. };
  1005.  
  1006. container.appendChild(tc);
  1007. }
  1008.  
  1009. // 删除没有用户的标记
  1010. {
  1011. const tc = document.createElement("div");
  1012.  
  1013. tc.innerHTML += `
  1014. <br/>
  1015. <div>
  1016. <button>删除没有用户的标记</button>
  1017. </div>
  1018. `;
  1019.  
  1020. const actions = tc.getElementsByTagName("button");
  1021.  
  1022. actions[0].onclick = () => {
  1023. if (confirm("是否确认?")) {
  1024. Object.values(data.tags).forEach(item => {
  1025. if (
  1026. Object.values(data.users).filter(user =>
  1027. user.tags.find(tag => tag === item.id)
  1028. ).length === 0
  1029. ) {
  1030. delete data.tags[item.id];
  1031. }
  1032. });
  1033.  
  1034. saveData();
  1035. reFilter();
  1036. }
  1037. };
  1038.  
  1039. container.appendChild(tc);
  1040. }
  1041. };
  1042.  
  1043. return func;
  1044. })();
  1045.  
  1046. return {
  1047. name: "通用设置",
  1048. content,
  1049. refresh
  1050. };
  1051. })();
  1052.  
  1053. u.addModule(blockModule).toggle();
  1054. u.addModule(tagModule);
  1055. u.addModule(commonModule);
  1056.  
  1057. // 增加菜单项
  1058. (() => {
  1059. const title = "屏蔽/标记";
  1060. let window;
  1061.  
  1062. n.mainMenu.addItemOnTheFly(title, null, () => {
  1063. if (window === undefined) {
  1064. window = n.createCommmonWindow();
  1065. }
  1066.  
  1067. window._.addContent(null);
  1068. window._.addTitle(title);
  1069. window._.addContent(u.content);
  1070. window._.show();
  1071. });
  1072. })();
  1073. })(commonui);