您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
append the form to submit to codeforces contest problem page.
// ==UserScript== // @name cf-fast-submit // @name:ja cf-fast-submit // @namespace https://github.com/LumaKernel/cf-fast-submit // @version 2.8 // @description append the form to submit to codeforces contest problem page. // @description:ja codeforcesのコンテストの問題ページに提出フォームを置くツール. // @author Luma // @match http://codeforces.com/contest/*/problem/* // @match http://codeforces.com/gym/*/problem/* // @match http://codeforces.com/problemset/problem/* // @match http://codeforces.com/group/*/contest/*/problem/* // @match http://*.contest.codeforces.com/group/*/contest/*/problem/* // @match https://codeforces.com/contest/*/problem/* // @match https://codeforces.com/gym/*/problem/* // @match https://codeforces.com/problemset/problem/* // @match https://codeforces.com/group/*/contest/*/problem/* // @match https://*.contest.codeforces.com/group/*/contest/*/problem/* // @grant none // ==/UserScript== /* global $ ace Codeforces */ ;(function () { 'use strict' const openNewWindow = false const SCRIPT_NAME = 'cf fast submit' const origin = location.origin const pathname = location.pathname const modelist = ace.require('ace/ext/modelist') const logged = !!$('a').filter((_, el) => $(el).text() === 'Logout').length let $form let $programType let $toggleEditor let $tabSize let $selectProblem let editor // ~/0 というURLは A 問題として扱われる const startId = '0' const defaultProblemIds = ['A', 'A1'] const pattern = /(contest|gym)\/(.*)\/problem\/([^/]*)\/?$/ const problemsetPattern = /problemset\/problem\/([^/]*)\/([^/]*)\/?$/ const groupPattern = /group\/([^/]+)\/contest\/([^/]*)\/problem\/([^/]*)\/?$/ let type // 'contest' | 'gym' | 'problemset' | 'group' let submitURL let problemId let contestId let participantId // got from submit page /* eslint-disable-next-line object-property-newline */ const extensionMap = {2: "program.cpp", 3: "program.dpr", 4: "program.pas", 6: "program.php", 7: "program.py", 8: "program.rb", 9: "program.cs", 12: "program.hs", 13: "program.pl", 19: "program.ml", 20: "[^{}]*object\s+(\w+).*|$1.scala", 28: "program.d", 31: "a.py", 32: "program.go", 34: "program.js", 36: "[^{}]*public\s+(final)?\s*class\s+(\w+).*|$2.java", 40: "a.py", 41: "a.py", 42: "program.cpp", 43: "program.c", 48: "program.kt", 49: "program.rs", 50: "program.cpp", 51: "program.pas", 52: "program.cpp", 54: "program.cpp", 55: "program.js", 59: "program.cpp", 60: "[^{}]*public\s+(final)?\s*class\s+(\w+).*|$2.java", 61: "program.cpp"} const regenerateInterval = 30 // minutes const retryInterval = 1000 // msec const retryTimes = 20 let doRegenerateOnSubmit = false if (!checkRequirements()) return if (!initInfo()) return tryToInit(true) function checkRequirements () { if (!logged) { console.error(`[${SCRIPT_NAME}] not logged in.`) return false } if (!$) { console.error(`[${SCRIPT_NAME}] not found jQuery.`) return false } if (!ace) { console.error(`[${SCRIPT_NAME}] not found ace.`) return false } return true } function initInfo () { if (pathname.match(/^\/problemset\//)) { type = 'problemset' submitURL = origin + '/problemset/submit' const match = pathname.match(problemsetPattern) contestId = match[1] problemId = match[2] } else if (pathname.match(/^\/group\//)) { type = 'group' const match = pathname.match(groupPattern) const groupId = match[1] contestId = match[2] problemId = match[3] submitURL = `${origin}/group/${groupId}/contest/${contestId}/submit` } else { pathname.match(pattern) const match = pathname.match(pattern) if (!match) return false type = match[1] submitURL = origin + '/' + type + '/' + match[2] + '/submit' problemId = match[3] } return true } async function tryToInit (first) { for (let i = 0; i < retryTimes; i++) { try { if (await initAppendForm(first, false)) return } catch (e) { removeForm() console.error(`[${SCRIPT_NAME}] unexpected error has been occured.`) throw e } removeForm() await delay(retryInterval) } console.error(`[${SCRIPT_NAME}] tried some times but failed.`) } function delay (ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms) }) } async function initAppendForm (first = true, doNotRegenerateOnSubmit = false) { let code = '' let srcFile const ajaxData = {} const raw = await $.ajax(submitURL, { method: 'get', ...ajaxData }) const $newForm = $(raw).find('form.submit-form') if (!$newForm.length) return false if (!first) { code = getCode() || '' srcFile = $form.find('[name=sourceFile]') removeForm() } $form = $newForm $('.problem-statement').append($form) editor = ace.edit('editor') $form.attr('action', submitURL + $form.attr('action')) $programType = $form.find('select[name=programTypeId]') $toggleEditor = $form.find('#toggleEditorCheckbox') $tabSize = $form.find('#tabSizeInput') $selectProblem = $form.find('[name=submittedProblemIndex]') // codeforces default settings editor.setTheme('ace/theme/chrome') editor.setShowPrintMargin(false) editor.setOptions({ enableBasicAutocompletion: true }) if (type === 'contest' || type === 'gym' || type === 'group') { const existsProblemID = id => $selectProblem.find('option').filter((_, el) => $(el).val() === id).length let exists = existsProblemID(problemId) if (!exists && problemId === startId) { for (const id of defaultProblemIds) { if (existsProblemID(id)) { problemId = id exists = true break } } } if (!exists) return false $selectProblem.val(problemId) // ダミーを作る // そのままdisabledにするとformに含まれなくなるので const $cloneSelectProblem = $($selectProblem.prop('outerHTML')) $cloneSelectProblem.prop('disabled', true) $cloneSelectProblem.removeAttr('name') $cloneSelectProblem.val(problemId) $cloneSelectProblem.attr('id', 'submitted_problem_index_fake_display') $selectProblem.after($cloneSelectProblem) $selectProblem.prop('hidden', true) } if (type === 'problemset') { if (problemId === startId) { $form.find('[name=submittedProblemCode]').val(contestId + 'A') } } if (type === 'contest' || type === 'problemset') { contestId = (raw.match(/contestId\s*=\s*(\d+)/) || {1: 0})[1] participantId = (raw.match(/participantId\s*:\s*(\d+)/) || {1: 0})[1] } if (raw.match('updateProblemLockInfo')) updateProblemLockInfo() if (raw.match('updateSubmitButtonState')) updateSubmitButtonState() applyEditorVisibility() setAceMode() updateFilesAndLimits() $toggleEditor.on('change', () => { applyEditorVisibility() const editorEnabled = !$toggleEditor.is(':checked') $.post( '/data/customtest', { communityCode: '', action: 'setEditorEnabled', editorEnabled: editorEnabled }, function (response) {} ) return false }) $tabSize.on('change', () => { const tabSize = $tabSize.val() editor.setOptions({ tabSize }) $.post( '/data/customtest', { communityCode: '', action: 'setTabSize', tabSize: tabSize }, function (response) {} ) }) $programType.on('change', () => { setAceMode() }) editor.getSession().on('change', function () { $('#sourceCodeTextarea').val(editor.getValue()) }) $('#sourceCodeTextarea').on('change', function () { editor.setValue($(this).val(), 1) }) $form.on('submit', preSubmit) if (!first) { if (code) setCode(code) if (srcFile) $form.find('[name=sourceFile]').replaceWith(srcFile) } doRegenerateOnSubmit = false if (!doNotRegenerateOnSubmit) { delay(1000 * 60 * regenerateInterval).then(() => { doRegenerateOnSubmit = true }) } return true } function setAceMode () { var filePath = extensionMap[$programType.val()] const mode = modelist.getModeForPath(filePath).mode if (editor) editor.session.setMode(mode) } function applyEditorVisibility () { if ($('#toggleEditorCheckbox').is(':checked')) { $('#editor').hide() $('#sourceCodeTextarea').show() $('.tabSizeDiv').hide() } else { $('#editor').show() editor.setValue(editor.getValue()) $('#sourceCodeTextarea').hide() $('.tabSizeDiv').show() } } function updateFilesAndLimits () { var problemFiles = $('#submittedProblemFiles') var problemLimits = $('#submittedProblemLimits') var problemIndex = $('select[name=submittedProblemIndex]').val() var option = $('select[name=submittedProblemIndex] option:selected') var timeLimit = option.attr('data-time-limit') var memoryLimit = option.attr('data-memory-limit') var inputFile = option.attr('data-input-file') var outputFile = option.attr('data-output-file') if (problemIndex === '') { problemFiles.text('') problemLimits.text('') } else { var filesStyle = 'float: left; font-weight: bold' if (inputFile === '') { if (outputFile === '') { filesStyle = 'float: left;' problemFiles.text('standard input/output') } else { problemFiles.text('standard input / ' + outputFile) } } else { if (outputFile === '') { problemFiles.text(inputFile + ' / standard output') } else { problemFiles.text(inputFile + ' / ' + outputFile) } } problemFiles.attr('style', filesStyle) problemLimits.text(timeLimit + ' s, ' + memoryLimit + ' MB') } } function removeForm () { $('.submit-form').remove() } function succeedSubmit() { if(openNewWindow) { window.open(location.href) } } function preSubmit () { if (doRegenerateOnSubmit) { initAppendForm(false, true).then(() => { $form.trigger('submit') }) return false } const button = $form.find('input.submit') const img = $form.find('img.ajax-loading-gif') if ($(this).hasAttr('data-submitting')) { succeedSubmit() return true } if (button.prop('disabled')) { return false } var result = callback.call(this) let alwaysDisable = false if (result || alwaysDisable) { img.show() button.prop('disabled', true) setTimeout(function () { img.hide() button.prop('disabled', false) }, alwaysDisable ? 1000 : 10000) } if(result) succeedSubmit() return result } function callback () { var form = $(this) var $ftaa = form.find("input[name='ftaa']") var $bfaa = form.find("input[name='bfaa']") if (window._ftaa && window._bfaa) { $ftaa.val(window._ftaa) $bfaa.val(window._bfaa) } if (form.attr('enctype') === 'multipart/form-data') { var sourceFiles = form.find('.table-form input[name=sourceFile]') if ( sourceFiles.length === 1 && sourceFiles[0].files && sourceFiles[0].files.length === 0 ) { form.removeAttr('enctype') } } return true } function getCode () { const $el = $('#sourceCodeTextarea') return $el.val() } function setCode (code) { const $el = $('#sourceCodeTextarea') $el.val(code) $el.trigger('change') } /* eslint-disable */ // from contest submit page (/contest/****/submit) {{{ function updateProblemLockInfo () { var problemIndex = $('select[name=submittedProblemIndex]').val() updateFilesAndLimits() if (problemIndex != '') { $.post('/data/problemLock', {action: 'checkProblemLock', contestId, participantId, problemIndex: problemIndex}, function (response) { if (response['problemLocked'] == 'true') { Codeforces.setAjaxFormErrors('form table', {error__submittedProblemIndex: 'Problem was locked for submission, it is impossible to resubmit it'}) $('.submit-form :submit').attr('disabled', 'disabled') $('#submittedProblemFiles').text('') $('#submittedProblemLimits').text('') } else { Codeforces.clearAjaxFormErrors('form table') $('.submit-form :submit').removeAttr('disabled') } }, 'json' ) } else { Codeforces.clearAjaxFormErrors('form table') $('.submit-form :submit').attr('disabled', 'disabled') } } function updateSubmitButtonState () { var problemIndex = $('select[name=submittedProblemIndex]').val() updateFilesAndLimits() if (problemIndex == '') { $('.submit-form :submit').attr('disabled', 'disabled') } else { $('.submit-form :submit').removeAttr('disabled') } } // }}} /* eslint-enable */ })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址