Replace Dropdown Lists for Bangumi

调整页面上的下拉列表选项顺序,保留原本的默认值,并按首字母排序,并且使用懒加载功能

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Replace Dropdown Lists for Bangumi
// @name:zh-CN   bangumi下拉列表排序
// @namespace    https://github.com/Adachi-Git/ReplaceDropdownListsForBangumi
// @version      0.6
// @description  调整页面上的下拉列表选项顺序,保留原本的默认值,并按首字母排序,并且使用懒加载功能
// @author       Adachi
// @match        *://bangumi.tv/subject/*
// @match        *://bgm.tv/subject/*
// @match        *://chii.in/subject/*
// @grant        none
// @license MIT
// ==/UserScript==
(function() {
    'use strict';

    var delay = 2000; // 设置延迟执行时间,单位是毫秒

    // 汉字拼音首字母映射表
    var pinyinMap = {
        '原': 'Y',
        '总': 'Z',
        '导': 'D',
        '副': 'F',
        '脚': 'J',
        '分': 'F',
        '主': 'Z',
        '演': 'Y',
        '音': 'Y',
        '人': 'R',
        '构': 'G',
        '系': 'X',
        '美': 'M',
        '色': 'S',
        '机': 'J',
        '道': 'D',
        '作': 'Z',
        '动': 'D',
        '摄': 'S',
        'C': 'C',
        '3': '3',
        '监': 'J',
        '第': 'D',
        'O': 'O',
        '制': 'Z',
        '背': 'B',
        '数': 'S',
        '剪': 'J',
        '插': 'C',
        '企': 'Q',
        '宣': 'X',
        '录': 'L',
        '製': 'Z',
        '设': 'S',
        '特': 'T',
        '配': 'P',
        '联': 'L',
        '补': 'B',
        '执': 'Z',
        '助': 'Z',
        '台': 'T',
        '后': 'H',
        '协': 'X',
        '连': 'L',
        '译': 'Y',
        '客': 'K',
        '文': 'W',
        '出': 'C',
        '改': 'G',
        '前': 'Q',
        '续': 'X',
        '全': 'Q',
        '番': 'F',
        '相': 'X',
        '不': 'B',
        '衍': 'Y',
        '角': 'J',
        '其': 'Q',
        '开': 'K',
        '发': 'F',
        '游': 'Y',
        '剧': 'J',
        'S': 'S',
        '程': 'C',
        'Q': 'Q',
        '关': 'G',
        '创': 'C',
        '编': 'B',
        '共': 'G',
        '故': 'G',
        '艺': 'Y',
        '厂': 'C',
        '片': 'P',
        '印': 'Y',
        '广': 'G',
    };

    // 定义一个函数来处理下拉列表的逻辑
    function adjustSelectOptions(select) {
        // 保存原本的默认值
        var defaultValue = select.value;

        // 获取所有选项并转换为数组
        var optionsArray = Array.from(select.options);

        // 移除所有选项
        optionsArray.forEach(function(option) {
            select.remove(option.index);
        });

        // 按汉字的拼音首字母排序选项数组
        optionsArray.sort(function(a, b) {
            var pinyinA = pinyinMap[a.textContent[0]];
            var pinyinB = pinyinMap[b.textContent[0]];

            // 如果拼音首字母不存在,则将其视为 -Infinity,确保空值被放到列表的最上面
            pinyinA = pinyinA ? pinyinA : -Infinity;
            pinyinB = pinyinB ? pinyinB : -Infinity;

            // 排序时忽略空值
            if (pinyinA === -Infinity && pinyinB === -Infinity) {
                return 0;
            } else if (pinyinA === -Infinity) {
                return -1;
            } else if (pinyinB === -Infinity) {
                return 1;
            } else {
                return pinyinA.localeCompare(pinyinB);
            }
        });

        // 将重新排序后的选项重新添加到下拉列表中,并在选项文本前添加拼音首字母
        optionsArray.forEach(function(option) {
            var originalText = option.textContent;
            var pinyin = pinyinMap[originalText[0]];

            // 如果拼音首字母存在,则在文本前添加拼音首字母;否则只保留原始文本
            option.textContent = (pinyin ? pinyin + ' - ' : '') + originalText;
            select.add(option);
        });

        // 保留原本的默认值
        select.value = defaultValue;
    }

    // 延迟执行处理函数
    setTimeout(function() {
        // 查找可见的下拉列表并处理它们
        var selects = document.querySelectorAll('select[name^="infoArr"]:not([data-adjusted])');
        selects.forEach(function(select) {
            if (isElementInViewport(select)) {
                adjustSelectOptions(select);
                select.setAttribute('data-adjusted', 'true');
            }
        });
    }, delay);

    // 添加 DOM 变化的监听器
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            // 查找新增的下拉列表并处理
            var newSelects = mutation.target.querySelectorAll('select[name^="infoArr"]:not([data-adjusted])');
            newSelects.forEach(function(newSelect) {
                if (isElementInViewport(newSelect)) {
                    adjustSelectOptions(newSelect);
                    newSelect.setAttribute('data-adjusted', 'true');
                }
            });
        });
    });

    // 监听整个文档的变化
    observer.observe(document.documentElement, {
        childList: true,
        subtree: true
    });

    // 添加滚动事件监听器
    window.addEventListener('scroll', function() {
        // 查找尚未处理的下拉列表
        var selects = document.querySelectorAll('select[name^="infoArr"]:not([data-adjusted])');
        selects.forEach(function(select) {
            // 如果该下拉列表在视图内,进行处理并标记为已处理
            if (isElementInViewport(select)) {
                adjustSelectOptions(select);
                select.setAttribute('data-adjusted', 'true');
            }
        });
    });

    // 检查元素是否在视图内
    function isElementInViewport(element) {
        var rect = element.getBoundingClientRect();
        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
    }
})();