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 });
  }