Greasy Fork

Greasy Fork is available in English.

Kemono 更新标记 (动态天数彩色版-原尺寸)

根据天数为更新附加不同颜色标记,保持原推荐尺寸。

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

// ==UserScript==
// @name              Kemono 更新標示 (動態天數彩色版-原尺寸)
// @name:zh-TW        Kemono 更新標示 (動態天數彩色版-原尺寸)
// @name:zh-CN        Kemono 更新标记 (动态天数彩色版-原尺寸)
// @namespace         http://greasyfork.icu/users/1051751-mark-levi
// @version           2.2.1
// @description       Highlights recent posts on Kemono with different colors based on days. Original recommended size.
// @description:zh-TW 根據天數為更新附加不同顏色標示,保持原推薦尺寸。
// @description:zh-CN 根据天数为更新附加不同颜色标记,保持原推荐尺寸。
// @author            Mark
// @match             https://kemono.cr/account/favorites/artists
// @grant             none
// @run-at            document-idle
// @license           MIT
// ==/UserScript==

(function() {
    'use strict';

    const prefix = '[KemonoCR]';
    const log = (...args) => console.log(prefix, ...args);

    // --- 日期處理函式 ---
    const toYMD = (date) => {
        const y = date.getFullYear();
        const m = String(date.getMonth() + 1).padStart(2, '0');
        const d = String(date.getDate()).padStart(2, '0');
        return `${y}-${m}-${d}`;
    };

    const today = new Date();
    const todayStr = toYMD(today);

    // 計算天數差的函式
    function getDayDiff(dateStr) {
        const postDate = new Date(dateStr);
        // 清除時間部分,只比較日期
        const todayNoTime = new Date(todayStr);
        const postDateNoTime = new Date(dateStr);

        const diffTime = todayNoTime - postDateNoTime;
        const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
        return diffDays;
    }

    log('Dynamic day calculator with color coding initialized');

    /**
     * 添加標記到元素(根據天數顯示不同顏色,原推薦尺寸)
     * @param {HTMLElement} el - time元素
     * @param {string} text - 要顯示的文字
     * @param {number} dayDiff - 天數差
     */
    function appendLabel(el, text, dayDiff) {
        const labelSpan = document.createElement('span');
        labelSpan.textContent = text;
        labelSpan.style.fontWeight = 'bold';
        labelSpan.style.marginLeft = '5px'; // 恢復原推薦尺寸

        // 根據天數設定不同顏色
        if (dayDiff === 0) {
            labelSpan.style.color = 'red';           // 今日:紅色
        } else if (dayDiff === 1) {
            labelSpan.style.color = 'orange';       // 1天前:橙色
        } else if (dayDiff === 2) {
            labelSpan.style.color = 'gold';         // 2天前:金色
        } else if (dayDiff === 3) {
            labelSpan.style.color = 'green';        // 3天前:綠色
        } else if (dayDiff === 4) {
            labelSpan.style.color = 'blue';         // 4天前:藍色
        } else if (dayDiff === 5) {
            labelSpan.style.color = 'purple';       // 5天前:紫色
        } else {
            labelSpan.style.color = '#666666';      // 6天以上:深灰色
        }

        el.appendChild(labelSpan);
    }

    /**
     * 處理單一 <time> 元素
     * @param {HTMLElement} el
     */
    function processElement(el) {
        // 如果已處理過,直接跳過
        if (el.dataset.kemonoProcessed) {
            return;
        }

        // 只取日期部分進行比對
        const dateText = (el.textContent || '').trim().split(' ')[0];

        // 計算天數差
        const dayDiff = getDayDiff(dateText);

        if (dayDiff === 0) {
            // 今日:紅色標記
            appendLabel(el, '今日更新', dayDiff);
            log('  -> Today:', dateText);
        } else if (dayDiff >= 1) {
            // 1天前以上:顯示具體天數,根據天數顯示不同顏色
            appendLabel(el, `${dayDiff}天前`, dayDiff);
            log('  -> Days ago:', dateText, `${dayDiff}天前`);
        }

        // 標記為已處理,避免重複執行
        el.dataset.kemonoProcessed = 'true';
    }

    /**
     * 在指定的節點內尋找並處理所有未處理的 <time.timestamp> 元素
     * @param {Node} targetNode
     */
    function findAndProcessInNode(targetNode) {
        if (targetNode.nodeType !== Node.ELEMENT_NODE) {
            return;
        }
        // 使用 :not 選擇器,高效地跳過已處理的元素
        const elements = targetNode.querySelectorAll('time.timestamp:not([data-kemono-processed])');
        if (elements.length > 0) {
            log(`Found ${elements.length} new element(s) to process.`);
            elements.forEach(processElement);
        }
    }

    // --- 腳本主邏輯 ---

    // 1. 頁面初次載入時,處理所有現存的元素
    log('Initial page scan...');
    findAndProcessInNode(document.body);
    log('Initial scan complete.');

    // 2. 設定 MutationObserver 來監控後續動態加入的內容
    const observer = new MutationObserver((mutationsList) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                // 只對新增加的節點進行掃描
                mutation.addedNodes.forEach(node => {
                    findAndProcessInNode(node);
                });
            }
        }
    });

    // 開始監聽 document.body 的子節點變化
    observer.observe(document.body, {
        childList: true, // 只關心子節點的增加或刪除
        subtree: true    // 監聽所有後代節點
    });

})();