您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在洛谷问题页面的特定位置添加一个跳转到 VJudge 的链接
当前为
// ==UserScript== // @name 洛谷到 VJudge 跳转脚本 // @namespace http://tampermonkey.net/ // @version 2.1 // @description 在洛谷问题页面的特定位置添加一个跳转到 VJudge 的链接 // @author Kimi // @match https://www.luogu.com.cn/problem/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; /** * 提取当前页面的题目代码 * @returns {string|null} 题目代码或 null */ function getProblemCode() { const path = window.location.pathname; const match = path.match(/^\/problem\/([^/]+)/); return match ? match[1] : null; } /** * 根据题目代码生成对应的 VJudge URL * @param {string} problemCode - 题目代码 * @returns {string|null} VJudge 的 URL 或 null */ function getVJudgeURL(problemCode) { let match; if ((match = problemCode.match(/^(P|B)(\d+)$/))) { const prefix = '洛谷'; return `https://vjudge.net/problem/${encodeURIComponent(`${prefix}-${problemCode}`)}`; } if ((match = problemCode.match(/^(UVA)(\d+)$/))) { return `https://vjudge.net/problem/${encodeURIComponent(`UVA-${match[2]}`)}`; } if ((match = problemCode.match(/^(CF)(\d+[A-Za-z]*)$/))) { return `https://vjudge.net/problem/${encodeURIComponent(`CodeForces-${match[2]}`)}`; } if ((match = problemCode.match(/^(SP)(\d+)$/))) { const span = document.querySelector('span[title*=" - "]'); if (span) { const title = span.getAttribute('title'); const contestName = title.split(' - ')[0]; return `https://vjudge.net/problem/${encodeURIComponent(`SPOJ-${contestName}`)}`; } return null; } if ((match = problemCode.match(/^AT_(.+)$/))) { return `https://vjudge.net/problem/${encodeURIComponent(`AtCoder-${match[1]}`)}`; } if ((match = problemCode.match(/^AT(.+)$/))) { return `https://vjudge.net/problem/${encodeURIComponent(`AtCoder-${match[1]}`)}`; } return null; } /** * 在页面中注入 VJudge 的链接 * @param {string} vjudgeURL - VJudge 的 URL */ function injectVJudgeLink(vjudgeURL) { const actionDiv = document.querySelector('div.action'); if (actionDiv) { // 检查是否已经注入过链接,避免重复 if (!actionDiv.querySelector('a.vjudge-link')) { const link = document.createElement('a'); link.href = vjudgeURL; link.target = '_blank'; link.style.marginLeft = '10px'; link.textContent = '跳转至 VJudge'; link.classList.add('vjudge-link'); // 添加类名以便后续检查 actionDiv.appendChild(link); console.log('VJudge链接已成功注入:', vjudgeURL); } } else { console.warn('Luogu to VJudge Script: 未找到目标元素 "div.action"。'); } } /** * 主函数,负责整体流程 */ function main() { const problemCode = getProblemCode(); if (!problemCode) { console.warn('Luogu to VJudge Script: 无法提取问题代码。'); return; } const vjudgeURL = getVJudgeURL(problemCode); if (!vjudgeURL) { console.warn(`Luogu to VJudge Script: 未找到问题代码 "${problemCode}" 对应的 VJudge 映射。`); return; } injectVJudgeLink(vjudgeURL); } /** * 使用 MutationObserver 监听 DOM 变化,确保目标元素出现后执行注入 */ function observeDOM() { const observer = new MutationObserver((mutations, obs) => { const actionDiv = document.querySelector('div.action'); if (actionDiv) { main(); obs.disconnect(); // 注入后停止观察 } }); observer.observe(document.body, { childList: true, subtree: true }); } /** * 初始化脚本,根据当前文档状态决定执行方式 */ function init() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { main(); observeDOM(); // 在初始执行后继续观察,以防动态加载 }); } else { main(); observeDOM(); } } // 立即执行初始化 init(); })();