Greasy Fork

来自缓存

Greasy Fork is available in English.

Bangumi 评分隐藏

在首页、条目页、搜索页、标签页、浏览页和人物作品页隐藏全站评分

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Bangumi 评分隐藏
// @namespace    https://bgm.tv/user/662064
// @version      2.4.0
// @description  在首页、条目页、搜索页、标签页、浏览页和人物作品页隐藏全站评分
// @author       板斧青凤
// @match        https://bgm.tv/subject/*
// @match        https://bangumi.tv/subject/*
// @match        https://chii.in/subject/*
// @match        https://bgm.tv/subject_search/*
// @match        https://bangumi.tv/subject_search/*
// @match        https://chii.in/subject_search/*
// @match        https://bgm.tv/
// @match        https://bangumi.tv/
// @match        https://chii.in/
// @match        https://bgm.tv/*/tag/*
// @match        https://bangumi.tv/*/tag/*
// @match        https://chii.in/*/tag/*
// @match        https://bgm.tv/*/browser*
// @match        https://bangumi.tv/*/browser*
// @match        https://chii.in/*/browser*
// @match        https://bgm.tv/person/*/works*
// @match        https://bangumi.tv/person/*/works*
// @match        https://chii.in/person/*/works*
// @match        https://bgm.tv/user/*/timeline
// @match        https://bangumi.tv/user/*/timeline
// @match        https://chii.in/user/*/timeline
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
  'use strict';

  /*** ---------------- 全局状态与工具函数 ---------------- ***/

  // 存放页面中被隐藏或显示的评分相关元素
  let elementsToToggle = [];

  // 存放所有用于切换评分显隐的按钮引用
  const allToggleButtons = new Set();

  // 当前评分是否处于隐藏状态
  let isHidden = true;

  /**
   * 创建一个按钮或链接元素。
   * @param {string} text - 按钮文字。
   * @param {boolean} isInlineAnchor - 是否创建为 a 标签(用于 dock 内的样式)。
   */
  function createButtonElement(text, isInlineAnchor = false) {
    let el;
    if (isInlineAnchor) {
      el = document.createElement('a');
      el.href = 'javascript:void(0);';
      el.textContent = text;
      el.className = 'toggle-rating';
    } else {
      el = document.createElement('button');
      el.textContent = text;
      el.style.cssText = 'display:block;margin:10px auto;text-align:center;';
    }
    return el;
  }

  /**
   * 更新所有按钮的显示文字,与当前显隐状态保持一致。
   */
  function updateButtonsText() {
    const text = isHidden ? '显示评分' : '隐藏评分';
    allToggleButtons.forEach(btn => {
      btn.textContent = text;
    });
  }

  /**
   * 将所有评分元素设置为隐藏。
   */
  function ensureHiddenOnce() {
    if (!elementsToToggle.length) return;
    elementsToToggle.forEach(el => {
      el.style.display = 'none';
    });
    isHidden = true;
    updateButtonsText();
  }

  /**
   * 切换评分元素的显隐状态,并更新所有按钮文字。
   */
  function toggleAll() {
    if (!elementsToToggle.length) return;
    const nextHidden = !isHidden;
    elementsToToggle.forEach(el => {
      el.style.display = nextHidden ? 'none' : '';
    });
    isHidden = nextHidden;
    updateButtonsText();
  }

  /**
   * 注册按钮点击事件,使其能与其他按钮保持状态同步。
   * @param {HTMLElement} btn - 需要注册的按钮。
   */
  function registerButton(btn) {
    btn.addEventListener('click', toggleAll);
    allToggleButtons.add(btn);
    updateButtonsText();
  }

  /**
   * 监听并等待指定选择器元素出现。
   * @param {string} selector - 要等待的选择器。
   * @param {object} [options] - 监听选项。
   * @returns {Promise<Element>} - 返回首次出现的元素。
   */
  function waitFor(selector, { root = document, once = true } = {}) {
    return new Promise(resolve => {
      const found = root.querySelector(selector);
      if (found) {
        resolve(found);
        return;
      }
      const obs = new MutationObserver(() => {
        const el = root.querySelector(selector);
        if (el) {
          if (once) obs.disconnect();
          resolve(el);
        }
      });
      obs.observe(root === document ? document.documentElement : root, {
        childList: true,
        subtree: true
      });
    });
  }

  /**
   * 查找并隐藏指定选择器对应的评分元素。
   * 在评分元素加载完成后会自动调用隐藏逻辑。
   * @param {string} selectors - CSS 选择器。
   */
  function observeTargetsAndHide(selectors) {
    return new Promise(resolve => {
      const tryCollect = () => {
        const nodes = Array.from(document.querySelectorAll(selectors));
        if (nodes.length > 0) {
          elementsToToggle = nodes;
          ensureHiddenOnce();
          resolve(nodes);
          return true;
        }
        return false;
      };

      if (tryCollect()) return;

      const obs = new MutationObserver(() => {
        if (tryCollect()) obs.disconnect();
      });
      obs.observe(document.documentElement, { childList: true, subtree: true });
    });
  }

  /*** ---------------- 页面类型判断与处理 ---------------- ***/

  const path = window.location.pathname;

  const isSubject = /\/subject\/\d+/.test(path);
  const isTimelineOrHome = /^\/user\/.*\/timeline/.test(path) || path === '/';
  const isSearchOrTagOrBrowserOrWorks =
    /subject_search/.test(path) ||
    /\/tag\//.test(path) ||
    /\/browser/.test(path) ||
    /\/person\/\d+\/works/.test(path);

  // 每种页面类型对应的评分选择器与按钮插入方式
  let selectors = '';
  const originalButtonsInstallers = [];

  // 条目页评分区域处理
  if (isSubject) {
    selectors = 'div[class*="global_rating"], #ChartWarpper';
    originalButtonsInstallers.push(async () => {
      const ratingDiv = await waitFor('div[rel="v:rating"]');
      if (ratingDiv && ratingDiv.parentNode) {
        const btn = createButtonElement('显示评分', false);
        ratingDiv.parentNode.insertBefore(btn, ratingDiv);
        registerButton(btn);
      }
    });
  }

  // 时间线和首页评分区域处理
  else if (isTimelineOrHome) {
    selectors = '.starstop-one, .fade';
    originalButtonsInstallers.push(async () => {
      const tabs = await waitFor('#timelineTabs');
      if (tabs) {
        const li = document.createElement('li');
        const btn = createButtonElement('显示评分', false);
        li.appendChild(btn);
        tabs.appendChild(li);
        registerButton(btn);
      }
    });
  }

  // 搜索页、标签页、浏览页和人物作品页评分区域处理
  else if (isSearchOrTagOrBrowserOrWorks) {
    selectors = '.starstop-s, .fade';
    originalButtonsInstallers.push(async () => {
      const tools = await waitFor('#browserTools');
      if (tools) {
        const btn = createButtonElement('显示评分', false);
        btn.style.margin = '10px';
        btn.style.display = '';
        tools.appendChild(btn);
        registerButton(btn);
      }
    });
  }

  /**
   * 在 dock 中追加“显示评分/隐藏评分”按钮。
   * 按钮位于第一个 li.first 元素的后方,表现为内联链接。
   */
  async function installDockButton() {
    const dock = await waitFor('#dock');
    if (!dock) return;

    const firstLi = dock.querySelector('ul.clearit > li.first');
    if (!firstLi) return;

    const sep = document.createTextNode(' | ');
    const dockBtn = createButtonElement('显示评分', true);

    firstLi.appendChild(sep);
    firstLi.appendChild(dockBtn);
    registerButton(dockBtn);
  }

  /*** ---------------- 启动主逻辑 ---------------- ***/

  (async function init() {
    if (!selectors) return;

    // 监听评分元素并隐藏
    await observeTargetsAndHide(selectors);

    // 在页面原位置插入按钮
    for (const installer of originalButtonsInstallers) {
      try {
        await installer();
      } catch (e) {
        // 忽略个别插入失败
      }
    }

    // 在 dock 中插入按钮
    try {
      await installDockButton();
    } catch (e) {
      // 忽略异常
    }
  })();
})();