CodeOceanEditorEvaluation = { // A list of non-printable characters that are not allowed in the code output. // Taken from https://stackoverflow.com/a/69024306 nonPrintableRegEx: /[\u0000-\u0008\u000B\u000C\u000F-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF]/g, /** * Scoring-Functions */ scoreCode: function (event) { event.preventDefault(); const cause = $('#assess'); this.newSentryTransaction(cause, async () => { this.stopCode(event); this.clearScoringOutput(); $('#submit').addClass("d-none"); const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); if (!submission) return; this.showSpinner($('#assess')); $('#score_div').removeClass('d-none'); await this.socketScoreCode(submission.id); }); }, handleScoringResponse: function (results) { this.printScoringResults(results); var score = _.reduce(results, function (sum, result) { return sum + result.score * result.weight; }, 0).toFixed(2); $('#score').data('score', score); this.renderScore(); }, printScoringResult: function (result, index) { if (result === undefined || result === null) { return; } $('#results').show(); let card; if (result.file_role === 'teacher_defined_linter') { card = $('#linter-dummies').children().first().clone(); } else { card = $('#test-dummies').children().first().clone(); } if (card.isPresent()) { // the card won't be present if @embed_options[:hide_test_results] == true this.populateCard(card, result, index); $('#results ul').first().append(card); } }, printScoringResults: function (response) { response = (Array.isArray(response)) ? response : [response] const test_results = response.filter(function(x) { if (x === undefined || x === null) { return false; } switch (x.file_role) { case 'teacher_defined_test': return true; case 'teacher_defined_linter': return true; default: return false; } }); $('#results ul').first().html(''); $('.test-count .number').html(test_results.length); this.clearOutput(); _.each(test_results, function (result, index) { // based on https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript if (result === Object(result)) { this.printOutput(result, false, index); this.printScoringResult(result, index); } }.bind(this)); if (_.some(response, function (result) { return result.status === 'timeout'; })) { this.showTimeoutMessage(); } if (_.some(response, function (result) { return result.status === 'out_of_memory'; })) { this.showOutOfMemoryMessage(); } if (_.some(response, function (result) { return result.status === 'runner_in_use'; })) { this.showRunnerInUseMessage(); } if (_.some(response, function (result) { return result.status === 'container_depleted'; })) { this.showContainerDepletedMessage(); } }, renderScore: function () { var score = parseFloat($('#score').data('score')); var maximum_score = parseFloat($('#score').data('maximum-score')); if (score >= 0 && score <= maximum_score && maximum_score > 0) { var percentage_score = (score / maximum_score * 100).toFixed(0); $('.score').html(percentage_score + '%'); } else { $('.score').html(0 + '%'); } this.renderProgressBar(score, maximum_score); }, /** * Testing-Logic */ handleTestResponse: function (result) { this.clearOutput(); this.printOutput(result, false, 0); this.showStatus(result); this.showOutputBar(); }, /** * Stop-Logic */ stopCode: function (event) { event.preventDefault(); if (this.isActiveFileStoppable() && this.websocket) { this.websocket.send(JSON.stringify({'cmd': 'client_kill'})); this.killWebsocket(); this.cleanUpUI(); } }, killWebsocket: function () { if (this.websocket != null && this.websocket.getReadyState() !== WebSocket.OPEN) { return; } this.websocket.killWebSocket(); this.websocket.onError(_.noop); this.running = false; }, cleanUpUI: function () { this.hideSpinner(); this.toggleButtonStates(); this.hidePrompt(); }, /** * Output-Logic */ printWebsocketOutput: function (msg) { if (!msg.data || msg.data === "\r") { return; } var stream = {}; stream[msg.stream] = msg.data; this.printOutput(stream, true, 0); }, clearOutput: function () { $('#output > .output-element').remove(); CodeOceanEditorTurtle.hideCanvas(); }, clearScoringOutput: function () { $('#results ul').first().html(''); $('.test-count .number').html(0); $('#score').data('score', 0); this.renderScore(); this.clearOutput(); }, printOutput: function (output, colorize, index) { if (output === undefined || output === null || output.stderr === undefined && output.stdout === undefined) { // Prevent empty element with no text at all return; } const sanitizedStdout = this.sanitizeOutput(output.stdout); const sanitizedStderr = this.sanitizeOutput(output.stderr); const element = this.findOrCreateOutputElement(index); const pre = $(''); if (sanitizedStdout !== '') { if (colorize) { pre.addClass('text-success'); } pre.append(sanitizedStdout) } if (sanitizedStderr !== '') { if (colorize) { pre.addClass('text-warning'); } else { pre.append('StdErr: '); } pre.append(sanitizedStderr); } if (sanitizedStdout === '' && sanitizedStderr === '') { if (colorize) { pre.addClass('text-body-secondary'); } pre.text($('#output').data('message-no-output')) } element.append(pre); }, sanitizeOutput: function (rawContent) { let sanitizedContent = _.escape(rawContent).replace(this.nonPrintableRegEx, ""); if (rawContent !== undefined && rawContent.trim().startsWith(" document const parsedElement = doc.firstChild.lastChild.firstChild; if (parsedElement.src.startsWith("data:image")) { const sanitizedImg = document.createElement('img'); sanitizedImg.src = parsedElement.src; sanitizedContent = sanitizedImg.outerHTML; } } return sanitizedContent; }, getDeadlineInformation: function(deadline, translation_key, otherwise) { if (deadline !== undefined) { let li = document.createElement("li"); this.submission_deadline = new Date(deadline); let deadline_text = I18n.l("time.formats.long", this.submission_deadline); deadline_text += ` (${this.getUTCTime(this.submission_deadline, I18n.locale === 'en')})`; const bullet_point = I18n.t(`exercises.editor.hints.${translation_key}`, { deadline: deadline_text, otherwise: otherwise }) let text = $.parseHTML(bullet_point); $(li).append(text); return li; } }, getUTCTime: function(d, use_am_pm) { let hour = d.getUTCHours(); const pm = hour >= 12; let hour12 = hour % 12; if (!hour12) { hour12 += 12; } hour = hour.toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping: false}) const minute = d.getUTCMinutes().toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping: false}) const second = d.getUTCSeconds().toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping: false}) if (use_am_pm) { return `${hour12}:${minute}:${second} ${pm ? 'pm' : 'am'} UTC`; } else { return `${hour}:${minute}:${second} UTC`; } }, initializeDeadlines: function () { const deadline = $('#deadline'); if (deadline) { const submission_deadline = deadline.data('submission-deadline'); const late_submission_deadline = deadline.data('late-submission-deadline'); const ul = document.createElement("ul"); if (submission_deadline && late_submission_deadline) { // i18n-tasks-use t('exercises.editor.hints.submission_deadline') ul.append(this.getDeadlineInformation(submission_deadline, 'submission_deadline', '')); // i18n-tasks-use t('exercises.editor.hints.late_submission_deadline') ul.append(this.getDeadlineInformation(late_submission_deadline, 'late_submission_deadline', '')); } else { const otherwise_no_points = I18n.t('exercises.editor.hints.otherwise'); // i18n-tasks-use t('exercises.editor.hints.submission_deadline') ul.append(this.getDeadlineInformation(submission_deadline, 'submission_deadline', otherwise_no_points)); } $(ul).insertAfter($(deadline).children()[0]); } } };