Greasy Fork

Greasy Fork is available in English.

晋江章节修改高亮

高亮修改过的章节,区别首发时间和更新时间

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         晋江章节修改高亮
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  高亮修改过的章节,区别首发时间和更新时间
// @author       oovz
// @match        *://www.jjwxc.net/onebook.php?novelid=*
// @exclude      *://www.jjwxc.net/onebook.php?novelid=*&chapterid=*
// @source       http://greasyfork.icu/en/scripts/536627-%E6%99%8B%E6%B1%9F%E7%AB%A0%E8%8A%82%E4%BF%AE%E6%94%B9%E9%AB%98%E4%BA%AE
// @source       https://gist.github.com/oovz/e1f36ff2bf18c0ab64180e785f837a12
// @license      MIT
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';
    
    const HIGHLIGHT_COLOR = '#FFFACD'; // Light yellow
    const TOGGLE_BUTTON_ID = 'toggle-chapter-highlight';
    const STORAGE_KEY = 'jjwxc-chapter-highlight-enabled';
    
    GM_addStyle(`
        .chapter-modified {
            background-color: ${HIGHLIGHT_COLOR} !important;
        }
        #${TOGGLE_BUTTON_ID} {
            margin-left: 10px;
            cursor: pointer;
        }
        #${TOGGLE_BUTTON_ID}:hover {
            text-decoration: underline;
        }
        .highlight-tooltip {
            position: relative;
            display: inline-block;
        }
        .highlight-tooltip .tooltip-text {
            visibility: hidden;
            width: 200px;
            background-color: #FFF8DC;
            color: #000;
            text-align: center;
            border-radius: 3px;
            border: 1px solid #DEB887;
            padding: 3px 5px;
            position: absolute;
            z-index: 1000;
            bottom: 125%;
            left: 50%;
            margin-left: -100px;
            opacity: 0;
            transition: opacity 0.3s;
            font-size: 12px;
        }
        .highlight-tooltip:hover .tooltip-text {
            visibility: visible;
            opacity: 1;
        }
    `);
    
    // State
    // highlighting is enabled by default
    const storedValue = localStorage.getItem(STORAGE_KEY);
    let isHighlightEnabled = storedValue === null || storedValue === 'true'; // Default to enabled if not set
    
    /**
     * Find all chapter rows in the document
     * @returns {NodeList} List of chapter rows
     */
    function findChapterRows() {
        // latest updated chapter will have itemprop="chapter newestChapter"
        return document.querySelectorAll('tr[itemprop*="chapter"]');
    }
    
    /**
     * Extract the publish time from a chapter row
     * @param {Element} chapterRow - The TR element representing a chapter
     * @returns {string|null} The extracted publish time or null if not found
     */
    function getPublishTimeFromChapter(chapterRow) {
        try {
            const titleCell = chapterRow.querySelector("td[title]");
            if (!titleCell || !titleCell.title) {
                return null;
            }
            
            // Title will contain both store and publish times
            // '章节存稿时间:2022-09-09 16:23:42\n章节首发时间:2022-09-09 16:25:00'
            const titleText = titleCell.title;
            
            // Extract the publish time after "章节首发时间:"
            const publishTimeMatch = titleText.match(/章节首发时间:([^\n]+)/);
            if (publishTimeMatch && publishTimeMatch[1]) {
                return publishTimeMatch[1].trim();
            }
        } catch (e) {
            console.warn('Error extracting publish time:', e);
        }
        return null; // No publish time found
    }
    
    /**
     * Extract the update time from a chapter row
     * @param {Element} chapterRow - The TR element representing a chapter
     * @returns {string|null} The extracted update time or null if not found
     */
    function getUpdateTimeFromChapter(chapterRow) {
        try {
            return chapterRow.querySelector("td[title] > span:not([id='latestUpdates'])").textContent.trim();
        } catch (e) {
            console.warn('Error extracting update time:', e);
        }
        return null; // No update time found
    }
    
    /**
     * Apply highlighting to chapters with different publish vs update times
     */
    function highlightModifiedChapters() {
        const chapterRows = findChapterRows();
        let modifiedCount = 0;
        
        chapterRows.forEach(row => {
            try {
                const publishTime = getPublishTimeFromChapter(row);
                const updateTime = getUpdateTimeFromChapter(row);
                
                if (publishTime && updateTime && publishTime !== updateTime) {
                    // Different times - highlight the row
                    row.classList.add('chapter-modified');
                    // Store original classes if needed for toggling
                    if (!row.dataset.originalBg) {
                        row.dataset.originalBg = 'saved';
                    }
                    modifiedCount++;
                }
            } catch (e) {
                console.warn('Error processing chapter row:', e);
            }
        });
        
        console.log(`Found ${modifiedCount} modified chapters out of ${chapterRows.length} total`);
    }
    
    /**
     * Remove all highlighting from chapter rows
     */
    function removeHighlights() {
        const chapterRows = findChapterRows();
        chapterRows.forEach(row => {
            row.classList.remove('chapter-modified');
        });
    }
    
    /**
     * Create and insert the toggle button
     */
    function createToggleButton() {
        // Find the second tr element in the document
        const secondRow = document.querySelector('tbody > tr:nth-of-type(2) > td');
        
        if (!secondRow) {
            console.error('Cannot insert toggle button');
        } else {
            // Create toggle button wrapper with tooltip
            const wrapper = document.createElement('span');
            wrapper.className = 'highlight-tooltip';
            wrapper.style.marginLeft = '10px';
            
            const button = createButton();
            wrapper.appendChild(button);
            
            const tooltip = document.createElement('span');
            tooltip.className = 'tooltip-text';
            tooltip.textContent = '高亮显示更新时间与首发时间不同的章节';
            wrapper.appendChild(tooltip);
            
            // Insert the button at the end of the second row
            secondRow.appendChild(wrapper);
        }
    }
    
    /**
     * Create the toggle button element
     * @returns {HTMLElement} The created button
     */
    function createButton() {
        // Create a link that looks like a button
        const button = document.createElement('a');
        button.id = TOGGLE_BUTTON_ID;
        button.href = 'javascript:void(0);'; // Make it clickable but not navigate
        button.textContent = isHighlightEnabled ? '[插件:关闭章节高亮]' : '[插件:开启章节高亮]';
        button.style.color = '#FF0000'; // Red text color
        button.style.textDecoration = 'none'; // No underline
        
        // Add click handler
        button.addEventListener('click', handleToggleClick);
        
        return button;
    }
    
    /**
     * Handle toggle button click - toggle highlighting on/off
     */
    function handleToggleClick() {
        isHighlightEnabled = !isHighlightEnabled;
        
        const button = document.getElementById(TOGGLE_BUTTON_ID);
        if (button) {
            button.textContent = isHighlightEnabled ? '[插件:关闭章节高亮]' : '[插件:开启章节高亮]';
        }
        
        if (isHighlightEnabled) {
            highlightModifiedChapters();
        } else {
            removeHighlights();
        }
        
        try {
            localStorage.setItem(STORAGE_KEY, isHighlightEnabled ? 'true' : 'false');
        } catch (e) {
            console.warn('Could not save highlight preference to localStorage', e);
        }
    }
    
    /**
     * Initialize the script
     */
    function initialize() {
        console.log('Initializing chapter highlight script...');
        createToggleButton();
        if (isHighlightEnabled) {
            highlightModifiedChapters();
        }
    }
    
    // Wait for the DOM to be ready before initializing
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();