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

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

目前为 2023-04-08 提交的版本,查看 最新版本

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