Twitter - 为用户添加备注(别名/标签)

为用户添加备注(别名/标签)功能,以帮助识别和搜索,并支持 WebDAV 同步功能

目前为 2023-11-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Twitter - Add notes to the user
  3. // @name:zh-CN Twitter - 为用户添加备注(别名/标签)
  4. // @name:zh-TW Twitter - 為使用者新增備註(別名/標籤)
  5. // @namespace https://greasyfork.org/zh-CN/users/193133-pana
  6. // @homepage https://greasyfork.org/zh-CN/users/193133-pana
  7. // @icon data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+
  8. // @version 6.1.8
  9. // @description Add notes (aliases/tags) for users to help identify and search, and support WebDAV sync
  10. // @description:zh-CN 为用户添加备注(别名/标签)功能,以帮助识别和搜索,并支持 WebDAV 同步功能
  11. // @description:zh-TW 為使用者新增備註(別名/標籤)功能,以幫助識別和搜尋,並支援 WebDAV 同步功能
  12. // @author pana
  13. // @license GNU General Public License v3.0 or later
  14. // @compatible chrome
  15. // @compatible firefox
  16. // @match *://*twitter.com/*
  17. // @require https://gcore.jsdelivr.net/gh/LightAPIs/greasy-fork-library@a25cf92d6d3660cdac6e84c5c1cd0ab027bf6611/Note_Obj.js
  18. // @connect *
  19. // @noframes
  20. // @grant GM_info
  21. // @grant GM_getValue
  22. // @grant GM_setValue
  23. // @grant GM_deleteValue
  24. // @grant GM_listValues
  25. // @grant GM_openInTab
  26. // @grant GM_addStyle
  27. // @grant GM_xmlhttpRequest
  28. // @grant GM_registerMenuCommand
  29. // @grant GM_unregisterMenuCommand
  30. // @grant GM_addValueChangeListener
  31. // @grant GM_removeValueChangeListener
  32. // ==/UserScript==
  33.  
  34. (function () {
  35. 'use strict';
  36. const UPDATED = '2023-11-23';
  37. const TWITTER_ICON = {
  38. NOTE_GRAY: 'url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2IoMTAxLCAxMTksIDEzNCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYigxMDEsIDExOSwgMTM0KSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)',
  39. NOTE_BLUE: 'url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)'
  40. };
  41. const selector = {
  42. root: '#react-root',
  43. homepage: {
  44. id: 'div[data-testid="User-Name"] a[role="link"] > div[dir] > span',
  45. article: 'article',
  46. toolBar: '[tabindex="0"]:scope [role="group"][id]',
  47. showName: 'div[data-testid="User-Name"] a[role="link"] > div > div[dir] > span',
  48. reprintA: 'a[role][dir][id]',
  49. reprintName: '[data-testid="socialContext"] [dir]',
  50. at: '[data-testid="tweetText"] a[dir][role="link"]',
  51. blockquote: 'div[aria-labelledby][id] div[id] div[role="link"]',
  52. blockquoteId: 'div[data-testid="User-Name"] div[tabindex] div[dir]',
  53. blockquoteShowName: 'div[data-testid="User-Name"] div[dir]'
  54. },
  55. userpage: {
  56. main: '.css-175oi2r.r-ymttw5.r-ttdzmv.r-1ifxtd0',
  57. id: '[data-testid="UserName"] div[tabindex] div[dir] > span',
  58. showName: '[data-testid="UserName"] div[dir] > span',
  59. follow: '.css-175oi2r.r-obd0qt.r-18u37iz.r-1w6e6rj.r-1h0z5md.r-dnmrzs'
  60. },
  61. comment: {
  62. toolBar: '[tabindex="-1"]:scope [role="group"][id]'
  63. },
  64. hover: {
  65. panel: 'div[data-testid="HoverCard"] > div > div',
  66. userAvatar: '[data-testid^="UserAvatar-Container-"]',
  67. id: 'a[role="link"]',
  68. showName: 'a[role="link"] > div > [dir] > span'
  69. },
  70. modal: {
  71. cell: '[aria-labelledby="modal-header"] [data-testid="UserCell"]',
  72. id: 'a[role="link"]',
  73. showName: 'a[role="link"] > div > [dir] > span'
  74. },
  75. follow: {
  76. cell: '[data-testid="cellInnerDiv"] [data-testid="UserCell"]',
  77. id: 'a[role="link"]',
  78. showName: 'a[role="link"] > div > [dir] > span'
  79. },
  80. rightRecommended: {
  81. cell: '[role="complementary"] [data-testid="UserCell"]',
  82. id: 'a[role="link"]',
  83. showName: 'a[role="link"] > div > [dir]'
  84. }
  85. };
  86. const nameSet = {
  87. blueTag: 'note-obj-twitter-blue-tag',
  88. noteBtn: 'note-obj-twitter-note-btn',
  89. panelBtn: 'note-obj-twitter-panel-btn',
  90. beforeFollowNoteBtn: 'note-obj-twitter-before-follow-note-btn',
  91. baseToolBarBtn: 'note-obj-twitter-base-tool-bar-btn',
  92. commentToolBarBtn: 'note-obj-twitter-comment-tool-bar-btn'
  93. };
  94. const style = `
  95. .${nameSet.blueTag} {
  96. background-color: #3c81df;
  97. color: #fff;
  98. display: inline-flex;
  99. align-items: center;
  100. padding: 2px 10px;
  101. line-height: 100%;
  102. border-radius: 50px;
  103. }
  104. .${nameSet.noteBtn} {
  105. background-image: ${TWITTER_ICON.NOTE_GRAY};
  106. background-repeat: no-repeat;
  107. background-position: center;
  108. background-color: rgba(0, 0, 0, 0);
  109. border-bottom-left-radius: 9999px;
  110. border-bottom-right-radius: 9999px;
  111. border-top-left-radius: 9999px;
  112. border-top-right-radius: 9999px;
  113. transition-property: background-color, box-shadow;
  114. transition-duration: 0.2s;
  115. }
  116. .${nameSet.noteBtn}:hover {
  117. background-image: ${TWITTER_ICON.NOTE_BLUE};
  118. background-color: rgba(29, 161, 242, .1);
  119. }
  120. .${nameSet.panelBtn} {
  121. height: 32px;
  122. width: 32px;
  123. margin: 5px 0px 0px 0px;
  124. background-size: 28px auto;
  125. cursor: pointer !important;
  126. border-radius: 0px;
  127. }
  128. .${nameSet.panelBtn}:hover::after {
  129. content: "";
  130. display: flex;
  131. position: relative;
  132. background-color: rgba(29, 161, 242, .1);
  133. width: 48px;
  134. height: 48px;
  135. top: -8px;
  136. left: -8px;
  137. border-radius: 99px;
  138. }
  139. .${nameSet.beforeFollowNoteBtn} {
  140. height: 36px;
  141. width: 36px;
  142. background-image: ${TWITTER_ICON.NOTE_BLUE};
  143. background-repeat: no-repeat;
  144. background-size: 19px auto;
  145. background-position: center;
  146. margin-bottom: 12px;
  147. margin-right: 12px;
  148. cursor: pointer;
  149. border: 1px solid rgba(29, 161, 242, 1);
  150. border-bottom-left-radius: 9999px;
  151. border-bottom-right-radius: 9999px;
  152. border-top-left-radius: 9999px;
  153. border-top-right-radius: 9999px;
  154. background-color: rgba(0, 0, 0, 0);
  155. transition-property: background-color, box-shadow;
  156. transition-duration: 0.2s;
  157. }
  158. .${nameSet.beforeFollowNoteBtn}:hover {
  159. background-color: rgba(29, 161, 242, .1);
  160. }
  161. .${nameSet.baseToolBarBtn} {
  162. height: 18px;
  163. width: 18px;
  164. margin: 0px -40px 0px 0px;
  165. background-size: 20px auto;
  166. border-radius: 0px;
  167. margin: 0 12px;
  168. }
  169. .${nameSet.baseToolBarBtn}:hover::after {
  170. content: "";
  171. position: absolute;
  172. background-color: rgba(29, 161, 242, .1);
  173. width: 34px;
  174. height: 34px;
  175. top: -8px;
  176. right: 27px;
  177. border-radius: 99px;
  178. }
  179. .${nameSet.commentToolBarBtn} {
  180. height: 24px;
  181. width: 24px;
  182. margin: 9px 0px 0px 0px;
  183. background-size: 24px auto;
  184. border-radius: 0px;
  185. cursor: pointer;
  186. margin-left: 12px;
  187. }
  188. .${nameSet.commentToolBarBtn}:hover::after {
  189. content: "";
  190. position: absolute;
  191. background-color: rgba(29, 161, 242, .1);
  192. width: 38px;
  193. height: 38px;
  194. top: 2px;
  195. right: -2px;
  196. border-radius: 99px;
  197. }
  198. ${selector.homepage.showName}, ${selector.modal.showName} {
  199. white-space: normal;
  200. }`;
  201. const noteObj = new Note_Obj({
  202. id: 'myTwitterNote',
  203. script: {
  204. author: {
  205. name: 'pana',
  206. homepage: 'https://greasyfork.org/zh-CN/users/193133-pana'
  207. },
  208. url: 'https://greasyfork.org/scripts/404587',
  209. updated: UPDATED
  210. },
  211. style,
  212. changeEvent: changeEvent,
  213. settings: {
  214. showToolbarButton: {
  215. type: 'checkbox',
  216. lang: {
  217. en: 'Display the "Note" button in the toolbar below each tweet (if there is no such button in the user\'s hover information panel, this option can be turned on)',
  218. zhHans: '在每条推特下方的工具栏里显示"备注"按钮 (如果在用户的悬停信息面板里没有此按钮时,可以打开此选项)',
  219. zhHant: '在每條推特下方的工具欄裡顯示"備註"按鈕 (如果在使用者的懸停資訊面板裡沒有此按鈕時,可以開啟此選項)'
  220. },
  221. default: false,
  222. event: insertToolbarButtonEvent
  223. },
  224. disableInTweets: {
  225. type: 'checkbox',
  226. lang: {
  227. en: 'Disable replacing @user with @note in tweets',
  228. zhHans: '禁用将推文中的 @user 替换为 @note',
  229. zhHant: '禁用將推文中的 @user 替換為 @note'
  230. },
  231. default: false,
  232. event: disableInTweetsEvent
  233. }
  234. }
  235. });
  236. function atFilter(text) {
  237. return text.replace(/^@/, '');
  238. }
  239. function hrefComparator(href) {
  240. return /^\/[^/]+$/i.test(href);
  241. }
  242. function toolBarNoteButton(ele, state) {
  243. const eleId = noteObj.fn.getText(ele, selector.homepage.id, 'error', atFilter);
  244. if (eleId) {
  245. const eleName = noteObj.fn.getText(ele, selector.homepage.showName, 'info');
  246. const homepageToolBar = noteObj.fn.query(ele, selector.homepage.toolBar, 'info');
  247. const commentToolBar = noteObj.fn.query(ele, selector.comment.toolBar, 'info');
  248. if (homepageToolBar) {
  249. const homepageToolBarBtn = noteObj.fn.query(homepageToolBar, '.' + Note_Obj.btnClassName, 'none');
  250. if (state) {
  251. !homepageToolBarBtn && homepageToolBar.appendChild(noteObj.createNoteBtn(eleId, eleName, [nameSet.noteBtn, nameSet.baseToolBarBtn]));
  252. } else {
  253. homepageToolBarBtn && homepageToolBarBtn.remove();
  254. }
  255. }
  256. if (commentToolBar) {
  257. const commentToolBarBtn = noteObj.fn.query(commentToolBar, '.' + Note_Obj.btnClassName, 'none');
  258. if (state) {
  259. !commentToolBarBtn && commentToolBar.appendChild(noteObj.createNoteBtn(eleId, eleName, [nameSet.noteBtn, nameSet.commentToolBarBtn]));
  260. } else {
  261. commentToolBarBtn && commentToolBarBtn.remove();
  262. }
  263. }
  264. }
  265. }
  266. function homepageNote(ele, changeId) {
  267. const eleId = noteObj.fn.getText(ele, selector.homepage.id, 'error', atFilter);
  268. if (eleId) {
  269. if (changeId) {
  270. changeId === eleId && noteObj.handler(eleId, ele, selector.homepage.showName, {
  271. add: 'span',
  272. className: [nameSet.blueTag]
  273. });
  274. } else {
  275. const eleName = noteObj.fn.getText(ele, selector.homepage.showName, 'info');
  276. noteObj.handler(eleId, ele, selector.homepage.showName, {
  277. add: 'span',
  278. className: [nameSet.blueTag]
  279. }, eleName);
  280. }
  281. }
  282. }
  283. function reprintANote(ele, changeId) {
  284. const reprintA = noteObj.fn.queryAnchor(ele, selector.homepage.reprintA, 'info');
  285. if (reprintA) {
  286. const eleId = noteObj.fn.getIdFromUrl(reprintA.href);
  287. if (!changeId || changeId === eleId) {
  288. noteObj.handler(eleId, reprintA, selector.homepage.reprintName, {
  289. add: 'span',
  290. className: [nameSet.blueTag],
  291. offsetWidth: 30
  292. });
  293. }
  294. }
  295. }
  296. function blockquoteNote(ele, changeId) {
  297. const blockquote = noteObj.fn.query(ele, selector.homepage.blockquote, 'info');
  298. if (blockquote) {
  299. const blockquoteUser = noteObj.fn.query(blockquote, selector.homepage.blockquoteShowName);
  300. if (blockquoteUser) {
  301. const eleId = noteObj.fn.getText(blockquote, selector.homepage.blockquoteId, 'error', atFilter);
  302. if (!changeId || changeId === eleId) {
  303. noteObj.handler(eleId, blockquoteUser, undefined, {
  304. add: 'span',
  305. className: [nameSet.blueTag]
  306. });
  307. }
  308. }
  309. }
  310. }
  311. function homepageAtNote(ele, state, changeId) {
  312. for (const atUser of noteObj.fn.queryAllAnchor(ele, selector.homepage.at, 'info')) {
  313. if (hrefComparator(atUser.getAttribute('href') || '')) {
  314. const atUserId = noteObj.fn.getIdFromUrl(atUser.href);
  315. if (!changeId || changeId === atUserId) {
  316. noteObj.handler(atUserId, atUser, undefined, {
  317. prefix: '@',
  318. restore: state
  319. });
  320. }
  321. }
  322. }
  323. }
  324. function userpageNote(ele, changeId) {
  325. const eleId = noteObj.fn.getText(ele, selector.userpage.id, 'error', atFilter);
  326. if (changeId) {
  327. changeId === eleId && noteObj.handler(eleId, ele, selector.userpage.showName, {
  328. add: 'span',
  329. className: [nameSet.blueTag]
  330. });
  331. } else {
  332. const eleName = noteObj.fn.getText(ele, selector.userpage.showName, 'info');
  333. noteObj.handler(eleId, ele, selector.userpage.showName, {
  334. add: 'span',
  335. className: [nameSet.blueTag]
  336. }, eleName);
  337. }
  338. }
  339. function followNote(ele, changeId) {
  340. spanItemNote(ele, selector.follow.id, selector.follow.showName, changeId);
  341. }
  342. function rightRecommendedNote(ele, changeId) {
  343. spanItemNote(ele, selector.rightRecommended.id, selector.rightRecommended.showName, changeId);
  344. }
  345. function modalNote(ele, changeId) {
  346. spanItemNote(ele, selector.modal.id, selector.modal.showName, changeId);
  347. }
  348. function spanItemNote(ele, idSelector, nameSelector, changeId) {
  349. const eleId = noteObj.fn.getUrlId(ele, idSelector);
  350. if (!changeId || changeId === eleId) {
  351. noteObj.handler(eleId, ele, nameSelector, {
  352. add: 'span',
  353. className: [nameSet.blueTag]
  354. });
  355. }
  356. }
  357. function disableInTweetsEvent(status) {
  358. noteObj.fn.queryAll(selector.homepage.article, 'none').forEach(ele => {
  359. homepageAtNote(ele, status);
  360. });
  361. }
  362. function insertToolbarButtonEvent(status) {
  363. noteObj.fn.queryAll(selector.homepage.article, 'none').forEach(ele => {
  364. toolBarNoteButton(ele, status);
  365. });
  366. }
  367. function changeEvent(changeId) {
  368. noteObj.fn.queryAll(selector.homepage.article, 'none').forEach(ele => {
  369. homepageNote(ele, changeId);
  370. reprintANote(ele, changeId);
  371. blockquoteNote(ele, changeId);
  372. homepageAtNote(ele, noteObj.getOtherConfig().disableInTweets === true, changeId);
  373. });
  374. noteObj.fn.queryAll(selector.userpage.main).forEach(ele => {
  375. userpageNote(ele, changeId);
  376. });
  377. noteObj.fn.queryAll(selector.follow.cell, 'info').forEach(ele => {
  378. followNote(ele, changeId);
  379. });
  380. noteObj.fn.queryAll(selector.rightRecommended.cell).forEach(ele => {
  381. rightRecommendedNote(ele, changeId);
  382. });
  383. noteObj.fn.queryAll(selector.modal.cell, 'info').forEach(ele => {
  384. modalNote(ele, changeId);
  385. });
  386. }
  387. function init() {
  388. const arriveOption = {
  389. fireOnAttributesModification: true,
  390. existing: true
  391. };
  392. const rootDom = noteObj.fn.query(selector.root);
  393. if (rootDom === null) {
  394. return;
  395. }
  396. noteObj.arrive(rootDom, selector.homepage.article, arriveOption, ele => {
  397. toolBarNoteButton(ele, noteObj.getOtherConfig().showToolbarButton === true);
  398. homepageNote(ele);
  399. reprintANote(ele);
  400. blockquoteNote(ele);
  401. const disableInTweets = noteObj.getOtherConfig().disableInTweets === true;
  402. if (!disableInTweets) {
  403. homepageAtNote(ele, disableInTweets);
  404. }
  405. });
  406. noteObj.arrive(rootDom, selector.userpage.main, arriveOption, ele => {
  407. const eleId = noteObj.fn.getText(ele, selector.userpage.id, 'error', atFilter);
  408. if (eleId) {
  409. const eleName = noteObj.fn.getText(ele, selector.userpage.showName, 'info');
  410. let followNoteBtn;
  411. const userpageFollow = noteObj.fn.query(ele, selector.userpage.follow);
  412. if (userpageFollow) {
  413. followNoteBtn = noteObj.createNoteBtn(eleId, eleName, [nameSet.beforeFollowNoteBtn, 'css-901oao']);
  414. userpageFollow.insertAdjacentElement('afterbegin', followNoteBtn);
  415. }
  416. const userIdChange = new MutationObserver(() => {
  417. const newUserId = noteObj.fn.getText(ele, selector.userpage.id, 'error', atFilter);
  418. if (newUserId) {
  419. noteObj.handler('', ele, selector.userpage.showName, {
  420. add: 'span',
  421. className: [nameSet.blueTag]
  422. });
  423. const newUserName = noteObj.fn.getText(ele, selector.userpage.showName, 'info');
  424. if (followNoteBtn) {
  425. followNoteBtn.remove();
  426. followNoteBtn = noteObj.createNoteBtn(newUserId, newUserName, [nameSet.beforeFollowNoteBtn, 'css-901oao']);
  427. userpageFollow && userpageFollow.insertAdjacentElement('afterbegin', followNoteBtn);
  428. }
  429. noteObj.handler(newUserId, ele, selector.userpage.showName, {
  430. add: 'span',
  431. className: [nameSet.blueTag]
  432. }, newUserName);
  433. }
  434. });
  435. const obId = noteObj.fn.query(ele, selector.userpage.id);
  436. obId && userIdChange.observe(obId, {
  437. subtree: true,
  438. characterData: true
  439. });
  440. }
  441. userpageNote(ele);
  442. });
  443. noteObj.arrive(rootDom, selector.follow.cell, arriveOption, ele => {
  444. followNote(ele);
  445. });
  446. noteObj.arrive(rootDom, selector.rightRecommended.cell, arriveOption, ele => {
  447. rightRecommendedNote(ele);
  448. });
  449. noteObj.arrive(rootDom, selector.modal.cell, arriveOption, ele => {
  450. modalNote(ele);
  451. });
  452. noteObj.arrive(rootDom, selector.hover.panel, arriveOption, ele => {
  453. const eleId = noteObj.fn.getUrlId(ele, selector.hover.id);
  454. if (eleId) {
  455. const userShowNameText = noteObj.fn.getText(ele, selector.hover.showName, 'info');
  456. const userAvatar = noteObj.fn.query(ele, selector.hover.userAvatar);
  457. userAvatar && userAvatar.after(noteObj.createNoteBtn(eleId, userShowNameText, [nameSet.noteBtn, nameSet.panelBtn]));
  458. noteObj.handler(eleId, ele, selector.hover.showName, {
  459. add: 'span',
  460. className: [nameSet.blueTag]
  461. }, userShowNameText);
  462. }
  463. });
  464. }
  465. init();
  466. })();