Greasy Fork is available in English.

UserscriptAPI

My API for userscripts.

Verze ze dne 07. 09. 2021. Zobrazit nejnovější verzi.

Tento skript by neměl být instalován přímo. Jedná se o knihovnu, kterou by měly jiné skripty využívat pomocí meta příkazu // @require https://update.greatest.deepsurf.us/scripts/409641/968206/UserscriptAPI.js

  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.1.20210907
  20. * @author Laster2800
  21. * @see {@link https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI UserscriptAPI}
  22. */
  23. class UserscriptAPI {
  24. /** 可访问模块 */
  25. static #modules = {}
  26. /** 待添加模块样式队列 */
  27. #moduleCssQueue = []
  28.  
  29. /**
  30. * @param {Object} [options] 选项
  31. * @param {string} [options.id='default'] 标识符
  32. * @param {string} [options.label] 日志标签,为空时不设置标签
  33. * @param {Object} [options.wait] `wait` API 默认选项(默认值见构造器代码)
  34. * @param {Object} [options.wait.condition] `wait` 条件 API 默认选项
  35. * @param {Object} [options.wait.element] `wait` 元素 API 默认选项
  36. * @param {number} [options.fadeTime=400] UI 渐变时间
  37. */
  38. constructor(options) {
  39. this.options = {
  40. id: 'default',
  41. label: null,
  42. fadeTime: 400,
  43. ...options,
  44. wait: {
  45. condition: {
  46. callback: result => api.logger.info(result),
  47. interval: 100,
  48. timeout: 10000,
  49. onTimeout: function() {
  50. api.logger[this.stopOnTimeout ? 'error' : 'warn'](['TIMEOUT', 'executeAfterConditionPassed', options])
  51. },
  52. stopOnTimeout: true,
  53. stopCondition: null,
  54. onStop: () => api.logger.error(['STOP', 'executeAfterConditionPassed', options]),
  55. stopInterval: 50,
  56. stopTimeout: 0,
  57. onError: e => api.logger.error(['ERROR', 'executeAfterConditionPassed', options, e]),
  58. stopOnError: true,
  59. timePadding: 0,
  60. ...options?.wait?.condition,
  61. },
  62. element: {
  63. base: document,
  64. exclude: null,
  65. callback: el => api.logger.info(el),
  66. subtree: true,
  67. multiple: false,
  68. repeat: false,
  69. throttleWait: 100,
  70. timeout: 10000,
  71. onTimeout: function() {
  72. api.logger[this.stopOnTimeout ? 'error' : 'warn'](['TIMEOUT', 'executeAfterElementLoaded', options])
  73. },
  74. stopOnTimeout: false,
  75. stopCondition: null,
  76. onStop: () => api.logger.error(['STOP', 'executeAfterElementLoaded', options]),
  77. onError: e => api.logger.error(['ERROR', 'executeAfterElementLoaded', options, e]),
  78. stopOnError: true,
  79. timePadding: 0,
  80. ...options?.wait?.element,
  81. },
  82. },
  83. }
  84.  
  85. const win = typeof unsafeWindow == 'undefined' ? window : unsafeWindow
  86. /** @type {UserscriptAPI} */
  87. let api = win[`_userscriptAPI_${this.options.id}`]
  88. if (api) {
  89. api.options = this.options
  90. return api
  91. }
  92. api = win[`_userscriptAPI_${this.options.id}`] = this
  93.  
  94. /** @type {UserscriptAPIDom} */
  95. this.dom = this.#getModuleInstance('dom')
  96. /** @type {UserscriptAPILogger} */
  97. this.logger = this.#getModuleInstance('logger')
  98. /** @type {UserscriptAPIMessage} */
  99. this.message = this.#getModuleInstance('message')
  100. /** @type {UserscriptAPITool} */
  101. this.tool = this.#getModuleInstance('tool')
  102. /** @type {UserscriptAPIWait} */
  103. this.wait = this.#getModuleInstance('wait')
  104. /** @type {UserscriptAPIWeb} */
  105. this.web = this.#getModuleInstance('web')
  106.  
  107. if (!api.dom) {
  108. api.dom = {
  109. addStyle(css) {
  110. const style = document.createElement('style')
  111. style.setAttribute('type', 'text/css')
  112. style.className = `${api.options.id}-style`
  113. style.appendChild(document.createTextNode(css))
  114. const parent = document.head || document.documentElement
  115. if (parent) {
  116. parent.appendChild(style)
  117. }
  118. },
  119. }
  120. }
  121. if (!api.logger) {
  122. api.logger = {
  123. info: console.log,
  124. warn: console.warn,
  125. error: console.error,
  126. }
  127. }
  128.  
  129. for (const css of this.#moduleCssQueue) {
  130. api.dom.addStyle(css)
  131. }
  132. }
  133.  
  134. /**
  135. * 注册模块
  136. * @param {string} name 模块名称
  137. * @param {Object} module 模块类
  138. */
  139. static registerModule(name, module) {
  140. this.#modules[name] = module
  141. }
  142. /**
  143. * 获取模块实例
  144. * @param {string} name 模块名称
  145. * @returns {Object} 模块实例,无对应模块时返回 `null`
  146. */
  147. #getModuleInstance(name) {
  148. const module = UserscriptAPI.#modules[name]
  149. return module ? new module(this) : null
  150. }
  151.  
  152. /**
  153. * 初始化模块样式(仅应在模块构造器中使用)
  154. * @param {string} css 样式
  155. */
  156. initModuleStyle(css) {
  157. this.#moduleCssQueue.push(css)
  158. }
  159. }