libs

JavaScript 库函数,以便调用

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/491971/1356811/libs.js

  1. // ==UserScript==
  2. // @name libs
  3. // @description JavaScript 库函数,以便调用
  4. // @namespace essence/libs
  5. // @version 0.4
  6. // @grant GM_addStyle
  7. // ==/UserScript==
  8.  
  9. // 等待
  10. const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
  11.  
  12. /**
  13. * 等待指定元素出现后,执行回调
  14. * @param selector 元素选择器。参考`document.querySelector`
  15. * @param callback 需要执行的回调。传递的参数为目标元素
  16. */
  17. const waitElem = (selector, callback) => {
  18. const observer = new MutationObserver(() => {
  19. const element = document.querySelector(selector)
  20. if (element) {
  21. callback(element)
  22. observer.disconnect()
  23. }
  24. })
  25.  
  26. observer.observe(document.body, {
  27. childList: true,
  28. subtree: true,
  29. })
  30. }
  31.  
  32. /**
  33. * 读取网站存储到 localStorage 的所有值
  34. * @param excludeRegexp 不读取的键的正则。如 /^Hm_lvt/
  35. * @return {string} JSON 文本
  36. */
  37. const readLocalStorageValues = (excludeRegexp = undefined) => {
  38. const result = {}
  39. for (let i = 0; i < localStorage.length; i++) {
  40. const key = localStorage.key(i)
  41. if (excludeRegexp && excludeRegexp.test(key)) {
  42. continue
  43. }
  44.  
  45. result[key] = localStorage.getItem(key)
  46. }
  47.  
  48. return JSON.stringify(result)
  49. }
  50.  
  51. /**
  52. * 恢复数据到网站的 localStorage
  53. * @param {string} json
  54. */
  55. const restoreLocalStorageValues = (json) => {
  56. const data = JSON.parse(json)
  57.  
  58. Object.keys(data).forEach(key => localStorage.setItem(key, data[key]))
  59. }
  60.  
  61. /**
  62. * 让容器以 flex row 显示子元素
  63. * @param elem {HTMLElement} 容器元素
  64. * @param gap {string} 子元素的间隔。默认"8px"
  65. * @param alignItems {string} 子元素垂直对齐。默认"center"
  66. */
  67. const containerToRow = (elem, gap = "8px", alignItems = "center") => {
  68. elem.style.display = "flex"
  69. elem.style.flexDirection = "row"
  70. elem.style.justifyContent = "space-between"
  71. elem.style.gap = gap
  72. elem.style.alignItems = alignItems
  73. }
  74.  
  75. /**
  76. * 发送通知。可使用 Toast 实例快捷发送消息
  77. * @param message {string} 消息
  78. * @param color {string} 消息类型。可选:"default" | "primary" | "success" | warning" | "danger"
  79. * @param element 右侧操作按钮
  80. * @param disappear 持续时长(毫秒)
  81. */
  82. const toast = (message, color = 'primary', element = null, disappear = 5000) => {
  83. // 创建通知元素
  84. const toast = document.createElement('div')
  85.  
  86. // 设置消息文本
  87. toast.textContent = message
  88.  
  89. // 如果提供了操作按钮元素,添加到通知
  90. if (element) {
  91. toast.appendChild(element)
  92. }
  93.  
  94. // 根据消息的类型设置颜色,并显示
  95. toast.classList.add("toast", "essenceX", color)
  96.  
  97. // 将通知元素添加到页面
  98. document.body.appendChild(toast)
  99.  
  100. // 显示 toast
  101. setTimeout(() => toast.classList.add("show"), 10)
  102.  
  103. // toast 在 N 秒后消失
  104. setTimeout(() => {
  105. toast.classList.remove("show")
  106. toast.classList.add("hide")
  107.  
  108. // 在消失动画完成后,从页面移除通知元素
  109. toast.addEventListener('transitionend', () => toast.remove())
  110. }, disappear)
  111. }
  112.  
  113. /**
  114. * 快捷发送通知
  115. */
  116. const Toast = {
  117. default: (message, element = null, disappear = 5000) => toast(message, "default", element, disappear),
  118. primary: (message, element = null, disappear = 5000) => toast(message, "primary", element, disappear),
  119. success: (message, element = null, disappear = 5000) => toast(message, "success", element, disappear),
  120. warning: (message, element = null, disappear = 5000) => toast(message, "warning", element, disappear),
  121. danger: (message, element = null, disappear = 5000) => toast(message, "danger", element, disappear)
  122. }
  123.  
  124. const removeDialog = (dialog) => {
  125. dialog.classList.remove("show")
  126. dialog.classList.add("hide")
  127.  
  128. // 在消失动画完成后,从页面移除通知元素
  129. dialog.addEventListener('transitionend', () => dialog.remove())
  130. }
  131.  
  132. /**
  133. * 显示对话框
  134. * @param title {string} 标题
  135. * @param body {Element} 内容
  136. * @param isModel {boolean} 是否为模态对话框
  137. * @return {HTMLElement} 返回对话框实例,以便 remove()
  138. */
  139. const showDialog = (title, body, isModel = false) => {
  140. const dialog = document.createElement('div')
  141. const content = document.createElement('div')
  142. const header = document.createElement('div')
  143. const dialogBody = document.createElement('div')
  144.  
  145. dialog.classList.add("dialog", "essenceX")
  146. content.classList.add("dialog-content", "essenceX")
  147. header.classList.add("dialog-header", "essenceX")
  148. dialogBody.classList.add("dialog-body", "essenceX")
  149.  
  150. header.textContent = title
  151. dialogBody.appendChild(body)
  152.  
  153. content.append(header, dialogBody)
  154.  
  155. dialog.appendChild(content)
  156.  
  157. document.body.appendChild(dialog)
  158.  
  159. // 使用动画显示对话框
  160. setTimeout(() => dialog.classList.add("show"), 10)
  161.  
  162. // 不是模态对话框时,点击外部即取消
  163. if (!isModel) {
  164. window.addEventListener("click", event => {
  165. if (event.target === dialog) {
  166. removeDialog(dialog)
  167. }
  168. })
  169. }
  170.  
  171. return dialog
  172. }