用户脚本运行环境

尝试使其他用户脚本适配没有油猴支持的运行环境(如:X浏览器,via浏览器等等)。注意:所有的脚本存储在本地的数据都将不会被真正储存,而是会随着浏览器页面的关闭而被丢弃,也就是说,每次脚本运行时,都将如同第一次运行一样。

目前为 2021-10-03 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Userscript Runtime
  3. // @name:zh 用户脚本运行环境
  4. // @name:zh-CN 用户脚本运行环境
  5. // @namespace GM_PolyFill for Users
  6. // @version 0.1
  7. // @description Try to make userscripts work on non-tempermonkey environments(e.g. X Browser, Via Browser, etc.) as well. Attention: All data that are expected to be saved in browser storage will actually NOT be saved, that means, everytime the userscript executes will act as this is the first time.
  8. // @description:zh 尝试使其他用户脚本适配没有油猴支持的运行环境(如:X浏览器,via浏览器等等)。注意:所有的脚本存储在本地的数据都将不会被真正储存,而是会随着浏览器页面的关闭而被丢弃,也就是说,每次脚本运行时,都将如同第一次运行一样。
  9. // @description:zh-CN 尝试使其他用户脚本适配没有油猴支持的运行环境(如:X浏览器,via浏览器等等)。注意:所有的脚本存储在本地的数据都将不会被真正储存,而是会随着浏览器页面的关闭而被丢弃,也就是说,每次脚本运行时,都将如同第一次运行一样。
  10. // @author PY-DNG
  11. // @include *
  12. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM_listValue
  16. // @grant GM_deleteValue
  17. // @grant GM_xmlhttpRequest
  18. // @grant GM_openInTab
  19. // @grant GM_setClipboard
  20. // @grant unsafeWindow
  21. // @run-at document-start
  22. // ==/UserScript==
  23.  
  24. (function() {
  25. 'use strict';
  26.  
  27. bypassXB();
  28. GM_PolyFill_Once();
  29.  
  30. // Bypass xbrowser's useless GM_functions
  31. function bypassXB() {
  32. if (typeof(mbrowser) === 'object') {
  33. window.unsafeWindow = window.GM_setClipboard = window.GM_openInTab = window.GM_xmlhttpRequest = window.GM_getValue = window.GM_setValue = window.GM_listValues = window.GM_deleteValue = undefined;
  34. }
  35. }
  36.  
  37. // GM_Polyfill By PY-DNG
  38. // Special Edition For Userscript Users
  39. // 2021.07.18 - 2021.07.19, 2021.10.03 - 2021.10.03
  40. // Simply provides the following GM_functions using localStorage, XMLHttpRequest and window.open:
  41. // Returns object GM_POLYFILLED which has the following properties that shows you which GM_functions are actually polyfilled:
  42. // GM_setValue, GM_getValue, GM_deleteValue, GM_listValues, GM_xmlhttpRequest, GM_openInTab, GM_setClipboard, unsafeWindow(object)
  43. // All polyfilled GM_functions are accessable in window object/Global_Scope(only without Tempermonkey Sandboxing environment)
  44. // -!!!- IMPORTANT: PROVIDED GM_*FUNCTIONS WILL ONLY BE ABLE IN SINGLE USE - THAT MEANS THAT ALL DATA YOU EXPECTED TO BE STORED IN BROWSER USING GM_SETVALUE WILL ACTUALLY NOT BE STORED! GM_SETVALUE WILL JUST STORE DATA INTO A CACHE OBJECT! -!!!-
  45. function GM_PolyFill_Once() {
  46. let GM_POLYFILL_storage = {};
  47. const GM_POLYFILLED = {
  48. GM_setValue: true,
  49. GM_getValue: true,
  50. GM_deleteValue: true,
  51. GM_listValues: true,
  52. GM_xmlhttpRequest: true,
  53. GM_openInTab: true,
  54. GM_setClipboard: true,
  55. unsafeWindow: true,
  56. once: true
  57. }
  58.  
  59. GM_setValue_polyfill();
  60. GM_getValue_polyfill();
  61. GM_deleteValue_polyfill();
  62. GM_listValues_polyfill();
  63. GM_xmlhttpRequest_polyfill();
  64. GM_openInTab_polyfill();
  65. GM_setClipboard_polyfill();
  66. unsafeWindow_polyfill();
  67.  
  68. // GM_setValue
  69. function GM_setValue_polyfill() {
  70. typeof (GM_setValue) === 'function' ? GM_POLYFILLED.GM_setValue = false: window.GM_setValue = PF_GM_setValue;;
  71.  
  72. function PF_GM_setValue(name, value) {
  73. name = String(name);
  74. GM_POLYFILL_storage[name] = value;
  75. }
  76. }
  77.  
  78. // GM_getValue
  79. function GM_getValue_polyfill() {
  80. typeof (GM_getValue) === 'function' ? GM_POLYFILLED.GM_getValue = false: window.GM_getValue = PF_GM_getValue;
  81.  
  82. function PF_GM_getValue(name, defaultValue) {
  83. name = String(name);
  84. if (GM_POLYFILL_storage.hasOwnProperty(name)) {
  85. return GM_POLYFILL_storage[name];
  86. } else {
  87. return defaultValue;
  88. }
  89. }
  90. }
  91.  
  92. // GM_deleteValue
  93. function GM_deleteValue_polyfill() {
  94. typeof (GM_deleteValue) === 'function' ? GM_POLYFILLED.GM_deleteValue = false: window.GM_deleteValue = PF_GM_deleteValue;
  95.  
  96. function PF_GM_deleteValue(name) {
  97. name = String(name);
  98. if (GM_POLYFILL_storage.hasOwnProperty(name)) {
  99. delete GM_POLYFILL_storage[name];
  100. }
  101. }
  102. }
  103.  
  104. // GM_listValues
  105. function GM_listValues_polyfill() {
  106. typeof (GM_listValues) === 'function' ? GM_POLYFILLED.GM_listValues = false: window.GM_listValues = PF_GM_listValues;
  107.  
  108. function PF_GM_listValues() {
  109. return Object.keys(GM_POLYFILL_storage);
  110. }
  111. }
  112.  
  113. // unsafeWindow
  114. function unsafeWindow_polyfill() {
  115. typeof (unsafeWindow) === 'object' ? GM_POLYFILLED.unsafeWindow = false: window.unsafeWindow = window;
  116. }
  117.  
  118. // GM_xmlhttpRequest
  119. // not supported properties of details: synchronous binary nocache revalidate context fetch
  120. // not supported properties of response(onload arguments[0]): finalUrl
  121. // ---!IMPORTANT!--- DOES NOT SUPPORT CROSS-ORIGIN REQUESTS!!!!! ---!IMPORTANT!---
  122. function GM_xmlhttpRequest_polyfill() {
  123. typeof (GM_xmlhttpRequest) === 'function' ? GM_POLYFILLED.GM_xmlhttpRequest = false: window.GM_xmlhttpRequest = PF_GM_xmlhttpRequest;
  124.  
  125. // details.synchronous is not supported as Tempermonkey
  126. function PF_GM_xmlhttpRequest(details) {
  127. const xhr = new XMLHttpRequest();
  128.  
  129. // open request
  130. const openArgs = [details.method, details.url, true];
  131. if (details.user && details.password) {
  132. openArgs.push(details.user);
  133. openArgs.push(details.password);
  134. }
  135. xhr.open.apply(xhr, openArgs);
  136.  
  137. // set headers
  138. if (details.headers) {
  139. for (const key of Object.keys(details.headers)) {
  140. xhr.setRequestHeader(key, details.headers[key]);
  141. }
  142. }
  143. details.cookie ? xhr.setRequestHeader('cookie', details.cookie) : function () {};
  144. details.anonymous ? xhr.setRequestHeader('cookie', '') : function () {};
  145.  
  146. // properties
  147. xhr.timeout = details.timeout;
  148. xhr.responseType = details.responseType;
  149. details.overrideMimeType ? xhr.overrideMimeType(details.overrideMimeType) : function () {};
  150.  
  151. // events
  152. xhr.onabort = details.onabort;
  153. xhr.onerror = details.onerror;
  154. xhr.onloadstart = details.onloadstart;
  155. xhr.onprogress = details.onprogress;
  156. xhr.onreadystatechange = details.onreadystatechange;
  157. xhr.ontimeout = details.ontimeout;
  158. xhr.onload = function (e) {
  159. const response = {
  160. readyState: xhr.readyState,
  161. status: xhr.status,
  162. statusText: xhr.statusText,
  163. responseHeaders: xhr.getAllResponseHeaders(),
  164. response: xhr.response
  165. };
  166. (details.responseType === '' || details.responseType === 'text') ? (response.responseText = xhr.responseText) : function () {};
  167. (details.responseType === '' || details.responseType === 'document') ? (response.responseXML = xhr.responseXML) : function () {};
  168. details.onload(response);
  169. }
  170.  
  171. // send request
  172. details.data ? xhr.send(details.data) : xhr.send();
  173.  
  174. return {
  175. abort: xhr.abort
  176. };
  177. }
  178. }
  179.  
  180. // NOTE: options(arg2) is NOT SUPPORTED! if provided, then will just be skipped.
  181. function GM_openInTab_polyfill() {
  182. typeof (GM_openInTab) === 'function' ? GM_POLYFILLED.GM_openInTab = false: window.GM_openInTab = PF_GM_openInTab;
  183.  
  184. function PF_GM_openInTab(url) {
  185. window.open(url);
  186. }
  187. }
  188.  
  189. // NOTE: needs to be called in an event handler function, and info(arg2) is NOT SUPPORTED!
  190. function GM_setClipboard_polyfill() {
  191. typeof (GM_setClipboard) === 'function' ? GM_POLYFILLED.GM_setClipboard = false: window.GM_setClipboard = PF_GM_setClipboard;
  192.  
  193. function PF_GM_setClipboard(text) {
  194. // Create a new textarea for copying
  195. const newInput = document.createElement('textarea');
  196. document.body.appendChild(newInput);
  197. newInput.value = text;
  198. newInput.select();
  199. document.execCommand('copy');
  200. document.body.removeChild(newInput);
  201. }
  202. }
  203.  
  204. window.GM_POLYFILLED = GM_POLYFILLED;
  205. return GM_POLYFILLED;
  206. }
  207. })();