您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.
// ==UserScript== // @name 4d4y Markdown Enhancer // @namespace http://tampermonkey.net/ // @version 2.5 // @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"; window.copyCode = function (button) { let codeElement = button.parentElement.querySelector("pre code"); if (!codeElement) return; let text = codeElement.innerText; navigator.clipboard.writeText(text).then(() => { button.innerText = "已复制!"; setTimeout(() => (button.innerText = "复制"), 1500); }); }; function markdownToHtml(md) { if (!md) return ""; let blocks = {}; let blockIndex = 0; // **1. 处理带语言标签的代码块** md = md.replace(/```(\w+)\s*<br>\s*([\s\S]*?)```/g, (match, lang, code) => { let placeholder = `%%CODE${blockIndex}%%`; let cleanCode = code.replace(/<br\s*\/?>/g, "").trim(); let langLabel = `<div style=" background-color: #3a3f4b; color: #ffffff; font-size: 12px; font-weight: bold; padding: 6px 12px; border-top-left-radius: 6px; font-family: sans-serif; display: inline-block; min-width: 100px; text-align: left; ">${lang}</div>`; let copyButton = `<button onclick="copyCode(this)" style=" position: absolute; top: 6px; right: 10px; background-color: transparent; border: none; color: #ffffff; font-size: 12px; cursor: pointer; font-family: sans-serif; opacity: 0; transition: opacity 0.2s ease-in-out; ">复制</button>`; blocks[placeholder] = ` <div style=" position: relative; display: inline-block; width: 100%; background-color: #3a3f4b; border-radius: 6px; margin-bottom: 10px; overflow: hidden; " onmouseover="this.querySelector('button').style.opacity = 1" onmouseout="this.querySelector('button').style.opacity = 0"> ${langLabel} ${copyButton} <pre style=" background-color: #2d2d2d; color: #f8f8f2; padding: 12px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; overflow-x: auto; font-family: 'Consolas', 'Courier New', monospace; font-size: 14px; line-height: 1.5; margin: 0; "><code>${cleanCode.replace(/</g, "<").replace(/>/g, ">")}</code></pre> </div>`; blockIndex++; return placeholder; }); // **2. 处理普通代码块** md = md.replace(/```([\s\S]*?)```/g, (match, code) => { let placeholder = `%%CODE${blockIndex}%%`; let cleanCode = code.replace(/<br\s*\/?>/g, "").trim(); let copyButton = `<button onclick="copyCode(this)" style=" position: absolute; top: 6px; right: 10px; background-color: transparent; border: none; color: #ffffff; font-size: 12px; cursor: pointer; font-family: sans-serif; opacity: 0; transition: opacity 0.2s ease-in-out; ">复制</button>`; blocks[placeholder] = `<div style=" position: relative; display: inline-block; width: 100%; background-color: #3a3f4b; border-radius: 6px; margin-bottom: 10px; overflow: hidden; " onmouseover="this.querySelector('button').style.opacity = 1" onmouseout="this.querySelector('button').style.opacity = 0"> ${copyButton} <pre style=" background-color: #2d2d2d; color: #f8f8f2; padding: 12px; border-radius: 6px; overflow-x: auto; font-family: 'Consolas', 'Courier New', monospace; font-size: 14px; line-height: 1.5; margin: 0; "><code>${cleanCode.replace(/</g, "<").replace(/>/g, ">")}</code></pre> </div>`; blockIndex++; return placeholder; }); // **3. 还原 Markdown 形式的超链接** md = md.replace( /\[([^\]]+)\]\(<a href="([^"]+)"[^>]*>.*?<\/a>\)/g, "[$1]($2)", ); // **4. 处理标题** md = md .replace(/^### (.*$)/gm, "<h3>$1</h3>") .replace(/^## (.*$)/gm, "<h2>$1</h2>") .replace(/^# (.*$)/gm, "<h1>$1</h1>"); // **5. 处理加粗、斜体** md = md .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>") .replace(/\*(.*?)\*/g, "<em>$1</em>"); // **6. 解析 Markdown 列表** md = processLists(md); // **7. 处理行内代码** md = md.replace( /`([^`]+)`/g, `<code style=" background-color: #f5f5f5; color: #d63384; padding: 2px 5px; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 90%; ">$1</code>`, ); // **8. 恢复代码块** Object.keys(blocks).forEach((placeholder) => { md = md.replace(placeholder, blocks[placeholder]); }); // **9. 还原 Markdown 超链接为标准 HTML `<a>`** // 还原 Markdown 超链接,支持各种协议(http, https, chrome-extension, file, mailto 等) md = md.replace( /\[([^\[\]]+)\]\(\s*(([a-zA-Z][a-zA-Z\d+\-.]*):\/\/[^\s)]+)\s*\)/g, '<a href="$2">$1</a>', ); return md; } function processLists(md) { if (!md) return ""; let lines = md.split("\n"); let output = []; let prevWasNewList = true; lines.forEach((line) => { let isNewLine = line.trim() === "<br>"; if (isNewLine) { prevWasNewList = true; output.push(line); return; } let cleanedLine = line.replace(/<br>$/, ""); let spaces = (cleanedLine.match(/^(?: )+/) || [""])[0].length / 6; let reducedLine = cleanedLine.replace(/^(?: )+/, "").trim(); // 检查有序列表 (必须是整数 + 点 + 空格) let matchOrdered = reducedLine.match(/^(\d+)\.\s+(.+)$/); // 检查无序列表 (- 或 * 后跟空格) let matchUnordered = reducedLine.match(/^([-*])\s+(.+)$/); if (matchOrdered) { let number = matchOrdered[1]; let content = matchOrdered[2]; let marginLeft = spaces * 20; // 每级缩进 20px let listItem = `<div style="margin-left: ${marginLeft}px;"><span style="font-weight:bold;">${number}.</span> ${content}</div>`; output.push(listItem); prevWasNewList = false; } else if (matchUnordered) { let bullet = matchUnordered[1] === "-" ? "•" : "◦"; // 使用不同符号区分 - 和 * let content = matchUnordered[2]; let marginLeft = spaces * 20; let listItem = `<div style="margin-left: ${marginLeft}px;"><span style="font-weight:bold;">${bullet}</span> ${content}</div>`; output.push(listItem); prevWasNewList = false; } else { output.push(line); prevWasNewList = false; } }); return output.join("\n"); } 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"; // **默认 Markdown 模式** } }); } function toggleMarkdown(showNotification = true) { 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"; if (showNotification) showToggleNotification("原始文本模式已启用"); } else { markdownDiv.style.display = "block"; originalDiv.style.display = "none"; td.dataset.toggled = "true"; if (showNotification) showToggleNotification("Markdown 模式已启用"); } } }); } function showToggleNotification(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); }, 2000); } function setupKeyboardShortcut() { document.addEventListener("keydown", function (event) { if (event.ctrlKey && event.key === "m") { toggleMarkdown(true); // **按 Ctrl+M 时,一定要弹出通知** event.preventDefault(); } }); } window.addEventListener("load", () => { processForumPosts(); // **默认 Markdown 模式** setupKeyboardShortcut(); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址