diff --git a/ext/js/results.js b/ext/js/results.js
index 4cd81a1..3fe8cca 100644
--- a/ext/js/results.js
+++ b/ext/js/results.js
@@ -19,6 +19,25 @@
let url = document.location.href;
const contestID = getContestID(url);
+ /**
+ * Get submit extension if possible
+ * @returns {string|null} extension without dot or null
+ */
+ function getExt() {
+ const downloadUrl = $('a[href^="/download/Submit/"]').attr('href');
+ const dotPos = downloadUrl.lastIndexOf('.');
+ if (dotPos === -1) {
+ return null;
+ }
+ const ext = downloadUrl.substring(dotPos + 1).trim();
+ if (ext === '') {
+ return null;
+ }
+ return ext;
+ }
+
+ const ext = getExt();
+
/**
* Parse given HTML and return problem status code if it's found.
*
@@ -68,10 +87,8 @@
}
function initializeSyntaxHighlighter() {
- const downloadUrl = $('a[href^="/download/Submit/"]').attr('href');
- const dotPos = downloadUrl.lastIndexOf('.');
- if (dotPos !== -1) {
- let lang = downloadUrl.substr(dotPos + 1);
+ if (ext !== null) {
+ let lang = ext;
if (lang === 'asm') {
lang = 'x86asm';
}
@@ -97,9 +114,13 @@
'href',
`${SATORI_URL_HTTPS}contest/${contestID}/results?results_filter_problem=${submitID}`,
);
+ let resubmitUrl = new URL(submitUrl, window.location);
+ if (ext !== null) {
+ resubmitUrl.searchParams.set('filename', `program.${ext}`);
+ }
const submitButton = $('Submit another').attr(
'href',
- submitUrl,
+ resubmitUrl,
);
$('
')
.append(resultsButton)
diff --git a/ext/js/submit.js b/ext/js/submit.js
index 300bf0d..11b2bee 100644
--- a/ext/js/submit.js
+++ b/ext/js/submit.js
@@ -5,28 +5,80 @@
$('#content table tr:nth-child(1) th').text('Problem:');
$('#content table tr:nth-child(2) th').text('File:');
- $('#content table').append(
- 'Code: | | |
',
- );
+ $('#content table')
+ .append(
+ 'Code: | | |
',
+ )
+ .append(
+ 'Filename: | ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' | |
',
+ );
const problemSelect = $('#id_problem');
const filePicker = $('#id_codefile');
const codeTextarea = $('#code-textarea');
+ const codeFilenameRow = $('#code-filename-row');
+ const codeFilename = $('#code-filename');
+ const codeFilenameAuto = $('#code-filename-auto');
const form = $('#content form');
const submitButton = $('#content form input[type=submit]');
- submitButton.attr('tabindex', '4');
+ codeFilename.val(
+ new URLSearchParams(window.location.search).get('filename') ||
+ 'program.cpp',
+ );
+
+ ['c', 'cpp', 'py', 'asm', 'sql'].forEach((ext) => {
+ $('')
+ .text(`.${ext}`)
+ .appendTo(codeFilenameRow)
+ .on('click', (event) => {
+ event.preventDefault();
+ const filename = codeFilename.val().trim();
+ codeFilename.val(
+ `${filename.split('.')[0] || 'program'}.${ext}`,
+ );
+ });
+ });
+ const setExtButtons = $('.set-ext-button');
+
+ filePicker.wrap('');
+ const clearButton = $('').insertAfter(
+ filePicker,
+ );
+ submitButton.attr('tabindex', '6');
let loading = false;
const updatePickers = () => {
const fileSelected = filePicker.val() !== '';
const textEntered = codeTextarea.val() !== '';
- codeTextarea.attr('disabled', fileSelected);
- filePicker.attr('disabled', textEntered);
+ const filenameEntered = codeFilename.val().trim() !== '';
+
+ // disable one type if the other one is filled,
+ // but don't disable in case somehow both are filled
+ codeTextarea.attr('disabled', fileSelected && !textEntered);
+ filePicker.attr('disabled', textEntered && !fileSelected);
+
+ const showAutoFilename = fileSelected && !textEntered;
+ codeFilename.toggleClass('hidden', showAutoFilename);
+ setExtButtons.attr('disabled', showAutoFilename);
+ codeFilenameAuto.toggleClass('hidden', !showAutoFilename);
+ codeFilenameAuto.val(filePicker[0]?.files[0]?.name ?? '');
+
+ clearButton.toggleClass('hidden', !fileSelected);
+
submitButton.attr(
'disabled',
- loading || !problemSelect.val() || !(textEntered || fileSelected),
+ loading ||
+ !problemSelect.val() ||
+ // NXOR - disable submit if somehow both file and text are set:
+ textEntered === fileSelected ||
+ (textEntered && !filenameEntered),
);
};
@@ -74,10 +126,14 @@
if (filePicker.val() !== '') {
formData.set('codefile', filePicker[0].files[0]);
} else if (codeTextarea.val() !== '') {
+ const filename = codeFilename.val().trim();
+ if (filename === '') {
+ return;
+ }
const blob = new Blob([codeTextarea.val()], {
type: 'text/plain',
});
- formData.set('codefile', blob, 'code.cpp');
+ formData.set('codefile', blob, filename);
} else {
return;
}
@@ -112,4 +168,10 @@
loading = false;
updatePickers();
});
+
+ clearButton.on('click', (event) => {
+ event.preventDefault();
+ filePicker.val('');
+ updatePickers();
+ });
})();
diff --git a/ext/scss/submit.scss b/ext/scss/submit.scss
index d50a567..c6bdc83 100644
--- a/ext/scss/submit.scss
+++ b/ext/scss/submit.scss
@@ -11,14 +11,14 @@
}
td {
- select,
- input,
- textarea {
+ & > select,
+ & > input,
+ & > textarea {
width: 100%;
box-sizing: border-box;
}
- textarea {
+ & > textarea {
resize: vertical;
min-height: 100px;
height: 200px;
@@ -26,6 +26,21 @@
}
}
+ .input-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 4px;
+
+ input {
+ flex-grow: 1;
+ }
+
+ &.hidden {
+ display: none;
+ }
+ }
+
input[type='submit'] {
display: block;
margin-left: auto;