CodeChef/QOJ → VJudge Redirect

在 CodeChef 与 QOJ 题目页提供跳转到对应 VJudge 题目的按钮(可切换为自动跳转)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         CodeChef/QOJ → VJudge Redirect
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  在 CodeChef 与 QOJ 题目页提供跳转到对应 VJudge 题目的按钮(可切换为自动跳转)
// @author       znzryb
// @match        https://www.codechef.com/problems/*
// @match        https://qoj.ac/problem/*
// @match        https://qoj.ac/contest/*/problem/*
// @grant        none
// @license      GPL-3.0
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  /** 工具函数:创建并挂载按钮(避免重复) */
  function createJumpButton(targetUrl, label = 'Jump to VJudge') {
    if (!targetUrl) return;
    if (document.getElementById('vj-redirect-btn')) return;

    const btn = document.createElement('button');
    btn.id = 'vj-redirect-btn';
    btn.textContent = label;
    Object.assign(btn.style, {
      position: 'fixed',
      top: '100px',
      right: '20px',
      padding: '10px 15px',
      backgroundColor: '#28a745',
      color: '#fff',
      border: 'none',
      borderRadius: '6px',
      cursor: 'pointer',
      fontSize: '14px',
      zIndex: 10000,
      boxShadow: '0 2px 10px rgba(0,0,0,0.15)',
    });
    btn.onmouseenter = () => (btn.style.opacity = '0.9');
    btn.onmouseleave = () => (btn.style.opacity = '1');

    btn.addEventListener('click', () => {
      window.open(targetUrl, '_blank');
    });

    document.body.appendChild(btn);
  }

  /** 解析当前站点并生成对应的 VJudge URL */
  const { host, pathname } = window.location;

  // —— CodeChef ————————————————————————————————————————————————
  // URL 一般为 /problems/ABCDEF 或 /problems/ABCDEF?tab=statement
  if (host.includes('codechef.com')) {
    const parts = pathname.split('/').filter(Boolean); // ['problems','ABCDEF']
    let problemCode = parts[1] || '';                  // 索引 1 为题号
    // 去掉可能存在的查询串(通常问题代码不含 ?,此处保险处理)
    problemCode = problemCode.split('?')[0];

    if (!problemCode) return;

    const vjudgeUrl = `https://vjudge.net/problem/CodeChef-${problemCode}`;

    // === AUTO REDIRECT(可选)===
    // 如果需要自动跳转,取消下面一行注释:
    // window.location.href = vjudgeUrl;

    // 否则创建按钮
    createJumpButton(vjudgeUrl, 'Jump to VJudge');
    return;
  }

  // —— QOJ ————————————————————————————————————————————————
  // 支持:
  //   /problem/14548
  //   /contest/2521/problem/14501
  if (host === 'qoj.ac') {
    let qojPid = '';

    // 形式1:/problem/:pid
    let m = pathname.match(/^\/problem\/(\d+)(?:\/)?$/);
    if (m) {
      qojPid = m[1];
    } else {
      // 形式2:/contest/:cid/problem/:pid
      m = pathname.match(/^\/contest\/(\d+)\/problem\/(\d+)(?:\/)?$/);
      if (m) {
        qojPid = m[2];
      }
    }

    if (!qojPid) return;

    const vjudgeUrl = `https://vjudge.net/problem/QOJ-${qojPid}`;

    // === AUTO REDIRECT(可选)===
    // 如果需要自动跳转,取消下面一行注释:
    // window.location.href = vjudgeUrl;

    createJumpButton(vjudgeUrl, 'Jump to VJudge');
    return;
  }

  // 其他域名不处理
})();