NGA Watcher

同步客户端关注功能

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

  1. // ==UserScript==
  2. // @name NGA Watcher
  3. // @namespace https://gf.qytechs.cn/users/263018
  4. // @version 1.1.0
  5. // @author snyssss
  6. // @description 同步客户端关注功能
  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. // @grant GM_addValueChangeListener
  16. // @grant GM_registerMenuCommand
  17.  
  18. // @noframes
  19. // ==/UserScript==
  20.  
  21. ((ui, self) => {
  22. if (!ui) return;
  23.  
  24. // 钩子
  25. const hookFunction = (object, functionName, callback) => {
  26. ((originalFunction) => {
  27. object[functionName] = function () {
  28. const returnValue = originalFunction.apply(this, arguments);
  29.  
  30. callback.apply(this, [returnValue, originalFunction, arguments]);
  31.  
  32. return returnValue;
  33. };
  34. })(object[functionName]);
  35. };
  36.  
  37. // STYLE
  38. GM_addStyle(`
  39. .s-user-info-container:not(:hover) .ah {
  40. display: none !important;
  41. }
  42. .s-table {
  43. border: 1px solid #ead5bc;
  44. border-left: none;
  45. border-bottom: none;
  46. width: 99.95%;
  47. }
  48. .s-table thead {
  49. background-color: #591804;
  50. color: #fff8e7;
  51. }
  52. .s-table tbody tr {
  53. background-color: #fff0cd;
  54. }
  55. .s-table tbody tr:nth-of-type(odd) {
  56. background-color: #fff8e7;
  57. }
  58. .s-table td {
  59. border: 1px solid #ead5bc;
  60. border-top: none;
  61. border-right: none;
  62. padding: 6px;
  63. white-space: nowrap;
  64. }
  65. .s-table input:not([type]) {
  66. margin: 0;
  67. width: 100%;
  68. box-sizing: border-box;
  69. }
  70. `);
  71.  
  72. // 用户信息
  73. class UserInfo {
  74. execute(task) {
  75. task().finally(() => {
  76. if (this.waitingQueue.length) {
  77. const next = this.waitingQueue.shift();
  78.  
  79. this.execute(next);
  80. } else {
  81. this.isRunning = false;
  82. }
  83. });
  84. }
  85.  
  86. enqueue(task) {
  87. if (this.isRunning) {
  88. this.waitingQueue.push(task);
  89. } else {
  90. this.isRunning = true;
  91.  
  92. this.execute(task);
  93. }
  94. }
  95.  
  96. rearrange() {
  97. if (this.data) {
  98. const list = Object.values(this.children);
  99.  
  100. for (let i = 0; i < list.length; i++) {
  101. if (list[i].source === undefined) {
  102. list[i].create(this.data);
  103. }
  104.  
  105. Object.entries(this.container).forEach((item) => {
  106. list[i].clone(this.data, item);
  107. });
  108. }
  109. }
  110. }
  111.  
  112. reload() {
  113. this.enqueue(async () => {
  114. this.data = await new Promise((resolve) => {
  115. fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${this.uid}`)
  116. .then((res) => res.blob())
  117. .then((blob) => {
  118. const reader = new FileReader();
  119.  
  120. reader.onload = () => {
  121. const text = reader.result;
  122. const result = JSON.parse(
  123. text.replace("window.script_muti_get_var_store=", "")
  124. );
  125.  
  126. resolve(result.data[0]);
  127. };
  128.  
  129. reader.readAsText(blob, "GBK");
  130. })
  131. .catch(() => {
  132. resolve();
  133. });
  134. });
  135.  
  136. Object.values(this.children).forEach((item) => item.destroy());
  137.  
  138. this.rearrange();
  139. });
  140. }
  141.  
  142. constructor(id) {
  143. this.uid = id;
  144.  
  145. this.waitingQueue = [];
  146. this.isRunning = false;
  147.  
  148. this.container = {};
  149. this.children = {};
  150.  
  151. this.reload();
  152. }
  153. }
  154.  
  155. // 用户信息组件
  156. class UserInfoWidget {
  157. destroy() {
  158. if (this.source) {
  159. this.source = undefined;
  160. }
  161.  
  162. if (this.target) {
  163. Object.values(this.target).forEach((item) => {
  164. if (item.parentNode) {
  165. item.parentNode.removeChild(item);
  166. }
  167. });
  168. }
  169. }
  170.  
  171. clone(data, [argid, container]) {
  172. if (this.source) {
  173. if (this.target[argid] === undefined) {
  174. this.target[argid] = this.source.cloneNode(true);
  175.  
  176. if (this.callback) {
  177. this.callback(data, this.target[argid]);
  178. }
  179. }
  180.  
  181. container.appendChild(this.target[argid]);
  182. }
  183. }
  184.  
  185. constructor(func, callback) {
  186. this.create = (data) => {
  187. this.destroy();
  188.  
  189. this.source = func(data);
  190. this.target = {};
  191. };
  192.  
  193. this.callback = callback;
  194. }
  195. }
  196.  
  197. ui.sn = ui.sn || {};
  198. ui.sn.userInfo = ui.sn.userInfo || {};
  199.  
  200. ((info) => {
  201. // 关注
  202. const follow = (uid) =>
  203. new Promise((resolve, reject) => {
  204. fetch(
  205. `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=1`,
  206. {
  207. method: "post",
  208. }
  209. )
  210. .then((res) => res.blob())
  211. .then((blob) => {
  212. const reader = new FileReader();
  213.  
  214. reader.onload = () => {
  215. const text = reader.result;
  216. const result = JSON.parse(
  217. text.replace("window.script_muti_get_var_store=", "")
  218. );
  219.  
  220. if (result.data) {
  221. resolve(result.data[0]);
  222. } else {
  223. reject(result.error[0]);
  224. }
  225. };
  226.  
  227. reader.readAsText(blob, "GBK");
  228. })
  229. .catch(() => {
  230. resolve();
  231. });
  232. });
  233.  
  234. // 取消关注
  235. const un_follow = (uid) =>
  236. new Promise((resolve, reject) => {
  237. fetch(
  238. `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=8`,
  239. {
  240. method: "post",
  241. }
  242. )
  243. .then((res) => res.blob())
  244. .then((blob) => {
  245. const reader = new FileReader();
  246.  
  247. reader.onload = () => {
  248. const text = reader.result;
  249. const result = JSON.parse(
  250. text.replace("window.script_muti_get_var_store=", "")
  251. );
  252.  
  253. if (result.data) {
  254. resolve(result.data[0]);
  255. } else {
  256. reject(result.error[0]);
  257. }
  258. };
  259.  
  260. reader.readAsText(blob, "GBK");
  261. })
  262. .catch(() => {
  263. resolve();
  264. });
  265. });
  266.  
  267. // 移除粉丝
  268. const un_follow_fans = (uid) =>
  269. new Promise((resolve, reject) => {
  270. fetch(
  271. `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=256`,
  272. {
  273. method: "post",
  274. }
  275. )
  276. .then((res) => res.blob())
  277. .then((blob) => {
  278. const reader = new FileReader();
  279.  
  280. reader.onload = () => {
  281. const text = reader.result;
  282. const result = JSON.parse(
  283. text.replace("window.script_muti_get_var_store=", "")
  284. );
  285.  
  286. if (result.data) {
  287. resolve(result.data[0]);
  288. } else {
  289. reject(result.error[0]);
  290. }
  291. };
  292.  
  293. reader.readAsText(blob, "GBK");
  294. })
  295. .catch(() => {
  296. resolve();
  297. });
  298. });
  299.  
  300. // 获取关注列表
  301. const follow_list = (page) =>
  302. new Promise((resolve, reject) => {
  303. fetch(
  304. `/nuke.php?lite=js&__lib=follow_v2&__act=get_follow&page=${page}`,
  305. {
  306. method: "post",
  307. }
  308. )
  309. .then((res) => res.blob())
  310. .then((blob) => {
  311. const reader = new FileReader();
  312.  
  313. reader.onload = () => {
  314. const text = reader.result;
  315. const result = JSON.parse(
  316. text.replace("window.script_muti_get_var_store=", "")
  317. );
  318.  
  319. if (result.data) {
  320. resolve(result.data[0]);
  321. } else {
  322. reject(result.error[0]);
  323. }
  324. };
  325.  
  326. reader.readAsText(blob, "GBK");
  327. })
  328. .catch(() => {
  329. resolve();
  330. });
  331. });
  332.  
  333. // 获取粉丝列表
  334. const follow_by_list = (page) =>
  335. new Promise((resolve, reject) => {
  336. fetch(
  337. `/nuke.php?lite=js&__lib=follow_v2&__act=get_follow_by&page=${page}`,
  338. {
  339. method: "post",
  340. }
  341. )
  342. .then((res) => res.blob())
  343. .then((blob) => {
  344. const reader = new FileReader();
  345.  
  346. reader.onload = () => {
  347. const text = reader.result;
  348. const result = JSON.parse(
  349. text.replace("window.script_muti_get_var_store=", "")
  350. );
  351.  
  352. if (result.data) {
  353. resolve(result.data[0]);
  354. } else {
  355. reject(result.error[0]);
  356. }
  357. };
  358.  
  359. reader.readAsText(blob, "GBK");
  360. })
  361. .catch(() => {
  362. resolve();
  363. });
  364. });
  365.  
  366. // 获取关注动态
  367. const follow_dymanic_list = () =>
  368. new Promise((resolve, reject) => {
  369. fetch(`/nuke.php?lite=js&__lib=follow_v2&__act=get_push_list`, {
  370. method: "post",
  371. })
  372. .then((res) => res.blob())
  373. .then((blob) => {
  374. const reader = new FileReader();
  375.  
  376. reader.onload = () => {
  377. const text = reader.result;
  378. const result = JSON.parse(
  379. text.replace("window.script_muti_get_var_store=", "")
  380. );
  381.  
  382. if (result.data) {
  383. resolve(result.data);
  384. } else {
  385. reject(result.error[0]);
  386. }
  387. };
  388.  
  389. reader.readAsText(blob, "GBK");
  390. })
  391. .catch(() => {
  392. resolve();
  393. });
  394. });
  395.  
  396. // UI
  397. const u = (() => {
  398. const modules = {};
  399.  
  400. const createView = () => {
  401. const tabContainer = (() => {
  402. const c = document.createElement("div");
  403.  
  404. c.className = "w100";
  405. c.innerHTML = `
  406. <div class="right_" style="margin-bottom: 5px;">
  407. <table class="stdbtn" cellspacing="0">
  408. <tbody>
  409. <tr></tr>
  410. </tbody>
  411. </table>
  412. </div>
  413. <div class="clear"></div>
  414. `;
  415.  
  416. return c;
  417. })();
  418.  
  419. const tabPanelContainer = (() => {
  420. const c = document.createElement("div");
  421.  
  422. c.style = "width: 40vw;";
  423.  
  424. return c;
  425. })();
  426.  
  427. const content = (() => {
  428. const c = document.createElement("div");
  429.  
  430. c.append(tabContainer);
  431. c.append(tabPanelContainer);
  432.  
  433. return c;
  434. })();
  435.  
  436. const addModule = (() => {
  437. const tc = tabContainer.getElementsByTagName("tr")[0];
  438. const cc = tabPanelContainer;
  439.  
  440. return (module) => {
  441. const tabBox = document.createElement("td");
  442.  
  443. tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
  444.  
  445. const tab = tabBox.childNodes[0];
  446.  
  447. const toggle = () => {
  448. Object.values(modules).forEach((item) => {
  449. if (item.tab === tab) {
  450. item.tab.className = "nobr";
  451. item.content.style = "display: block";
  452. item.visible = true;
  453. } else {
  454. item.tab.className = "nobr silver";
  455. item.content.style = "display: none";
  456. item.visible = false;
  457. }
  458. });
  459.  
  460. module.refresh();
  461. };
  462.  
  463. tc.append(tabBox);
  464. cc.append(module.content);
  465.  
  466. tab.onclick = toggle;
  467.  
  468. modules[module.name] = {
  469. ...module,
  470. tab,
  471. toggle,
  472. visible: false,
  473. };
  474.  
  475. return modules[module.name];
  476. };
  477. })();
  478.  
  479. return {
  480. content,
  481. modules,
  482. addModule,
  483. };
  484. };
  485.  
  486. const refresh = () => {
  487. Object.values(modules)
  488. .find((item) => item.visible)
  489. ?.refresh();
  490. };
  491.  
  492. return {
  493. createView,
  494. refresh,
  495. };
  496. })();
  497.  
  498. // 我的关注
  499. {
  500. const name = "我的关注";
  501.  
  502. const content = (() => {
  503. const c = document.createElement("div");
  504.  
  505. c.style.display = "none";
  506. c.innerHTML = `
  507. <div style="max-height: 400px; overflow: auto;">
  508. <table class="s-table">
  509. <tbody></tbody>
  510. </table>
  511. </div>
  512. `;
  513.  
  514. return c;
  515. })();
  516.  
  517. let page = 0;
  518. let lastSize = -1;
  519. let isFetching = false;
  520.  
  521. const box = content.querySelector("DIV");
  522.  
  523. const list = content.querySelector("TBODY");
  524.  
  525. const fetchData = () => {
  526. isFetching = true;
  527.  
  528. follow_list(page)
  529. .then((res) => {
  530. lastSize = Object.keys(res).length;
  531.  
  532. for (let i in res) {
  533. const { uid, username } = res[i];
  534.  
  535. const name = `s-follow-${uid}`;
  536.  
  537. if (list.querySelector(`#${name}`)) {
  538. continue;
  539. }
  540.  
  541. const item = document.createElement("TR");
  542.  
  543. item.id = name;
  544. item.innerHTML = `
  545. <td>
  546. <a href="/nuke.php?func=ucp&uid=${uid}" class="b nobr">[@${username}]</a>
  547. </td>
  548. <td width="1">
  549. <button>移除</button>
  550. </td>
  551. `;
  552.  
  553. const action = item.querySelector("BUTTON");
  554.  
  555. action.onclick = () => {
  556. if (confirm("取消关注?")) {
  557. un_follow(uid).then(() => {
  558. info[uid].reload();
  559. u.refresh();
  560. });
  561. }
  562. };
  563.  
  564. list.appendChild(item);
  565. }
  566. })
  567. .finally(() => {
  568. isFetching = false;
  569. });
  570. };
  571.  
  572. box.onscroll = () => {
  573. if (isFetching || lastSize === 0) {
  574. return;
  575. }
  576.  
  577. if (box.scrollHeight - box.scrollTop - box.clientHeight <= 40) {
  578. page = page + 1;
  579.  
  580. fetchData();
  581. }
  582. };
  583.  
  584. const refresh = () => {
  585. list.innerHTML = "";
  586.  
  587. page = 1;
  588. lastSize = -1;
  589.  
  590. fetchData();
  591. };
  592.  
  593. hookFunction(u, "createView", (view) => {
  594. view.addModule({
  595. name,
  596. content,
  597. refresh,
  598. });
  599. });
  600. }
  601.  
  602. // 我的粉丝
  603. {
  604. const name = "我的粉丝";
  605.  
  606. const content = (() => {
  607. const c = document.createElement("div");
  608.  
  609. c.style.display = "none";
  610. c.innerHTML = `
  611. <div style="max-height: 400px; overflow: auto;">
  612. <table class="s-table">
  613. <tbody></tbody>
  614. </table>
  615. </div>
  616. `;
  617.  
  618. return c;
  619. })();
  620.  
  621. let page = 0;
  622. let lastSize = -1;
  623. let isFetching = false;
  624.  
  625. const box = content.querySelector("DIV");
  626.  
  627. const list = content.querySelector("TBODY");
  628.  
  629. const fetchData = () => {
  630. isFetching = true;
  631.  
  632. follow_by_list(page)
  633. .then((res) => {
  634. lastSize = Object.keys(res).length;
  635.  
  636. for (let i in res) {
  637. const { uid, username } = res[i];
  638.  
  639. const name = `s-fans-${uid}`;
  640.  
  641. if (list.querySelector(`#${name}`)) {
  642. continue;
  643. }
  644.  
  645. const item = document.createElement("TR");
  646.  
  647. item.id = name;
  648. item.innerHTML = `
  649. <td>
  650. <a href="/nuke.php?func=ucp&uid=${uid}" class="b nobr">[@${username}]</a>
  651. </td>
  652. <td width="1">
  653. <button>移除</button>
  654. </td>
  655. `;
  656.  
  657. const action = item.querySelector("BUTTON");
  658.  
  659. action.onclick = () => {
  660. if (confirm("移除粉丝?")) {
  661. un_follow_fans(uid).then(() => {
  662. u.refresh();
  663. });
  664. }
  665. };
  666.  
  667. list.appendChild(item);
  668. }
  669. })
  670. .finally(() => {
  671. isFetching = false;
  672. });
  673. };
  674.  
  675. box.onscroll = () => {
  676. if (isFetching || lastSize === 0) {
  677. return;
  678. }
  679.  
  680. if (box.scrollHeight - box.scrollTop - box.clientHeight <= 40) {
  681. page = page + 1;
  682.  
  683. fetchData();
  684. }
  685. };
  686.  
  687. const refresh = () => {
  688. list.innerHTML = "";
  689.  
  690. page = 1;
  691. lastSize = -1;
  692.  
  693. fetchData();
  694. };
  695.  
  696. hookFunction(u, "createView", (view) => {
  697. view.addModule({
  698. name,
  699. content,
  700. refresh,
  701. });
  702. });
  703. }
  704.  
  705. // 关注动态
  706. {
  707. const name = "关注动态";
  708.  
  709. const content = (() => {
  710. const c = document.createElement("div");
  711.  
  712. c.style.display = "none";
  713. c.innerHTML = `
  714. <div style="max-height: 400px; overflow: auto;">
  715. <table class="s-table">
  716. <tbody></tbody>
  717. </table>
  718. </div>
  719. `;
  720.  
  721. return c;
  722. })();
  723.  
  724. let page = 0;
  725. let lastSize = -1;
  726. let isFetching = false;
  727.  
  728. const box = content.querySelector("DIV");
  729.  
  730. const list = content.querySelector("TBODY");
  731.  
  732. const fetchData = () => {
  733. isFetching = true;
  734.  
  735. follow_dymanic_list(page)
  736. .then((res) => {
  737. if (res[1] === res[2]) {
  738. lastSize = 0;
  739. } else {
  740. lastSize = -1;
  741. }
  742.  
  743. return res[0];
  744. })
  745. .then((res) => {
  746. for (let i in res) {
  747. const id = res[i][0];
  748. const time = res[i][6];
  749. const summary = res[i]["summary"];
  750.  
  751. const name = `s-follow-dymanic-${id}`;
  752.  
  753. if (list.querySelector(`#${name}`)) {
  754. continue;
  755. }
  756.  
  757. const parsedSummary = summary
  758. .replace(
  759. /\[uid=(\d+)\](.+)\[\/uid\]/,
  760. `<a href="/nuke.php?func=ucp&uid=$1" class="b nobr">$2</a>`
  761. )
  762. .replace(
  763. /\[pid=(\d+)\](.+)\[\/pid\]/,
  764. `<a href="/read.php?pid=$1" class="b nobr">回复</a>`
  765. )
  766. .replace(/\[tid=(\d+)\](.+)\[\/tid\]/, function ($0, $1, $2) {
  767. let s = ui.cutstrbylen($2, 19);
  768. if (s.length < $2.length) {
  769. s += "...";
  770. }
  771.  
  772. return `<a href="/read.php?tid=${$1}" class="b nobr">${s}</a>`;
  773. });
  774.  
  775. const item = document.createElement("TR");
  776.  
  777. item.id = name;
  778. item.innerHTML = `
  779. <td width="100">
  780. ${ui.time2dis(time)}
  781. </td>
  782. <td>
  783. ${parsedSummary}
  784. </td>
  785. `;
  786.  
  787. list.appendChild(item);
  788. }
  789. })
  790. .finally(() => {
  791. isFetching = false;
  792. });
  793. };
  794.  
  795. box.onscroll = () => {
  796. if (isFetching || lastSize === 0) {
  797. return;
  798. }
  799.  
  800. if (box.scrollHeight - box.scrollTop - box.clientHeight <= 40) {
  801. page = page + 1;
  802.  
  803. fetchData();
  804. }
  805. };
  806.  
  807. const refresh = () => {
  808. list.innerHTML = "";
  809.  
  810. page = 1;
  811. lastSize = -1;
  812.  
  813. fetchData();
  814. };
  815.  
  816. hookFunction(u, "createView", (view) => {
  817. view.addModule({
  818. name,
  819. content,
  820. refresh,
  821. });
  822. });
  823. }
  824.  
  825. // 打开菜单
  826. const showMenu = (() => {
  827. let view, window;
  828.  
  829. return () => {
  830. if (view === undefined) {
  831. view = u.createView();
  832. }
  833.  
  834. view.modules["关注动态"].toggle();
  835.  
  836. if (window === undefined) {
  837. window = ui.createCommmonWindow();
  838. }
  839.  
  840. window._.addContent(null);
  841. window._.addTitle(`关注`);
  842. window._.addContent(view.content);
  843. window._.show();
  844. };
  845. })();
  846.  
  847. // 增加菜单项
  848. if (document.querySelector(`[name="unisearchinput"]`)) {
  849. const anchor = document.querySelector("#mainmenu .td:last-child");
  850.  
  851. const button = document.createElement("DIV");
  852.  
  853. button.className = `td`;
  854. button.innerHTML = `<a class="mmdefault" href="javascript: void(0);" style="white-space: nowrap;">关注</a>`;
  855.  
  856. button.onclick = showMenu;
  857.  
  858. anchor.before(button);
  859. }
  860.  
  861. let popover;
  862.  
  863. const execute = (argid) => {
  864. const args = ui.postArg.data[argid];
  865.  
  866. if (args.comment) return;
  867.  
  868. const uid = +args.pAid;
  869.  
  870. if (uid > 0) {
  871. if (info[uid] === undefined) {
  872. info[uid] = new UserInfo(uid);
  873. }
  874.  
  875. if (document.contains(info[uid].container[argid]) === false) {
  876. info[uid].container[argid] = args.uInfoC.querySelector(
  877. "[name=uid]"
  878. ).parentNode;
  879. }
  880.  
  881. info[uid].enqueue(async () => {
  882. args.uInfoC.className =
  883. args.uInfoC.className + " s-user-info-container";
  884.  
  885. if (info[uid].children[16]) {
  886. info[uid].children[16].destroy();
  887. }
  888.  
  889. info[uid].children[16] = new UserInfoWidget(
  890. (data) => {
  891. const value = data.follow_by_num || 0;
  892.  
  893. const element = document.createElement("SPAN");
  894.  
  895. if (uid === self || data.follow) {
  896. element.className =
  897. "small_colored_text_btn stxt block_txt_c2 vertmod";
  898. } else {
  899. element.className =
  900. "small_colored_text_btn stxt block_txt_c2 vertmod ah";
  901. }
  902.  
  903. element.style.cursor = "default";
  904. element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">★</span>&nbsp;${value}</span>`;
  905.  
  906. element.style.cursor = "pointer";
  907.  
  908. return element;
  909. },
  910. (data, element) => {
  911. if (!self) return;
  912.  
  913. const handleClose = () => {
  914. if (popover) {
  915. popover.style.display = "none";
  916. }
  917. };
  918.  
  919. const handleSwitchFollow = () => {
  920. if (data.follow) {
  921. if (confirm("取消关注?")) {
  922. un_follow(data.uid).then(() => {
  923. info[uid].reload();
  924. u.refresh();
  925. });
  926. }
  927. } else {
  928. follow(data.uid).then(() => {
  929. info[uid].reload();
  930. u.refresh();
  931. });
  932. }
  933.  
  934. handleClose();
  935. };
  936.  
  937. element.onclick = (e) => {
  938. if (uid === self) {
  939. showMenu();
  940. return;
  941. }
  942.  
  943. if (!popover) {
  944. popover = document.createElement("SPAN");
  945.  
  946. popover.className = "urltip2 urltip3 ah";
  947. popover.style = "textAlign: left; margin: 0;";
  948. }
  949.  
  950. if (element.parentNode !== popover.parentNode) {
  951. element.parentNode.appendChild(popover);
  952. }
  953.  
  954. if (data.follow) {
  955. if (popover.type !== 1) {
  956. popover.type = 1;
  957. popover.innerHTML = `<nobr>
  958. <a href="javascript: void(0);">[已关注]</a>
  959. <a href="javascript: void(0);">[关闭]</a>
  960. </nobr>`;
  961.  
  962. const buttons = popover.getElementsByTagName("A");
  963.  
  964. buttons[0].onclick = handleSwitchFollow;
  965. buttons[1].onclick = handleClose;
  966. }
  967. } else {
  968. if (popover.type !== 2) {
  969. popover.type = 2;
  970. popover.innerHTML = `<nobr>
  971. <a href="javascript: void(0);">[关注]</a>
  972. <a href="javascript: void(0);">[关闭]</a>
  973. </nobr>`;
  974.  
  975. const buttons = popover.getElementsByTagName("A");
  976.  
  977. buttons[0].onclick = handleSwitchFollow;
  978. buttons[1].onclick = handleClose;
  979. }
  980. }
  981.  
  982. popover.style.left = `${e.pageX}px`;
  983. popover.style.top = `${e.pageY}px`;
  984. popover.style.display = "block";
  985. };
  986. }
  987. );
  988.  
  989. info[uid].rearrange();
  990. });
  991. }
  992. };
  993.  
  994. if (ui.postArg) {
  995. Object.keys(ui.postArg.data).forEach((i) => execute(i));
  996. }
  997.  
  998. let initialized = false;
  999.  
  1000. hookFunction(ui, "eval", () => {
  1001. if (initialized) return;
  1002.  
  1003. if (ui.postDisp) {
  1004. hookFunction(
  1005. ui,
  1006. "postDisp",
  1007. (returnValue, originalFunction, arguments) => execute(arguments[0])
  1008. );
  1009.  
  1010. initialized = true;
  1011. }
  1012. });
  1013. })(ui.sn.userInfo);
  1014. })(commonui, __CURRENT_UID);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址