Skip to content

Commit 672f58c

Browse files
committed
Added a web editor as an alternative to uploading code files
1 parent 5822e27 commit 672f58c

File tree

5 files changed

+145
-7
lines changed

5 files changed

+145
-7
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "A code golf game server for JavaScript",
55
"type": "module",
66
"scripts": {
7-
"start": "CG_ADMIN_PASSWORD=admin ts-node --esm --files --project tsconfig.json src/index.ts",
7+
"start": "CG_ADMIN_PASSWORD=cheese ts-node --esm --files --project tsconfig.json src/index.ts",
88
"tests": "jest --runInBand",
99
"tests-watch": "jest --watch",
1010
"format": "prettier --write '**/*'",

public/css/app.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,47 @@ code {
204204
font-family: Courier, monospace;
205205
font-size: inherit;
206206
}
207+
208+
.editor-container {
209+
position: fixed;
210+
inset: 0;
211+
display: flex;
212+
flex-direction: row;
213+
align-items: stretch;
214+
flex-wrap: wrap;
215+
background: rgb(255,255,255,0.9);
216+
}
217+
218+
.editor {
219+
flex-grow: 1;
220+
min-width: 300px;
221+
}
222+
223+
.right {
224+
min-width: 200px;
225+
width: 50%;
226+
display: flex;
227+
flex-direction: column;
228+
height: 100%;
229+
}
230+
231+
.console {
232+
flex: 1;
233+
overflow: scroll;
234+
min-height: 0;
235+
}
236+
237+
.test-container {
238+
display: flex;
239+
flex-direction: row;
240+
}
241+
242+
.test-container input {
243+
flex-grow: 1;
244+
}
245+
246+
.controls {
247+
display: flex;
248+
flex-direction: row;
249+
gap: 2rem;
250+
}

src/game/verifier.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,13 @@ process.on('message', (entry: VerifyJob) => {
8181
)
8282
: assertion.output;
8383

84-
if (assertion.sortOutput && Array.isArray(result) && Array.isArray(expected)) {
84+
if (
85+
assertion.sortOutput &&
86+
Array.isArray(result) &&
87+
Array.isArray(expected)
88+
) {
8589
result.sort();
86-
expected.sort()
90+
expected.sort();
8791
}
8892

8993
if (!lodash.isEqual(result, expected)) {

src/routes/play.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import fs from 'fs';
22
import lodash from 'lodash';
33
import express from 'express';
44
import multiparty from 'multiparty';
5+
import Crypto from 'crypto';
6+
import { tmpdir } from 'os';
7+
import Path from 'path';
58

69
import * as game from '../game/game.js';
710
import { verify } from '../game/game-verifier.js';
@@ -81,6 +84,13 @@ app.get('/solution/:key', (req, res) => {
8184
.send(solution || 'No solution found');
8285
});
8386

87+
function tmpFile(ext:string) {
88+
return Path.join(
89+
tmpdir(),
90+
`archive.${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.${ext}`
91+
);
92+
}
93+
8494
app.post('/submit', (req, res) => {
8595
const redirect = (result: Partial<game.Entry>, err: string) => {
8696
const url = [
@@ -106,8 +116,12 @@ app.post('/submit', (req, res) => {
106116
const team = fields['team'][0];
107117
const key = fields['key'][0];
108118

109-
const file =
119+
let file =
110120
files && files['file'] && files['file'][0] ? files['file'][0].path : null;
121+
if (file == null) {
122+
file = tmpFile('.js');
123+
fs.writeFileSync(file, fields['code'][0]);
124+
}
111125

112126
const entry = {
113127
email,

views/play.html

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<link href="css/bootstrap.min.css" rel="stylesheet" media="screen" />
77
<link href="css/app.css" rel="stylesheet" media="screen" />
8-
<meta http-equiv="refresh" content="60">
8+
<script src=" https://cdn.jsdelivr.net/npm/[email protected]/src-min-noconflict/ace.min.js "></script>
9+
<link href=" https://cdn.jsdelivr.net/npm/[email protected]/css/ace.min.css " rel="stylesheet">
910
</head>
1011
<body>
1112
<div class="container">
@@ -75,8 +76,30 @@ <h4>Oops, your entry is incorrect</h4>
7576
onchange="javascript:this.form.submit();"
7677
/>
7778
<input type="hidden" name="key" value="{{session.key}}" />
79+
<input type="hidden" name="code" value="" />
80+
<div id="editor-container" class="editor-container" style="display: none">
81+
<div id="editor" class="editor"></div>
82+
<div class="right">
83+
<div class="controls">
84+
<button type="submit" class="btn btn-lg btn-info" id="submit">Submit Entry</button>
85+
<div>
86+
Write your code in the editor. Hit "Submit Entry" to submit your entry to the leaderboard. Use the test field to try out your code and the results will appear below. If you reload the page we will remember the last code you entered.
87+
</div>
88+
</div>
89+
<div id="console" class="console"></div>
90+
<div class="test-container">
91+
<input id="test" value="{{ challenge.exampleTest }}{{ ^challenge.exampleTest }}play() {{/challenge.exampleTest}}"/>
92+
<button class="btn btn-lg btn-info" id="run-test">Run Test</button>
93+
</div>
94+
95+
</div>
96+
</div>
7897
<div class="footer">
79-
<a href="#" class="btn btn-lg btn-info">Choose File</a>
98+
<div>
99+
You can upload a file from your computer with "Choose File" or you can edit online (or copy/paste) with "Web Editor"
100+
</div>
101+
<a href="#" class="btn btn-lg btn-info" id="choose">Choose File</a>
102+
<a href="#" class="btn btn-lg btn-info" id="open-editor">Web Editor</a>
80103
</div>
81104
</form>
82105
{{/game.running}} {{^game.running}}
@@ -110,7 +133,7 @@ <h4>You can now view other solutions by clicking on them.</h4>
110133
offsetAddEntryHeight();
111134
$(window).on('resize', offsetAddEntryHeight);
112135

113-
$('.submit-form a').click(function(e) {
136+
$('#choose').click(function(e) {
114137
e.preventDefault();
115138
$('.submit-form input[type="file"]').click();
116139
});
@@ -147,6 +170,59 @@ <h4>You can now view other solutions by clicking on them.</h4>
147170
clock.text([min, sec].join(':'));
148171
}, 1000);
149172
});
173+
174+
175+
176+
const editor = ace.edit("editor");
177+
editor.setTheme("ace/theme/monokai");
178+
editor.session.setMode("ace/mode/javascript");
179+
editor.setValue(localStorage["code"] || "let play = () => {}");
180+
function updateCode () {
181+
localStorage["code"] = editor.getValue();
182+
$('[name="code"]').val(editor.getValue());
183+
}
184+
updateCode();
185+
editor.session.on('change', updateCode);
186+
187+
$('#open-editor').click(function(e) {
188+
e.preventDefault();
189+
if (!$('[name="email"]').val() || !$('[name="team"]').val()) {
190+
alert("Please fill in a name and email first");
191+
return;
192+
}
193+
$('[name="file"]').remove();
194+
$('#editor-container').show();
195+
});
196+
197+
function runTest() {
198+
const oldConsoleLog = console.log;
199+
console.log = function (...args) {
200+
const output = args.join("");
201+
const pre = document.createElement("pre");
202+
pre.innerText = output;
203+
$('#console').append(pre);
204+
pre.scrollIntoView();
205+
};
206+
try {
207+
console.log(eval(editor.getValue() + "\n;\n" + $('#test').val()));
208+
} catch (e) {
209+
console.log(e)
210+
}
211+
console.log = oldConsoleLog;
212+
}
213+
214+
$('#test').keypress(function(e) {
215+
if (e.which === 13) {
216+
e.preventDefault();
217+
runTest();
218+
}
219+
});
220+
221+
$('#run-test').click(function(e) {
222+
e.preventDefault();
223+
runTest();
224+
});
225+
150226
});
151227
</script>
152228
</body>

0 commit comments

Comments
 (0)