Greasy Fork

Greasy Fork is available in English.

USACO Better

USACO 优化插件

当前为 2024-03-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         USACO Better
// @namespace    http://tampermonkey.net/
// @version      1.2.2
// @description  USACO 优化插件
// @author       ZnPdCo
// @match        https://usaco.org/*
// @icon         https://usaco.guide/favicon-32x32.png
// @grant        unsafeWindow
// @connect      www2.deepl.com
// @connect      www.iflyrec.com
// @connect      m.youdao.com
// @connect      api.interpreter.caiyunai.com
// @connect      translate.google.com
// @connect      greasyfork.org
// @connect      *
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_registerMenuCommand
// @require      https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js
// @license      MIT
// ==/UserScript==



(function() {
    // 常量
    const translates = {
        'gg': {'name': '谷歌翻译', 'func': translate_gg},
        'youdao_mobile': {'name': '有道翻译', 'func': translate_youdao_mobile},
        'deepl': {'name': 'deepl翻译', 'func': translate_deepl},
        'iflyrec': {'name': '讯飞听见翻译', 'func': translate_iflyrec},
    };

    // 设置页内容
    if(location.search == '?page=settings') {
        $('.content').html(`
<a href="index.php">
  <img src="current/images/usaco_logo.png" height="130px" border="0px">
</a>
<div class="navbar" align="left" border="10px">
  <ul>
    <li>
      <a href="index.php">Overview</a>
    </li>
    <li>
      <a href="index.php?page=training">Training</a>
    </li>
    <li>
      <a href="index.php?page=contests">Contests</a>
    </li>
    <li>
      <a href="index.php?page=history">History</a>
    </li>
    <li>
      <a href="index.php?page=staff">Staff</a>
    </li>
    <li>
      <a href="index.php?page=resources">Resources</a>
    </li>
  </ul>
</div>
<br>
<div style="position:relative; float:left; left:10px; top:-50px; width:880px;">
  <div class="panel">
    <h2>USACO Better 设置</h2>
  </div>
</div>
<br style="clear:both">`);

        // 题面翻译设置
        if(GM_getValue('translate') == undefined) {
            GM_setValue('translate', 'iflyrec');
        }
        $('.panel').append(`翻译引擎设置:<select id="translate"></select><br><br>`)
        for (var [key, value] of Object.entries(translates)) {
            $('#translate').append(`<option value="${key}" ${GM_getValue('translate') == key ? 'selected' : ''}>${value['name']}</option>`);
        }
        $('#translate').change(function() {
            GM_setValue('translate', $('#translate').val());
        })


        // 提交代码框设置
        if(GM_getValue('code_box') == undefined) {
            GM_setValue('code_box', true);
        }
        $('.panel').append(`提交代码时使用代码框:<input id="code_box" type="checkbox" ${GM_getValue('code_box') == true ? 'checked' : ''}><br><br>`)
        $('#code_box').change(function() {
            GM_setValue('code_box', $('#code_box').is(':checked'));
        })
    }

    // 菜单栏展示设置页
    $('.navbar ul').append(`<li><a href="index.php?page=settings">Settings</a></li>`)
    $('.navbar ul li a').css({'width': '90px'})

    // 界面翻译
    $('.navbar').html($('.navbar').html().
                      replaceAll('Overview', '主页').
                      replaceAll('Training', '训练').
                      replaceAll('Contests', '比赛').
                      replaceAll('History', '历史').
                      replaceAll('Staff', '成员').
                      replaceAll('Resources', '资源').
                      replaceAll('Settings', '设置'))
    // 主页内容
    if(location.search == '') {
        $('.panel a:contains("rules")').text(`比赛规则`)
    }
    // 训练页内容
    if(location.search == '?page=training') {
        $('.panel').html(`
<h2>训练</h2>

<p><b>新资源:</b> 一群敬业的学长们已经准备了一个新的培训平台: <a href="https://usaco.guide/">USACO Guide</a>。

<p> USACO的<a href="https://usaco.training">培训页面</a> 提供了数百个小时的免费指导和练习问题,帮助提高编程和问题解决能力。
(注:我们正在迁移培训页面至新系统;与此同时,它们托管在我们的旧系统上,在这段时间内,全新的usaco.org用户账户无法被识别 —— 您可能需要创建一个单独的账户来访问培训页面。)
</p>

<p> 通过在线培训页面展现出实质性进步,并在我们的<a href="index.php?page=contests">在线编程比赛</a>中表现出色的学生,有资格被选为受邀参加USACO夏季<a href="index.php?page=camp">培训营</a>的决赛选手,进一步接受指导,并有可能被考虑为参加国际信息学奥林匹克竞赛(IOI)的美国代表团成员。
</p>
        `)
    }

    // 历史页内容
    if(location.search == '?page=history') {
        $('.panel').html(`该页由于篇幅原因,不进行翻译` + $('.panel').html())
    }
    // 成员页内容
    if(location.search == '?page=staff') {
        $('.panel').html(`该页由于篇幅原因,不进行翻译` + $('.panel').html())
    }
    // 资源页内容
    if(location.search == '?page=resources') {
        $('.panel').html(`该页由于篇幅原因,不进行翻译` + $('.panel').html())
    }
    // 规则页内容
    if(location.search == '?page=instructions') {
        $('.panel').html(`一般情况下,C 和 C++ 时限 2 秒,Java 和 Python 时限 4 秒,代码长度不超过 100,000 bytes,编译不超过 30 秒,空间限制 256 MB,C 和 C++ 开 O2,Python 开 O 优化\n` + $('.panel').html())
    }
    // 比赛页
    if(location.search == '?page=contests') {
        $('.panel:eq(0)').html($('.panel:eq(0)').html().
                               replaceAll('Previous Contests', '往届比赛').
                               replaceAll('Season', '季度'))
    }

    //--谷歌翻译--start
    async function translate_gg(raw) {
        return new Promise((resolve, reject) => {
            const url = 'https://translate.google.com/m';
            const params = `tl=zh-CN&q=${encodeURIComponent(raw)}`;

            GM_xmlhttpRequest({
                method: 'GET',
                url: `${url}?${params}`,
                onload: function (response) {
                    const html = response.responseText;
                    const translatedText = $(html).find('.result-container').text();
                    resolve(translatedText);
                },
                onerror: function (response) {
                    reject("发生了未知的错误,请确认你是否能正常访问Google翻译,\n\n如果无法解决,请前往 http://greasyfork.icu/zh-CN/scripts/471106/feedback 反馈 请注意打码报错信息的敏感部分\n\n响应报文:" + JSON.stringify(response))
                }
            });
        });
    }
    //--谷歌翻译--end

    //--有道翻译m--start
    async function translate_youdao_mobile(raw) {
        const options = {
            method: "POST",
            url: 'http://m.youdao.com/translate',
            data: "inputtext=" + encodeURIComponent(raw) + "&type=AUTO",
            anonymous: true,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
                'Host': 'm.youdao.com',
                'Origin': 'http://m.youdao.com',
                'Referer': 'http://m.youdao.com/translate',
            }
        }
        return await BaseTranslate('有道翻译mobile', raw, options, res => /id="translateResult">\s*?<li>([\s\S]*?)<\/li>\s*?<\/ul/.exec(res)[1])
    }
    //--有道翻译m--end

    //--Deepl翻译--start
    // 获得时间戳
    function getTimeStamp(iCount) {
        const ts = Date.now();
        if (iCount !== 0) {
            iCount = iCount + 1;
            return ts - (ts % iCount) + iCount;
        } else {
            return ts;
        }
    }

    async function translate_deepl(raw) {
        const id = (Math.floor(Math.random() * 99999) + 100000) * 1000;
        const data = {
            jsonrpc: '2.0',
            method: 'LMT_handle_texts',
            id,
            params: {
                splitting: 'newlines',
                lang: {
                    source_lang_user_selected: 'auto',
                    target_lang: 'ZH',
                },
                texts: [{
                    text: raw,
                    requestAlternatives: 3
                }],
                timestamp: getTimeStamp(raw.split('i').length - 1)
            }
        }
        let postData = JSON.stringify(data);
        if ((id + 5) % 29 === 0 || (id + 3) % 13 === 0) {
            postData = postData.replace('"method":"', '"method" : "');
        } else {
            postData = postData.replace('"method":"', '"method": "');
        }
        const options = {
            method: 'POST',
            url: 'https://www2.deepl.com/jsonrpc',
            data: postData,
            headers: {
                'Content-Type': 'application/json',
                'Host': 'www2.deepl.com',
                'Origin': 'https://www.deepl.com',
                'Referer': 'https://www.deepl.com/',
            },
            anonymous: true,
            nocache: true,
        }
        return await BaseTranslate('Deepl翻译', raw, options, res => JSON.parse(res).result.texts[0].text)
    }

    //--Deepl翻译--end

    //--讯飞听见翻译--end
    async function translate_iflyrec(text) {
        const options = {
            method: "POST",
            url: 'https://www.iflyrec.com/TranslationService/v1/textTranslation',
            data: JSON.stringify({
                "from": "2",
                "to": "1",
                "contents": [{
                    "text": text,
                    "frontBlankLine": 0
                }]
            }),
            anonymous: true,
            headers: {
                'Content-Type': 'application/json',
                'Origin': 'https://www.iflyrec.com',
            },
            responseType: "json",
        };
        return await BaseTranslate('讯飞翻译', text, options, res => JSON.parse(res).biz[0].translateResult.replace(/\\n/g, "\n\n"));
    }
    //--讯飞听见翻译--end

    //--异步请求包装工具--start
    async function PromiseRetryWrap(task, options, ...values) {
        const { RetryTimes, ErrProcesser } = options || {};
        let retryTimes = RetryTimes || 5;
        const usedErrProcesser = ErrProcesser || (err => { throw err });
        if (!task) return;
        while (true) {
            try {
                return await task(...values);
            } catch (err) {
                if (!--retryTimes) {
                    console.warn(err);
                    return usedErrProcesser(err);
                }
            }
        }
    }

    async function BaseTranslate(name, raw, options, processer) {
        let errtext;
        const toDo = async () => {
            var tmp;
            try {
                const data = await Request(options);
                tmp = data.responseText;
                let result = await processer(tmp);
                return result;
            } catch (err) {
                errtext = tmp;
                throw {
                    responseText: tmp,
                    err: err
                }
            }
        }
        return await PromiseRetryWrap(toDo, { RetryTimes: 3, ErrProcesser: () => "翻译出错,请查看报错信息,并重试或更换翻译接口\n\n如果无法解决,请前往 http://greasyfork.icu/zh-CN/scripts/471106/feedback 反馈 请注意打码报错信息的敏感部分\n\n报错信息:" + errtext })
    }


    function Request(options) {
        return new Promise((reslove, reject) => GM_xmlhttpRequest({ ...options, onload: reslove, onerror: reject }))
    }

    async function show_translate_btn() {
        if($('#probtext-text').length) {
            $.ajax({
                type: "GET",
                url: location.href,
                async: false,
                success: function(data) {
                    window.data = data
                },
                error: function(xhr, statusText, error) {}
            });

            var origin_html = {};
            $('#probtext-text').html($(data).find('#probtext-text').html())
            var ele1 = $('#probtext-text').children()
            var ele2 = $('#probtext-text').contents().filter(function() {
                return this.nodeType === 3;
            })
            var ele = $.merge(ele1, ele2)
            for(let i = 0; i < ele.length; i++) {
                if(ele.eq(i).text().replaceAll('\n', '').replaceAll(' ', '') == '' || ele.get(i).tagName == 'PRE' || ele.eq(i).text().includes('SAMPLE INPUT:') || ele.eq(i).text().includes('SAMPLE OUTPUT:')) continue
                $(`
  <div style="text-align: right">
  <button style="margin-bottom:6px;
  background-color: transparent;
  color: #08c;
  margin-left: 4px;
  border: 1px solid #08c;
  border-radius: 3px;" class="fanyi" id="fanyi-${i}" type="button">翻译</button>
  </div>`).insertBefore(ele.eq(i))
                if(ele.get(i).nodeType == 3)origin_html[i] = ele.eq(i).text()
                else origin_html[i] = ele.eq(i).html()
            }

            MathJax.Hub.Queue(["Typeset", MathJax.Hub]);

            $(`.fanyi`).click(async function(e) {
                var fanyi_id = parseInt($(e.target).attr("id").replace('fanyi-', ''));
                var text = origin_html[fanyi_id]
                var texts = text.split('$$')
                var res = ""
                var tex = {}
                var cnt = 0;
                for(let i = 0; i < texts.length; i++) {
                    if(i % 2 == 0) res += texts[i];
                    else {
                        cnt++;
                        tex[cnt] = '$$' + texts[i] + '$$';
                        res += '{' + cnt + '}';
                    }
                }
                text = res
                texts = text.split('$')
                res = ""
                for(let i = 0; i < texts.length; i++) {
                    if(i % 2 == 0) res += texts[i];
                    else {
                        cnt++;
                        tex[cnt] = '$' + texts[i] + '$';
                        res += '{' + cnt + '}';
                    }
                }
                text = res

                if($(`#result-${fanyi_id}`).length == 0) {
                    $(`<div align="left" id="result-${fanyi_id}" class="problem-text mathjax" style="width:750px; padding-top:10px;"></div>`).insertAfter(e.target)
                    $(`
<button style="margin-bottom:6px;
background-color: transparent;
color: #08c;
margin-left: 4px;
border: 1px solid #08c;
border-radius: 3px;" type="button" onclick="
function run(){
    if($('#result-${fanyi_id}').is(':hidden')) {
        $('#result-${fanyi_id}').show()
    } else {
        $('#result-${fanyi_id}').hide()
    }
}
run()">折叠、展开</button>
                `).insertBefore(e.target)
                }
                var timer = setInterval(function() {
                    var tip = `正在使用 ${translates[GM_getValue('translate')]['name']} 翻译,稍安勿躁`;
                    var tip1 = tip + '.';
                    var tip2 = tip + '..';
                    var tip3 = tip + '...';
                    if($(`#result-${fanyi_id}`).html() == tip1) $(`#result-${fanyi_id}`).html(tip2);
                    else if($(`#result-${fanyi_id}`).html() == tip2) $(`#result-${fanyi_id}`).html(tip3);
                    else if($(`#result-${fanyi_id}`).html() == tip3) $(`#result-${fanyi_id}`).html(tip1);
                }, 100);
                $(`#result-${fanyi_id}`).html(`正在使用 ${translates[GM_getValue('translate')]['name']} 翻译,稍安勿躁.`);


                text = await translates[GM_getValue('translate')]['func'](text)

                texts = text.split(/{|}/)
                res = ""
                for(let i = 0; i < texts.length; i++) {
                    if(i % 2 == 0) res += texts[i]
                    else res += tex[parseInt(texts[i])]
                }
                text = res

                clearInterval(timer);
                $(`#result-${fanyi_id}`).html(text);
                $(e.target).text('重新翻译');
                MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
            })
        }
    }
    show_translate_btn();
    function show_code_box() {
        if($('#probtext-text').length && GM_getValue('code_box') == true) {
            $('#solution .field2:eq(2)').remove()
            $('#solution .field2:eq(1)').remove()
            $(`
<div class="field2">
<label for="sourcefile">Your Code:</label>
<textarea id="code" style="width: 800px; height: 200px;"></textarea></div>
<div class="field2">
<button id="solution-submit" type="button">Submit Solution</button></div>
            `).insertAfter('#solution .field2:eq(0)')
            $('#solution-submit').click(function() {
                var form = document.getElementsByClassName('submission')[0];
                var text = document.getElementById('code').value;
                var fileData = new File([text], 'foo.cpp', {
                    type: 'multipart/form-data',
                });
                var formData = new FormData(form);
                formData.set('sourcefile', fileData)
                $.ajax({
                    url: "current/tpcm/submit-solution.php",
                    type: "POST",
                    async: false,
                    data: formData,
                    processData: false, // 不处理数据
                    contentType: false // 不设置内容类型

                });
                location.reload();
            })
        }
    }
    show_code_box();
})();