4d4y Markdown Enhancer

Convert potential Markdown syntax into HTML in 4d4y forum posts without removing existing HTML elements. Toggle original text with Ctrl+M, with a mode switch notification.

目前為 2025-02-07 提交的版本,檢視 最新版本

// ==UserScript==
// @name         4d4y Markdown Enhancer
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  Convert potential Markdown syntax into HTML in 4d4y forum posts without removing existing HTML elements. Toggle original text with Ctrl+M, with a mode switch notification.
// @match        https://www.4d4y.com/forum/*
// @author       屋大维 + ChatGPT
// @license      MIT
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    function markdownToHtml(md) {
        return md
            .replace(/&lt;/g, '<').replace(/&gt;/g, '>') // 还原 HTML 转义字符
            .replace(/&nbsp;/g, ' ') // 还原空格
            .replace(/<span[^>]*>([^<]+)<\/span>/g, '$1') // **只移除纯文本的 <span>,保留包含 HTML 的**
            .replace(/\[([^\]]+)\]\(<a href="([^"]+)"[^>]*>.*?<\/a>\)/g, '[$1]($2)') // 还原 HTML 形式的超链接
            .replace(/\[([^\]]+)\]\(([^()\s]+:\/\/[^\s)]+)\)/g, '<a href="$2" target="_blank">$1</a>') // 解析 Markdown 链接
            .replace(/^- (.*)$/gm, '<li>$1</li>') // 解析无序列表 (- item)
            .replace(/^\* (.*)$/gm, '<li>$1</li>') // 解析无序列表 (* item)
            .replace(/(<li>.*<\/li>)/gms, '<ul>$1</ul>') // 包裹 <li> 的 <ul>
            .replace(/^### (.*$)/gm, '<h3>$1</h3>')  // ### Header 3
            .replace(/^## (.*$)/gm, '<h2>$1</h2>')   // ## Header 2
            .replace(/^# (.*$)/gm, '<h1>$1</h1>')    // # Header 1
            .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') // **Bold**
            .replace(/\*(.*?)\*/g, '<em>$1</em>') // *Italic*
            .replace(/`(.*?)`/g, '<code>$1</code>'); // `Inline Code`
    }

    function showNotification(message) {
        let notification = document.createElement('div');
        notification.textContent = message;
        notification.style.position = 'fixed';
        notification.style.top = '10px';
        notification.style.left = '50%';
        notification.style.transform = 'translateX(-50%)';
        notification.style.padding = '10px 20px';
        notification.style.backgroundColor = 'black';
        notification.style.color = 'white';
        notification.style.fontSize = '16px';
        notification.style.borderRadius = '5px';
        notification.style.zIndex = '1000';
        notification.style.opacity = '1';
        notification.style.transition = 'opacity 1s ease-in-out';
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.style.opacity = '0';
            setTimeout(() => document.body.removeChild(notification), 1000);
        }, 1500);
    }

    function processForumPosts() {
        document.querySelectorAll('td.t_msgfont').forEach(td => {
            if (!td.dataset.processed) {
                let originalDiv = document.createElement('div');
                let markdownDiv = document.createElement('div');

                originalDiv.innerHTML = td.innerHTML;
                markdownDiv.innerHTML = markdownToHtml(td.innerHTML);

                markdownDiv.style.display = 'block';
                originalDiv.style.display = 'none';

                td.innerHTML = '';
                td.appendChild(markdownDiv);
                td.appendChild(originalDiv);

                td.dataset.processed = 'true';
                td.dataset.toggled = 'true';
            }
        });
    }

    function toggleMarkdown() {
        let toggled = false;
        document.querySelectorAll('td.t_msgfont').forEach(td => {
            if (td.dataset.processed) {
                let markdownDiv = td.children[0];
                let originalDiv = td.children[1];

                if (td.dataset.toggled === 'true') {
                    markdownDiv.style.display = 'none';
                    originalDiv.style.display = 'block';
                    td.dataset.toggled = 'false';
                    toggled = 'Original Text Mode Enabled';
                } else {
                    markdownDiv.style.display = 'block';
                    originalDiv.style.display = 'none';
                    td.dataset.toggled = 'true';
                    toggled = 'Markdown Mode Enabled';
                }
            }
        });
        if (toggled) {
            showNotification(toggled);
        }
    }

    document.addEventListener('keydown', function(event) {
        if (event.ctrlKey && event.key.toLowerCase() === 'm') {
            event.preventDefault();
            toggleMarkdown();
        }
    });

    processForumPosts();
})();