MonkeyModifier

Change webpage content

目前為 2024-08-12 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name MonkeyModifier
  3. // @namespace https://github.com/JiyuShao/greasyfork-scripts
  4. // @version 2024-08-12
  5. // @description Change webpage content
  6. // @author Jiyu Shao <jiyu.shao@gmail.com>
  7. // @license MIT
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant unsafeWindow
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. // ################### common tools
  16. function replaceTextInNode(node, originalText, replaceText) {
  17. // 如果当前节点是文本节点并且包含 originalText
  18. if (node instanceof Text && node.textContent.includes(originalText)) {
  19. // 替换文本
  20. node.textContent = node.textContent.replace(originalText, replaceText);
  21. }
  22.  
  23. // 如果当前节点有子节点,递归处理每个子节点
  24. if (node.hasChildNodes()) {
  25. node.childNodes.forEach((child) => {
  26. replaceTextInNode(child, originalText, replaceText);
  27. });
  28. }
  29. }
  30.  
  31. function registerMutationObserver(node, config = {}, options = {}) {
  32. const finalConfig = {
  33. attributes: false,
  34. childList: true,
  35. subtree: true,
  36. ...config,
  37. };
  38.  
  39. const finalOptions = {
  40. // 元素的属性发生了变化
  41. attributes: options.attributes || [],
  42. // 子节点列表发生了变化
  43. childList: {
  44. addedNodes:
  45. options.childList.addedNodes ||
  46. [
  47. // {
  48. // filter: (node) => {},
  49. // action: (node) => {},
  50. // }
  51. ],
  52. removedNodes: options.childList.removedNodes || [],
  53. },
  54. // 文本节点的内容发生了变化
  55. characterData: options.characterData || [],
  56. };
  57.  
  58. const observer = new MutationObserver((mutationsList, _observer) => {
  59. mutationsList.forEach((mutation) => {
  60. if (mutation.type === 'attributes') {
  61. finalOptions.attributes.forEach(({ filter, action }) => {
  62. try {
  63. if (filter(mutation.target, mutation)) {
  64. action(mutation.target, mutation);
  65. }
  66. } catch (error) {
  67. console.error(
  68. 'MutationObserver attributes callback failed:',
  69. mutation.target,
  70. error
  71. );
  72. }
  73. });
  74. }
  75. if (mutation.type === 'childList') {
  76. // 检查是否有新增的元素
  77. mutation.addedNodes.forEach((node) => {
  78. finalOptions.childList.addedNodes.forEach(({ filter, action }) => {
  79. try {
  80. if (filter(node, mutation)) {
  81. action(node, mutation);
  82. }
  83. } catch (error) {
  84. console.error(
  85. 'MutationObserver childList.addedNodes callback failed:',
  86. node,
  87. error
  88. );
  89. }
  90. });
  91. });
  92.  
  93. // 检查是否有删除元素
  94. mutation.removedNodes.forEach((node) => {
  95. finalOptions.childList.removedNodes.forEach((filter, action) => {
  96. try {
  97. if (filter(node, mutation)) {
  98. action(node, mutation);
  99. }
  100. } catch (error) {
  101. console.error(
  102. 'MutationObserver childList.removedNodes callback failed:',
  103. node,
  104. error
  105. );
  106. }
  107. });
  108. });
  109. }
  110. if (mutation.type === 'characterData') {
  111. finalOptions.characterData.forEach(({ filter, action }) => {
  112. try {
  113. if (filter(mutation.target, mutation)) {
  114. action(mutation.target, mutation);
  115. }
  116. } catch (error) {
  117. console.error(
  118. 'MutationObserver characterData callback failed:',
  119. mutation.target,
  120. error
  121. );
  122. }
  123. });
  124. }
  125. });
  126. });
  127. observer.observe(node, finalConfig);
  128. return observer;
  129. }
  130.  
  131. // ################### 加载前插入样式覆盖
  132. const style = document.createElement('style');
  133. const cssRules = `
  134. .dropdown-submenu--viewmode {
  135. display: none !important;
  136. }
  137. [field=modified] {
  138. display: none !important;
  139. }
  140.  
  141. [data-value=modified] {
  142. display: none !important;
  143. }
  144. [data-value=lastmodify] {
  145. display: none !important;
  146. }
  147.  
  148. [data-grid-field=modified] {
  149. display: none !important;
  150. }
  151.  
  152. [data-field-key=modified] {
  153. display: none !important;
  154. }
  155.  
  156. #Revisions {
  157. display: none !important;
  158. }
  159.  
  160. #ContentModified {
  161. display: none !important;
  162. }
  163.  
  164. [title="最后修改时间"] {
  165. display: none !important;
  166. }
  167.  
  168. .left-tree-bottom__manager-company--wide {
  169. display: none !important;
  170. }
  171.  
  172. .left-tree-narrow .left-tree-bottom__personal--icons > a:nth-child(1) {
  173. display: none !important;
  174. }
  175. `;
  176. style.appendChild(document.createTextNode(cssRules));
  177. unsafeWindow.document.head.appendChild(style);
  178.  
  179. // ################### 网页内容加载完成立即执行脚本
  180. unsafeWindow.addEventListener('DOMContentLoaded', function () {
  181. // 监听任务右侧基本信息
  182. const taskRightInfoEles =
  183. unsafeWindow.document.querySelectorAll('#ContentModified');
  184. taskRightInfoEles.forEach((element) => {
  185. const parentDiv = element.closest('div.left_3_col');
  186. if (parentDiv) {
  187. parentDiv.style.display = 'none';
  188. }
  189. });
  190. });
  191.  
  192. // ################### 加载完成动态监听
  193. unsafeWindow.addEventListener('load', function () {
  194. registerMutationObserver(
  195. unsafeWindow.document.body,
  196. {
  197. attributes: false,
  198. childList: true,
  199. subtree: true,
  200. },
  201. {
  202. childList: {
  203. addedNodes: [
  204. // 动态文本替换问题
  205. {
  206. filter: (node, _mutation) => {
  207. return node.textContent.includes('最后修改时间');
  208. },
  209. action: (node, _mutation) => {
  210. replaceTextInNode(node, '最后修改时间', '迭代修改时间');
  211. },
  212. },
  213. // 监听动态弹窗 隐藏设置列表字段-最后修改时间左侧
  214. {
  215. filter: (node, _mutation) => {
  216. return (
  217. node.querySelectorAll &&
  218. node.querySelectorAll('input[value=modified]').length > 0
  219. );
  220. },
  221. action: (node, _mutation) => {
  222. node
  223. .querySelectorAll('input[value=modified]')
  224. .forEach((ele) => {
  225. const parentDiv = ele.closest('div.field');
  226. if (parentDiv) {
  227. parentDiv.style.display = 'none';
  228. }
  229. });
  230. },
  231. },
  232. // 监听动态弹窗 隐藏设置列表字段-最后修改时间右侧
  233. {
  234. filter: (node, _mutation) => {
  235. return (
  236. node.querySelectorAll &&
  237. node.querySelectorAll('span[title=最后修改时间]').length > 0
  238. );
  239. },
  240. action: (node, _mutation) => {
  241. node
  242. .querySelectorAll('span[title=最后修改时间]')
  243. .forEach((ele) => {
  244. const parentDiv = ele.closest('div[role=treeitem]');
  245. if (parentDiv) {
  246. parentDiv.style.display = 'none';
  247. }
  248. });
  249. },
  250. },
  251. ],
  252. },
  253. }
  254. );
  255. });
  256. })();