UserscriptAPI

My API for userscripts.

Fra og med 06.09.2021. Se den nyeste version.

Dette script bør ikke installeres direkte. Det er et bibliotek, som andre scripts kan inkludere med metadirektivet // @require https://update.greatest.deepsurf.us/scripts/409641/967894/UserscriptAPI.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

  1. /* exported UserscriptAPI */
  2. /**
  3. * UserscriptAPI
  4. *
  5. * 需要引入子模块方可工作。所有模块均依赖于 `UserscriptAPI`,模块间的依赖关系如下:
  6. *
  7. * ```plaintext
  8. * +─────────+─────────+
  9. * 模块 | 依赖模块
  10. * +─────────+─────────+
  11. * dom |
  12. * logger |
  13. * message | dom
  14. * tool |
  15. * wait | tool
  16. * web |
  17. * +─────────+─────────+
  18. * ```
  19. * @version 2.0.0.20210906
  20. * @author Laster2800
  21. * @see {@link https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI UserscriptAPI}
  22. */
  23. class UserscriptAPI {
  24. /** @type {UserscriptAPIDom} */
  25. dom = this.#getModuleInstance('dom')
  26. /** @type {UserscriptAPILogger} */
  27. logger = this.#getModuleInstance('logger')
  28. /** @type {UserscriptAPIMessage} */
  29. message = this.#getModuleInstance('message')
  30. /** @type {UserscriptAPITool} */
  31. tool = this.#getModuleInstance('tool')
  32. /** @type {UserscriptAPIWait} */
  33. wait = this.#getModuleInstance('wait')
  34. /** @type {UserscriptAPIWeb} */
  35. web = this.#getModuleInstance('web')
  36.  
  37. /**
  38. * @param {Object} [options] 选项
  39. * @param {string} [options.id='default'] 标识符
  40. * @param {string} [options.label] 日志标签,为空时不设置标签
  41. * @param {Object} [options.wait] `wait` API 默认选项(默认值见构造器代码)
  42. * @param {Object} [options.wait.condition] `wait` 条件 API 默认选项
  43. * @param {Object} [options.wait.element] `wait` 元素 API 默认选项
  44. * @param {number} [options.fadeTime=400] UI 渐变时间
  45. */
  46. constructor(options) {
  47. this.options = {
  48. id: 'default',
  49. label: null,
  50. fadeTime: 400,
  51. ...options,
  52. wait: {
  53. condition: {
  54. callback: result => api.logger.info(result),
  55. interval: 100,
  56. timeout: 10000,
  57. onTimeout: function() {
  58. api.logger[this.stopOnTimeout ? 'error' : 'warn'](['TIMEOUT', 'executeAfterConditionPassed', options])
  59. },
  60. stopOnTimeout: true,
  61. stopCondition: null,
  62. onStop: () => api.logger.error(['STOP', 'executeAfterConditionPassed', options]),
  63. stopInterval: 50,
  64. stopTimeout: 0,
  65. onError: e => api.logger.error(['ERROR', 'executeAfterConditionPassed', options, e]),
  66. stopOnError: true,
  67. timePadding: 0,
  68. ...options?.wait?.condition,
  69. },
  70. element: {
  71. base: document,
  72. exclude: null,
  73. callback: el => api.logger.info(el),
  74. subtree: true,
  75. multiple: false,
  76. repeat: false,
  77. throttleWait: 100,
  78. timeout: 10000,
  79. onTimeout: function() {
  80. api.logger[this.stopOnTimeout ? 'error' : 'warn'](['TIMEOUT', 'executeAfterElementLoaded', options])
  81. },
  82. stopOnTimeout: false,
  83. stopCondition: null,
  84. onStop: () => api.logger.error(['STOP', 'executeAfterElementLoaded', options]),
  85. onError: e => api.logger.error(['ERROR', 'executeAfterElementLoaded', options, e]),
  86. stopOnError: true,
  87. timePadding: 0,
  88. ...options?.wait?.element,
  89. },
  90. },
  91. }
  92.  
  93. const win = typeof unsafeWindow == 'undefined' ? window : unsafeWindow
  94. /** @type {UserscriptAPI} */
  95. let api = win[`_userscriptAPI_${this.options.id}`]
  96. if (api) {
  97. api.options = this.options
  98. return api
  99. }
  100. api = win[`_userscriptAPI_${this.options.id}`] = this
  101.  
  102. if (!api.dom) {
  103. api.dom = {
  104. addStyle(css) {
  105. const style = document.createElement('style')
  106. style.setAttribute('type', 'text/css')
  107. style.className = `${api.options.id}-style`
  108. style.appendChild(document.createTextNode(css))
  109. const parent = document.head || document.documentElement
  110. if (parent) {
  111. parent.appendChild(style)
  112. }
  113. },
  114. }
  115. }
  116. if (!api.logger) {
  117. api.logger = {
  118. info: console.log,
  119. warn: console.warn,
  120. error: console.error,
  121. }
  122. }
  123.  
  124. api.dom.addStyle(`
  125. :root {
  126. --${api.options.id}-light-text-color: white;
  127. --${api.options.id}-shadow-color: #000000bf;
  128. }
  129.  
  130. .${api.options.id}-msgbox {
  131. z-index: 100000000;
  132. background-color: var(--${api.options.id}-shadow-color);
  133. font-size: 16px;
  134. max-width: 24em;
  135. min-width: 2em;
  136. color: var(--${api.options.id}-light-text-color);
  137. padding: 0.5em 1em;
  138. border-radius: 0.6em;
  139. opacity: 0;
  140. transition: opacity ${api.options.fadeTime}ms ease-in-out;
  141. user-select: none;
  142. }
  143.  
  144. .${api.options.id}-msgbox .gm-advanced-table td {
  145. vertical-align: middle;
  146. }
  147. .${api.options.id}-msgbox .gm-advanced-table td:first-child {
  148. padding-right: 0.6em;
  149. }
  150. `)
  151. }
  152.  
  153. /** 可访问模块 */
  154. static #modules = {}
  155.  
  156. /**
  157. * 注册模块
  158. * @param {string} name 模块名称
  159. * @param {Object} module 模块类
  160. */
  161. static registerModule(name, module) {
  162. this.#modules[name] = module
  163. }
  164.  
  165. /**
  166. * 获取模块实例
  167. * @param {string} name 模块名称
  168. * @returns {Object} 模块实例,无对应模块时返回 `null`
  169. */
  170. #getModuleInstance(name) {
  171. const module = UserscriptAPI.#modules[name]
  172. return module ? new module(this) : null
  173. }
  174. }