vjudge++

为vJudge设置背景,并汉化部分界面

目前為 2023-08-17 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         vjudge++
// @namespace    vjudge-plus-v2
// @version      1.8.4b4
// @description  为vJudge设置背景,并汉化部分界面
// @author       axototl (original by Suntra)
// @match        https://vjudge.net/*
// @noframes
// @icon         https://vjudge.net/favicon.ico
// @license      AGPLv3 or later
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-end
// ==/UserScript==

// license text: https://www.gnu.org/licenses/agpl-3.0.txt

let config = {experimental: false, debug: false};

function dbgopt(...txt) {
    if(config.debug) console.debug(txt);
}

function getVal(key, def) {
    let gg = GM_getValue(key);
    if (gg === '' || gg === undefined) {
        GM_setValue(key, def);
        gg = def;
    }
    return gg;
}

function reloader() {
    if (!navigator.onLine) {
        alert("离线状态,无法重加载。\n修改无法即刻生效");
        return;
    }
    alert("设置成功,刷新生效");
    location.reload();
}

const loginBoxTranslate = {
    "#loginModalLabel": "登录",
    "#btn-forget-password": "忘记密码",
    "#btn-login": "登录"
};

const registerBoxTrans = {
    "#registerModalLabel": "注册",
    "[for=register-username]": "用户名\n(必填)",
    "[for=register-password]": "密码\n(必填)",
    "[for=register-repeat-password]": "重复密码\n(必填)",
    "[for=register-nickname]": "昵称\n(可修改)",
    "[for=register-school]": "学校",
    "[for=register-email]": "邮箱\n(必填)",
    "[for=register-introduction]": "自我介绍",
    "[for=register-captcha]": "验证码\n(必填)",
    "#btn-register": "注册",
};

const translateTable = {
    "#nav-problem > a": "问题列表",
    "#nav-status > a": "提交记录",
    "#nav-contest > a": "比赛",
    "#nav-workbook > a": "题单",
    "#nav-user > a": "用户",
    "#nav-group > a": "小组",
    "#nav-comment > a": "留言板",
    ".navbar-brand": "vJudge",
    ".login": "登录",
    ".register": "注册",
    ".logout": "登出",
    ".user-dropdown > a:nth-child(1)": "个人主页",
    ".update-profile": "更新个人信息",
    ".message": "消息"
};

const updateProfileTrans = {
    "#updateModalLabel": "更新个人信息",
    "[for=update-username]": "用户名",
    "[for=update-orig-password]": "原密码(必填)",
    "[for=update-password]": "新密码\n(可选)",
    "[for=update-repeat-password]": "重复新密码",
    "[for=update-nickname]": "昵称",
    "[for=update-school]": "学校",
    "[for=update-captcha]": "验证码",
    "[for=update-email]": "邮箱",
    "[for=update-introduction]": "个人简介",
    "#btn-update-profile": "更新"
}

function upd_trans(tr) {
    if (!config.experimental) return;
    for (let prop in tr) {
        let k = document.querySelector(prop);
        if(null != k) k.innerText = tr[prop];
        else dbgopt(prop, "is null");
    }
}

const commonBoxTrans = {
    ".btn[data-dismiss]": "取消"
}

function reg_box_trans(triggerElem_selector, table) {
    if (!config.experimental) return;
    let s = document.querySelector(triggerElem_selector);
    if (null != s) s.addEventListener("click", () => setTimeout(() =>  {upd_trans(table); upd_trans(commonBoxTrans);}, 100));
    else dbgopt(triggerElem_selector, "is null");
}

(async () => {
    config.experimental = getVal("experimental", false);
    debug = getVal("debug", true);
    upd_trans(translateTable);

    function reg_command(name, prompts, need_reload = true) {
        let flag = true;
        GM_registerMenuCommand(prompts[config[name] | 0], () => {
            if(flag) {
                config[name] = !config[name];
                GM_setValue(name, config[name]);
                flag = false;
            }
            if (need_reload) reloader();
        })
    }
    // 设置实验性功能
    reg_command("experimental", ["× 点击启用实验性功能(界面汉化等)", "✔ 点击关闭实验性功能"]);
    // 设置debug
    reg_command("debug", ["已禁用 debug 输出", "已启用debug输出"], false);
    // 在基于 Blink 浏览器上检测是否为正常返回
    if (navigator.userAgent.includes("Chrome") && performance.getEntries()[0].responseStatus != 200) return;

    // 设置背景
    let back = getVal("background", "https://cdn.luogu.com.cn/images/bg/fe/Day_And_Night_1.jpg");
    GM_registerMenuCommand("设置背景URL", () => {
        back = window.prompt("请输入背景URL", back);
        GM_setValue("background", back);
        reloader();
    });

    // 设置文字颜色
    let col = getVal("col", "#b93e3e");
    const tes = /^#([0-9a-f]{3,4}|[0-9a-f]{6})$/i;
    function getColor(t) {
        do {
            tmp = window.prompt("请输入颜色的Hexcode\n(比如#b93e3e)\n建议选择背景主色调的反差色", t);
        } while (!tes.test(t));
        return t;
    }
    GM_registerMenuCommand("设置文字颜色", () => {
        GM_setValue("col", getColor(col));
        reloader();
    });
    let collink = getVal("collink", "#ff4c8c");
    GM_registerMenuCommand("设置链接背景颜色", () => {
        GM_setValue("collink", getColor(collink));
        reloader();
    });
    document.body.innerHTML = "<div style='height: 60px'></div>" + document.body.innerHTML; // 防止顶栏和页面内容重叠
    // User defined style
    GM_addStyle("body {background: url("+back+") no-repeat center top fixed;background-size: 100% 100%;-moz-background-size: 100% 100%;color: "+col+";}"+
        "a:focus, a:hover {color: "+collink+";text-decoration: underline;}");
    // Global Style
    GM_addStyle(
        ".navbar {border-radius:0rem;background-color: rgba(0,0,0,65%) !important;position: fixed;top: 0;left: 0;z-index: 1000;width: 100%;}"+
        "scrollbar-width: none"+
        "#prob-ads {display: none;}"+ // 屏蔽广告
        "#img-support {display: none;}"+ // 屏蔽 sponsor 栏广告
        ".card-block, .card, .list-group-item, .btn-secondary, .page-link, .page-item.disabled .page-link, .dropdown-menu {background-color: rgba(255,255,255,65%)!important;}"+
        ".modal-content {background-color: rgba(255,255,255,90%);}"+
        ".form-control {background-color: rgba(255,255,255,50%);}"+
        ".tab-content {background-color: rgba(255,255,255,50%);border: 2px solid #eceeef;border-radius: 0.25rem;padding: 20px;}"+
        "table {background-color: rgba(255,255,255,70%);border-radius: 0.25rem;}"
    );

    reg_box_trans(".login", loginBoxTranslate);
    reg_box_trans(".register", registerBoxTrans);
    reg_box_trans(".update-profile", updateProfileTrans);
    document.querySelector("body > div.body-footer").innerHTML += '<p style="color: #3fb98b">Theme powered by vjudge++ (original by <a href="https://greasyfork.org/scripts/448801">vjudge+</a>)</p>';
})();