Greasy Fork is available in English.

USACO Better

USACO 优化插件

2024-03-17 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

// ==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如果无法解决,请前往 https://greasyfork.org/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如果无法解决,请前往 https://greasyfork.org/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();
})();