AtCoder Standings AC Time Formatter

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         AtCoder Standings AC Time Formatter
// @namespace    bo9chan
// @version      1.0.1
// @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();
            const config = { childList: true, subtree: true };
            const observer = new MutationObserver((mutations, obs) => {
                // 再帰トリガーを止める
                obs.disconnect();
                // フォーマット処理を実行
                handleMutation();
                // 監視を再開
                obs.observe(targetNode, config);
            });
            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);
})();