Greasy Fork is available in English.

libs

JavaScript 库函数,以便调用

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/491971/1356811/libs.js

// ==UserScript==
// @name         libs
// @description  JavaScript 库函数,以便调用
// @namespace    essence/libs
// @version      0.4
// @grant        GM_addStyle
// ==/UserScript==

// 等待
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

/**
 * 等待指定元素出现后,执行回调
 * @param selector 元素选择器。参考`document.querySelector`
 * @param callback 需要执行的回调。传递的参数为目标元素
 */
const waitElem = (selector, callback) => {
  const observer = new MutationObserver(() => {
    const element = document.querySelector(selector)
    if (element) {
      callback(element)
      observer.disconnect()
    }
  })

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  })
}

/**
 * 读取网站存储到 localStorage 的所有值
 * @param excludeRegexp 不读取的键的正则。如 /^Hm_lvt/
 * @return {string} JSON 文本
 */
const readLocalStorageValues = (excludeRegexp = undefined) => {
  const result = {}
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i)
    if (excludeRegexp && excludeRegexp.test(key)) {
      continue
    }

    result[key] = localStorage.getItem(key)
  }

  return JSON.stringify(result)
}

/**
 * 恢复数据到网站的 localStorage
 * @param {string} json
 */
const restoreLocalStorageValues = (json) => {
  const data = JSON.parse(json)

  Object.keys(data).forEach(key => localStorage.setItem(key, data[key]))
}

/**
 * 让容器以 flex row 显示子元素
 * @param elem {HTMLElement} 容器元素
 * @param gap {string} 子元素的间隔。默认"8px"
 * @param alignItems {string} 子元素垂直对齐。默认"center"
 */
const containerToRow = (elem, gap = "8px", alignItems = "center") => {
  elem.style.display = "flex"
  elem.style.flexDirection = "row"
  elem.style.justifyContent = "space-between"
  elem.style.gap = gap
  elem.style.alignItems = alignItems
}

/**
 * 发送通知。可使用 Toast 实例快捷发送消息
 * @param message {string} 消息
 * @param color {string} 消息类型。可选:"default" | "primary" | "success" | warning" | "danger"
 * @param element 右侧操作按钮
 * @param disappear 持续时长(毫秒)
 */
const toast = (message, color = 'primary', element = null, disappear = 5000) => {
  // 创建通知元素
  const toast = document.createElement('div')

  // 设置消息文本
  toast.textContent = message

  // 如果提供了操作按钮元素,添加到通知
  if (element) {
    toast.appendChild(element)
  }

  // 根据消息的类型设置颜色,并显示
  toast.classList.add("toast", "essenceX", color)

  // 将通知元素添加到页面
  document.body.appendChild(toast)

  // 显示 toast
  setTimeout(() => toast.classList.add("show"), 10)

  // toast 在 N 秒后消失
  setTimeout(() => {
    toast.classList.remove("show")
    toast.classList.add("hide")

    // 在消失动画完成后,从页面移除通知元素
    toast.addEventListener('transitionend', () => toast.remove())
  }, disappear)
}

/**
 * 快捷发送通知
 */
const Toast = {
  default: (message, element = null, disappear = 5000) => toast(message, "default", element, disappear),
  primary: (message, element = null, disappear = 5000) => toast(message, "primary", element, disappear),
  success: (message, element = null, disappear = 5000) => toast(message, "success", element, disappear),
  warning: (message, element = null, disappear = 5000) => toast(message, "warning", element, disappear),
  danger: (message, element = null, disappear = 5000) => toast(message, "danger", element, disappear)
}

const removeDialog = (dialog) => {
  dialog.classList.remove("show")
  dialog.classList.add("hide")

  // 在消失动画完成后,从页面移除通知元素
  dialog.addEventListener('transitionend', () => dialog.remove())
}

/**
 * 显示对话框
 * @param title {string} 标题
 * @param body {Element} 内容
 * @param isModel {boolean} 是否为模态对话框
 * @return {HTMLElement} 返回对话框实例,以便 remove()
 */
const showDialog = (title, body, isModel = false) => {
  const dialog = document.createElement('div')
  const content = document.createElement('div')
  const header = document.createElement('div')
  const dialogBody = document.createElement('div')

  dialog.classList.add("dialog", "essenceX")
  content.classList.add("dialog-content", "essenceX")
  header.classList.add("dialog-header", "essenceX")
  dialogBody.classList.add("dialog-body", "essenceX")

  header.textContent = title
  dialogBody.appendChild(body)

  content.append(header, dialogBody)

  dialog.appendChild(content)

  document.body.appendChild(dialog)

  // 使用动画显示对话框
  setTimeout(() => dialog.classList.add("show"), 10)

  // 不是模态对话框时,点击外部即取消
  if (!isModel) {
    window.addEventListener("click", event => {
      if (event.target === dialog) {
        removeDialog(dialog)
      }
    })
  }

  return dialog
}