UserscriptAPI

My API for userscripts.

Tính đến 06-09-2021. Xem phiên bản mới nhất.

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/scripts/409641/967908/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.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. }