Greasy Fork

Greasy Fork is available in English.

OP.GG搜索增强

优化OP.GG页面的英雄搜索功能,支持使用中文诙谐名

当前为 2025-02-02 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         OP.GG搜索增强
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  优化OP.GG页面的英雄搜索功能,支持使用中文诙谐名
// @author       You
// @match        https://www.op.gg/modes/*
// @icon         https://opgg-static.akamaized.net/meta/images/lol/latest/champion/Jinx.png?image=e_upscale,c_crop,h_103,w_103,x_9,y_9/q_auto:good,f_webp,w_160,h_160
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    const HERO_LIST_KEY = 'HeroList';
    const HERO_LIST_VERSION_KEY = "HeroListVersion";
    const LAST_UPDATE_TIME_KEY = "LastUpdateTime";
    const ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000; // 1天的毫秒数
    let heroListData = [];
    let currentUrl = window.location.href;

    // 在页面加载时检查并更新英雄列表
    checkAndUpdateHeroList();

    // 添加手动更新菜单命令
    GM_registerMenuCommand('手动更新英雄搜索映射表', updateHeroList);

    // 监听 URL 变化,spa跳转时,重新执行run
    observeUrlChange();

    // 主逻辑
    run();

    // 主逻辑
    async function run() {
        console.log("[TamOPGG]run")

        // 初始化英雄列表数据
        heroListData = GM_getValue(HERO_LIST_KEY, []);
        await performDOMReplacement();
        // 隐藏广告、无关内容
        hiddenAd();

        try {
            const searchInput = await waitForElement("#cloneSearchInput")
            searchInput.addEventListener('input', debounce(handleInput, 500));
        } catch (e) {
            console.error("[TamOPGG]run", e)
        }
    };

    // 监听 URL 变化
    function observeUrlChange() {
        const observer = new MutationObserver(() => {
            const newUrl = window.location.href;
            if (newUrl !== currentUrl) {
                console.log("[TamOPGG] URL 发生变化,重新执行 run 函数");
                currentUrl = newUrl;
                run();
            }
        });

        // 监听 body 的变化(包括 URL 变化)
        observer.observe(document.body, { childList: true, subtree: true });
    }

    function handleInput() {
        // const championRankingMap = getChampionRankingMap();
        const inputValue = this.value.toLowerCase();
        // 根据输入的内容过滤匹配的英雄
        console.log("[TamOPGG]inputValue: ", inputValue)

        // 英雄选项卡
        const trList = document.querySelectorAll("#content-container aside table tbody tr")
        // 点击排行(强度 or 胜率)的时候,会改变index。
        // 直接每次都从img.src获取位置
        const imgList = document.querySelectorAll("#content-container aside table tbody tr img.bg-image")
        const matchAlias = [];
        heroListData.filter(({ keywords, alias }) => {
            const isMatchHero = keywords?.includes(inputValue);
            if (isMatchHero) {
                matchAlias.push(alias.toLowerCase())
            }
        });
        console.log("[TamOPGG]matchAlias: ", matchAlias)


        imgList.forEach((el, i) => {
            const curHeroDom = trList[i];
            const isMatchHero = matchAlias.includes(getAlisaByImgSrc(el.src));

            if (isMatchHero) {
                curHeroDom.style.display = "table-row"
            } else {
                curHeroDom.style.display = "none";
            }
        })
    }

    // 获取img.src上面的alias
    function getAlisaByImgSrc(url) {
        const regex = /\/([^\/]+)\.png/;
        const match = url.match(regex);
        return match[1].toLowerCase();
    }

    // 替换原来的input
    async function performDOMReplacement() {
        // 获取 search 输入框
        console.log("[TamOPGG]performDOMReplacement")

        try {
            const searchInput = await waitForElement('#filterChampionInput');
            // console.log('[TamOPGG]Element found:', searchInput);
            // 克隆输入框
            const clonedInput = searchInput.cloneNode(true);
            clonedInput.id = "cloneSearchInput";
            // 替换原输入框
            searchInput.parentNode.replaceChild(clonedInput, searchInput);
            // 在这里添加你要执行的操作
        } catch (error) {
            console.error(error);
        }
    }

    // 隐藏无关内容、广告
    function hiddenAd() {
        console.log("[TamOPGG]关闭广告");
        try {
            const adSelector = [".page-title", "#opgg-kit-house-image-banner", ".banner-container"];
            adSelector.forEach(selector => {
                waitForElements(selector).then(els => {
                    els.forEach(el => {
                        el.style.display = "none";
                    })
                })
            })
        } catch (e) {
            console.error("[TamOPGG]关闭广告", e)
        }
    }

    // 检查并更新英雄列表
    function checkAndUpdateHeroList() {
        const lastUpdateTime = GM_getValue(LAST_UPDATE_TIME_KEY, 0);
        const currentTime = Date.now();
        const localVersion = GM_getValue(HERO_LIST_VERSION_KEY, '');

        if (currentTime - lastUpdateTime > ONE_DAY_IN_MILLIS || !localVersion) {
            updateHeroList();
        } else {
            console.log('[TamOPGG] 英雄列表1天内已更新过,跳过(如需更新请手动)。');
        }
    }

    // 更新英雄列表
    function updateHeroList() {
        const url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js';

        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function (response) {
                if (response.status === 200) {
                    const {hero, version} = JSON.parse(response.responseText);
                    const lastVersion = GM_getValue(HERO_LIST_VERSION_KEY);
                    if(lastVersion === version) {
                        console.log('[TamOPGG] 英雄列表已是最新,版本号:', version);
                        return;
                    };

                    const heroList = hero.map(({ alias, keywords, name }) => ({
                        name,
                        alias,
                        keywords
                    }))
                    GM_setValue(HERO_LIST_KEY, heroList);
                    GM_setValue(HERO_LIST_VERSION_KEY, version);
                    GM_setValue(LAST_UPDATE_TIME_KEY, Date.now());
                    console.log(`[TamOPGG] 英雄列表更新成功,版本号: ${version}`);
                    console.log('[TamOPGG] 更新后的英雄列表:', heroList);
                } else {
                    console.error('[TamOPGG] 获取英雄列表失败:', response.statusText);
                }
            },
            onerror: function (error) {
                console.error('[TamOPGG] 请求出错:', error);
            },
        });
    }

    function waitForElement(selector, { timeout = 5000, single = true } = {}) {
        return new Promise((resolve, reject) => {
            const startTime = Date.now();
            const observer = new MutationObserver((mutationsList) => {
                for (const mutation of mutationsList) {
                    if (mutation.type === 'childList') {
                        let result;
                        result = single ? document.querySelector(selector) : document.querySelectorAll(selector);

                        if ((single && result) || (!single && result.length > 0)) {
                            observer.disconnect();
                            resolve(result);
                            return;
                        }
                    }
                }
                if (Date.now() - startTime > timeout) {
                    observer.disconnect();
                    reject(new Error(`[enhancer]waitForElement: 元素"${selector}"没找到,within ${timeout}ms.`));
                }
            });

            const config = { childList: true, subtree: true };
            observer.observe(document.documentElement, config);
        });
    }

    function waitForElements(selector, { timeout = 5000 } = {}) {
        return waitForElement(selector, { timeout, single: false })
    }

    // 防抖:多次触发只执行最后一次
    function debounce(cb, delay) {
        let timer = null;

        return function (...args) {
            timer && clearTimeout(timer);
            timer = setTimeout(() => {
                cb.apply(this, args);
            }, delay)
        }
    }
})();