JM Shelf - Utils

normalizeTag/sigmoid/clamp 等工具函数 — JM Shelf 推荐脚本的模块库,通过 @require 被主脚本引用。

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.sleazyfork.org/scripts/581099/1842598/JM%20Shelf%20-%20Utils.js

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         JM Shelf - Utils
// @namespace    jmshelf-lib
// @version      1.0.0
// @author       Kesdi
// @description  normalizeTag/sigmoid/clamp 等工具函数 — JM Shelf 推荐脚本的模块库,通过 @require 被主脚本引用。
// @license      MIT
// ==/UserScript==
// 
// 此文件是 GreasyFork 库(library),不直接安装。
// 请安装主脚本: JM Shelf 给杂鱼的个性化推荐
//

// ═══ [2] UTILS ═══
  function normalizeTag(t) {
  const _ntCache = normalizeTag._cache || (normalizeTag._cache = {});
      if (!t) return t;
    // 逐字转换 (TAG_NORMALIZE, ~490常用字)
    let result = '';
    let hadHit = false, hadMiss = false;
    for (const ch of t) {
      const v = TAG_NORMALIZE[ch];
      if (v !== undefined) { result += v; hadHit = true; }
      else { result += ch; hadMiss = true; }
    }
    // 如果有未命中且OpenCC可用, 提交异步转换缓存
    if (hadMiss && _occ) {
      if (_ocCache[t] === undefined) {
        _ocCache[t] = result; // placeholder, 本次返回逐字结果
        Promise.resolve(_occ(t)).then(r => { _ocCache[t] = r; }).catch(() => {});
      } else if (_ocCache[t] !== result) {
        return _ocCache[t]; // 缓存已就绪
      }
    }
    _ntCache[t] = result;
  return result.toLowerCase();
  }
  function normalizeTags(arr) { return [...new Set((arr || []).map(t => normalizeTag(t)))]; }

  function sleep(ms) {
    return new Promise(r => setTimeout(r, ms));
  }

  function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
  function sigmoid(x, center, steep) { return 1 / (1 + Math.exp(-steep * (x - center))); }

  function parseViewCount(str) {
    str = str.replace(/,/g, '').trim();
    if (str.endsWith('K')) return Math.round(parseFloat(str) * 1000);
    if (str.endsWith('M')) return Math.round(parseFloat(str) * 1000000);
    if (str.endsWith('B')) return Math.round(parseFloat(str) * 1000000000);
    return parseInt(str, 10) || 0;
  }

  // Migrate blacklist sets from JSON
  // (Sets don't survive JSON.stringify, so we store arrays)
  function getBlacklist() {
    const raw = Storage.get('blacklist');
    if (!raw) return { tags: [], albums: [] };
    return raw;
  }
  function setBlacklist(b) {
    Storage.set('blacklist', { tags: b.tags, albums: b.albums });
  }