AtCoder Standings AC Time Formatter

AtCoderのコンテスト順位表に表示されるAC時間を "Dd H:MM:SS" にフォーマットする

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         AtCoder Standings AC Time Formatter
// @namespace    bo9chan
// @version      1.0.0
// @description  AtCoderのコンテスト順位表に表示されるAC時間を "Dd H:MM:SS" にフォーマットする
// @author       bo9chan
// @supportURL   https://github.com/bo9chan/atcoder-standings-ac-time-formatter
// @match        https://atcoder.jp/contests/*/standings*
// @exclude      https://atcoder.jp/contests/*/standings/json
// @license      MIT
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    /**
     * @param {string} timeStr 時間を"MM:SS"の形式で指定した文字列
     * @returns {string} 時間を "Dd H:MM:SS", "H:MM:SS" または "M:SS" の形式で指定した文字列
     */
    function convertToDHMS(timeStr) {
        const [totalMinutes, seconds] = timeStr.split(':').map(Number);
        const secondsStr = String(seconds).padStart(2, '0');
        const minutes = totalMinutes % 60;
        const minutesStr = String(minutes).padStart(2, '0');
        const hours = Math.floor(totalMinutes / 60) % 24;
        const days = Math.floor(totalMinutes / (24 * 60));
        if (days > 0) {
            return `${days}d ${hours}:${minutesStr}:${secondsStr}`;
        } else if (hours > 0) {
            return `${hours}:${minutesStr}:${secondsStr}`;
        } else {
            return `${minutes}:${secondsStr}`;
        }
    }

    let targetNode = null;

    const handleMutation = () => {
        const selector1 = 'tr > td.standings-result > p:last-child';
        const selector2 = 'tr.standings-fa > td > p:last-child';
        // AC時間の要素を取得
        for (const selector of [selector1, selector2]) {
            const timeElements = targetNode.querySelectorAll(selector);
            if (timeElements.length > 0) {
                // AC時間のフォーマットを変換
                timeElements.forEach(el => {
                    const originalTime = el.textContent.trim();
                    if (originalTime.split(':').length == 2) {
                        const convertedTime = convertToDHMS(originalTime);
                        el.textContent = convertedTime;
                    }
                });
            }
        }
    };

    const parentHandleMutation = () => {
        targetNode = document.getElementById('standings-tbody');
        if (targetNode) {
            parentObserver.disconnect();
            handleMutation();
            const config = { childList: true, subtree: true, characterData: true };
            const observer = new MutationObserver(handleMutation);
            observer.observe(targetNode, config);
        }
    }

    const parentTargetNode = document.getElementById('vue-standings');
    const parentConfig = { childList: true, subtree: true };
    const parentObserver = new MutationObserver(parentHandleMutation);
    parentObserver.observe(parentTargetNode, parentConfig);
})();