lib:textjack

3/5/2025, 7:16:43 PM

  1. // ==UserScript==
  2. // @name lib:textjack
  3. // @version 6
  4. // @match *://*/*
  5. // @run-at document-start
  6. // @author rssaromeo
  7. // @license GPLv3
  8. // @include *
  9. // @grant none
  10. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHJQTFRFAAAAEIijAo2yAI60BYyuF4WaFIifAY6zBI2wB4usGIaZEYigIoiZCIyrE4igG4iYD4mjEomhFoedCoqpDIqnDomlBYyvE4efEYmiDYqlA42xBoytD4mkCYqqGYSUFYidC4qoC4upAo6yCoupDYqmCYur4zowOQAAACZ0Uk5TAO////9vr////1+/D/+/L+/Pf/////+f3///////H4////////+5G91rAAACgUlEQVR4nM2Y22KjIBCGidg1264liZqDadK03X3/V2wNKHMC7MpF/xthHD5mgERAqZhWhfYqH6K+Qf2qNNf625hCoFj9/gblMUi5q5jLkXLCKudgyiRm0FMK82cWJp1fLbV5VmvJbCIc0GCYaFqqlDJgADdBjncqAXYobm1xh72aFMflbysteFfdy2Yi1XGOm5HGBzQ1dq7TzEoxjeNTjQZb7VA3e1c7+ImgasAgQ9+xusNVNZIo5xmOMgihIS2PbCQIiHEUdTvhxCcS/kPomfFI2zHy2PkWmA6aNatIJpKFJyekyy02xh5Y3DI9T4aOT6VhIUrsNTFp1pf79Z4SIIVDegl6IJO6cHiL/GimIZDhgTu/BlYWCQzHMl0zBWT/T3KAhtxOuUB9FtBrpsz0RV4xsjHmW+UCaffcSy/5viMGer0/6HdFNMZBq/vjJL38H9Dqx4Fuy0Em12DbZy+9pGtiDijbglwAehyj11n0tRD3WUBm+lwulE/8h4BuA+iWAQQnteg2Xm63WQLTpnMnpjdge0Mgu/GRPsV4xdjQ94Lfi624fabhDkfUqIKNrM64Q837v8yL0prasepCgrtvw1sJpoqanGEX7b5mQboNW8eawXaWXTMfMGxub472hzWzHSn6Sg2G9+6TAyRruE71s+zAzjWaknoyJCQzwxrghH2k5FDT4eqWunuNxyN9QCGcxVod5oADbYnIUkDTGZEf1xDJnSFteQ3KdsT8zYDMQXcHxsevcLH1TrsABzkNPyA/L7b0jg704viMMlpQI96WsHknCt/3YH0kOEo9zcGkwrFK39ck72rmoehmKqo2RKlilzSy/nJKEV45CT38myJp456fezktHjN5aeMAAAAASUVORK5CYII=
  11. // @description 3/5/2025, 7:16:43 PM
  12. // @namespace https://greasyfork.org/users/1184528
  13. // ==/UserScript==
  14.  
  15. let obslist = []
  16. var textJackList = []
  17. Object.assign(window, console)
  18. const a = loadlib("allfuncs")
  19.  
  20. function replaceText(text) {
  21. var oldtext = text
  22. for (var cb of textJackList) {
  23. text = cb(text)
  24. if (text !== oldtext) {
  25. // error("changed", text, oldtext)
  26. return (text = replaceText(text))
  27. }
  28. }
  29. return text
  30. }
  31. // Function to handle text changes
  32. function handleTextChange(mutations) {
  33. disableObservers()
  34. mutations.forEach((mutation) => {
  35. if (mutation.type === "childList") {
  36. mutation.addedNodes.forEach((node) => {
  37. if (node.nodeType === Node.TEXT_NODE) {
  38. // Filter text for text nodes
  39. var newtext = replaceText(node.textContent)
  40. if (node.textContent != newtext) {
  41. // trace("changed")
  42. node.textContent = newtext
  43. // log(1, node.textContent, newtext)
  44. }
  45. } else if (node.nodeType === Node.ELEMENT_NODE) {
  46. // debugger
  47. // If it's an element, check its text content
  48. node.childNodes.forEach((child) => {
  49. if (child.nodeType === Node.TEXT_NODE) {
  50. var newtext = replaceText(child.textContent)
  51. if (child.textContent != newtext) {
  52. // trace("changed")
  53. child.textContent = newtext
  54. // log(2, child.textContent, newtext)
  55. }
  56. }
  57. })
  58. // Observe any shadow roots
  59. if (node.shadowRoot) {
  60. createObserver(node.shadowRoot)
  61. }
  62. }
  63. })
  64. } else if (mutation.type === "characterData") {
  65. // Filter text for character data changes
  66. var text = mutation.target.textContent
  67. var newtext = replaceText(text)
  68. if (newtext !== text) {
  69. // trace("changed")
  70. mutation.target.textContent = newtext
  71. }
  72. }
  73. })
  74. enableObservers()
  75. }
  76.  
  77. // Create a MutationObserver for the main document
  78. function createObserver(elem) {
  79. var obs = new MutationObserver((mutations) => {
  80. handleTextChange(mutations)
  81. mutations.forEach((mutation) => {
  82. mutation.addedNodes.forEach((node) => {
  83. // If the added node is a shadow host, observe its shadow root
  84. if (node.nodeType === Node.ELEMENT_NODE && node.shadowRoot) {
  85. createObserver(node.shadowRoot)
  86. }
  87. })
  88. })
  89. })
  90.  
  91. obslist.push([
  92. obs,
  93. elem,
  94. {
  95. childList: true,
  96. subtree: true,
  97. characterData: true,
  98. },
  99. ])
  100. }
  101. // Function to enable all observers
  102. function enableObservers() {
  103. try {
  104. disableObservers()
  105. } catch (e) {}
  106. // warn(obslist, 1)
  107. obslist.forEach(([observer, target, opts]) =>
  108. observer.observe(target, opts)
  109. )
  110. }
  111.  
  112. // Function to disable all observers
  113. function disableObservers() {
  114. // warn(obslist, 0)
  115. obslist.forEach(([observer]) => observer.disconnect())
  116. }
  117.  
  118. // Optionally, filter existing text content on page load
  119. function filterExistingText(node) {
  120. disableObservers()
  121. if (node.nodeType === Node.TEXT_NODE) {
  122. var text = node.textContent
  123. var newtext = replaceText(text)
  124. if (newtext !== text) {
  125. // trace("changed")
  126. node.textContent = newtext
  127. }
  128. } else if (node.nodeType === Node.ELEMENT_NODE) {
  129. node.childNodes.forEach(filterExistingText)
  130. // If the element has a shadow root, filter its content
  131. if (node.shadowRoot) {
  132. filterExistingText(node.shadowRoot)
  133. }
  134. }
  135. enableObservers()
  136. }
  137.  
  138. // Filter existing text in the document
  139. var waitingForBody = false
  140. if (document.body) {
  141. setup()
  142. } else {
  143. waitingForBody = true
  144. }
  145.  
  146. loadlib("libloader").savelib("textjack", function newTextJack(cb) {
  147. textJackList.push(cb)
  148. if (document.body) {
  149. setup()
  150. } else {
  151. waitingForBody = true
  152. }
  153. })
  154. a.bodyload().then(() => {
  155. if (waitingForBody) {
  156. setup()
  157. }
  158. })
  159.  
  160. function setup() {
  161. filterExistingText(document.body)
  162. createObserver(document.body)
  163. enableObservers()
  164. }