Greasy Fork

来自缓存

Greasy Fork is available in English.

bsn-libs

工具箱

当前为 2024-12-10 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/520145/1500045/bsn-libs.js

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

/** 分组 */
window.groupBy = function (arr, predicate) {
  const obj = arr.reduce((acc, obj) => {
    const key = predicate(obj) ?? '';
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
  return Object.keys(obj).map(x => ({
    key: x,
    items: obj[x]
  }));
};

/** 睡眠 */
window.sleep = function (time) {
  return new Promise(resolve => setTimeout(resolve, time));
};

/** 设置粘贴板文字 */
window.setClipboardText = async function (data) {
  if (navigator.clipboard && navigator.clipboard.writeText) {
    await navigator.clipboard.writeText(data);
  }
};

/** 获取粘贴板文字 */
window.getClipboardText = async function () {
  if (navigator.clipboard && navigator.clipboard.readText) {
    const text = await navigator.clipboard.readText();
    return text;
  }
  return '';
};

/** 查找所有满足条件的元素 */
window.findAll = function (options) {
  const { selectors, parent, findTarget } = options;
  const parentEl =
    parent && parent.tagName.toLocaleLowerCase() === 'iframe'
      ? parent.contentDocument.body
      : parent;
  const eles = Array.from((parentEl ?? document.body).querySelectorAll(selectors));
  return findTarget ? eles.map((el, index) => findTarget(el, index)).filter(x => x) : eles;
};

/** 查找第一个满足条件的元素 */
window.find = function (options) {
  const eles = window.findAll(options);
  return eles.length > 0 ? eles[0] : null;
};

/** 查找最后一个满足条件的元素 */
window.findLast = function (options) {
  const eles = window.findAll(options);
  return eles.length > 0 ? eles[eles.length - 1] : null;
};

/** 模拟操作 */
window.simulateOperate = async function (actions) {
  for (const action of actions) {
    switch (action.type) {
      case 'sleep':
        await sleep(action.time);
        break;
      case 'focus':
      case 'input':
      case 'click':
        const { selectors, value, parent, waiting, findTarget } = action;
        if (waiting) await sleep(waiting);
        const parentEl =
          parent && parent.tagName.toLocaleLowerCase() === 'iframe'
            ? parent.contentDocument.body
            : parent;
        const eles = Array.from((parentEl ?? document.body).querySelectorAll(selectors));
        const targets = findTarget
          ? eles.map((el, index) => findTarget(el, index)).filter(x => x)
          : eles;
        if (targets.length > 0) {
          const target = targets[0];
          if (action.type === 'focus' || action.focusable) target.focus();
          if (action.type === 'input') {
            target.value = value;
            target.dispatchEvent(new Event('keydown'));
            target.dispatchEvent(new Event('keypress'));
            target.dispatchEvent(new Event('input'));
            target.dispatchEvent(new Event('keyup'));
            target.dispatchEvent(new Event('change'));
          } else if (action.type === 'click') {
            target.click();
          }
        }
        break;
    }
  }
};

/** 创建naive对话框(增加异步功能且只能在组件的setup函数里调用) */
window.createNaiveDialog = function () {
  const dialog = naive.useDialog();
  ['create', 'error', 'info', 'success', 'warning'].forEach(x => {
    dialog[x + 'Async'] = options => {
      return new Promise(resolve => {
        dialog[x]({
          ...options,
          onNegativeClick: () => resolve(false),
          onPositiveClick: () => resolve(true)
        });
      });
    };
  });
  return dialog;
};

/** 初始化Vue3(包括naive及自定义BTable组件) */
window.initVue3 = function (com) {
  const style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = `
  body {
    text-align: left;
  }
  .app-wrapper .btn-toggle {
    position: fixed;
    top: 50vh;
    right: 0;
    padding-left: 12px;
    padding-bottom: 4px;
    transform: translateX(calc(100% - 32px)) translateY(-50%);
  }
  .drawer-wrapper .n-form {
    margin: 0 8px;
  }
  .drawer-wrapper .n-form .n-form-item {
    margin: 8px 0;
  }
  .drawer-wrapper .n-form .n-form-item .n-space {
    flex: 1;
  }
  .drawer-wrapper .n-form .n-form-item .n-input-number {
    width: 100%;
  }
    `;
  document.getElementsByTagName('head').item(0).appendChild(style);
  const el = document.createElement('div');
  el.innerHTML = `<div id="app" class="app-wrapper"></div>`;
  el.style.backgroundColor = 'transparent';
  el.style.border = 'none';
  document.body.append(el);
  el.popover = 'manual';
  el.showPopover();

  const BTable = {
    template: `
    <table cellspacing="0" cellpadding="0">
      <tr v-for="(row, rowIndex) in rows">
        <td v-for="cell in row" :rowspan="cell.rowspan" :colspan="cell.colspan" :width="cell.width" :class="cell.class">
          <slot :cell="cell">{{cell.value}}</slot>
        </td>
      </tr>
    </table>
      `,
    props: {
      rowCount: Number,
      columns: Array, // [{ key: "", label: "", width: "100px", unit: "", editable: false }]
      cells: Array // [{ row: 0, col: 0, rowspan: 1, colspan: 1, value: "", useColumnLabel: false }]
    },
    setup(props) {
      const data = Vue.reactive({
        rows: Vue.computed(() => {
          const arr1 = [];
          for (let i = 0; i < props.rowCount; i++) {
            const arr2 = [];
            for (let j = 0; j < props.columns.length; j++) {
              const column = props.columns[j];
              const cell = props.cells.find(x => x.row === i && x.col === j);
              if (cell) {
                const colspan = cell.colspan ?? 1;
                arr2.push({
                  ...cell,
                  rowspan: cell.rowspan ?? 1,
                  colspan: colspan,
                  value: cell.useColumnLabel ? column.label : cell.value,
                  width: colspan > 1 ? undefined : column.width,
                  column: column
                });
              }
            }
            arr1.push(arr2);
          }
          return arr1;
        })
      });
      return data;
    }
  };
  const app = Vue.createApp({
    template: `
<n-dialog-provider>
  <n-message-provider>
    <n-button v-if="!showDrawer" class="btn-toggle" type="primary" round @click="showDrawer=true">
      <template #icon>⇆</template>
    </n-button>
  <n-drawer v-model:show="showDrawer" display-directive="show" resizable class="drawer-wrapper">
    <com @closeDrawer="showDrawer=false"/>
  </n-drawer>
  </n-message-provider>
</n-dialog-provider>
`,
    setup() {
      const data = Vue.reactive({
        showDrawer: false
      });
      return data;
    }
  });
  app.use(naive);
  app.component('b-table', BTable);
  app.component('com', com);
  app.mount('#app');
};