Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 2d33eae

Browse files
committed
Migrate livecode to new activecode
1 parent 5646710 commit 2d33eae

File tree

2 files changed

+218
-3
lines changed

2 files changed

+218
-3
lines changed

runestone/activecode/activecode.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def setup(app):
4444
app.add_javascript('activecode.js')
4545
app.add_javascript('skulpt.min.js')
4646
app.add_javascript('skulpt-stdlib.js')
47+
app.add_javascript('clike.js')
4748

4849
app.add_node(ActivcodeNode, html=(visit_ac_node, depart_ac_node))
4950

@@ -53,7 +54,7 @@ def setup(app):
5354

5455

5556
TEMPLATE = """
56-
<pre data-component="activecode" id=%(divid)s data-lang="%(language)s" %(autorun)s %(hidecode)s %(include)s %(timelimit)s %(coach)s %(codelens)s data-audio='%(ctext)s'>
57+
<pre data-component="activecode" id=%(divid)s data-lang="%(language)s" %(autorun)s %(hidecode)s %(include)s %(timelimit)s %(coach)s %(codelens)s data-audio='%(ctext)s' %(sourcefile)s %(datafile)s %(stdin)s>
5758
%(initialcode)s
5859
</pre>
5960
"""
@@ -152,7 +153,10 @@ class ActiveCode(Directive):
152153
'tour_5': directives.unchanged,
153154
'nocodelens': directives.flag,
154155
'coach': directives.flag,
155-
'timelimit': directives.unchanged
156+
'timelimit': directives.unchanged,
157+
'stdin' : directives.unchanged,
158+
'datafile' : directives.unchanged,
159+
'sourcefile' : directives.unchanged
156160
}
157161

158162
def run(self):
@@ -232,6 +236,22 @@ def run(self):
232236
else:
233237
self.options['coach'] = ''
234238

239+
# livecode options
240+
if 'stdin' in self.options:
241+
self.options['stdin'] = "data-stdin='%s'" % self.options['stdin']
242+
else:
243+
self.options['stdin'] = ""
244+
245+
if 'datafile' not in self.options:
246+
self.options['datafile'] = ""
247+
else:
248+
self.options['datafile'] = "data-datafile='%s'" % self.options['datafile']
249+
250+
if 'sourcefile' not in self.options:
251+
self.options['sourcefile'] = ""
252+
else:
253+
self.options['sourcefile'] = "data-sourcefile='%s'" % self.options['sourcefile']
254+
235255
return [ActivcodeNode(self.options)]
236256

237257

runestone/activecode/js/activecode.js

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,17 +1008,212 @@ AudioTour.prototype.setBackgroundForLines = function (divid, lnum, color) {
10081008
}
10091009
};
10101010

1011+
//
1012+
//
10111013

1014+
LiveCode.prototype = new ActiveCode();
1015+
1016+
function LiveCode(opts) {
1017+
if (opts) {
1018+
this.init(opts)
1019+
}
1020+
}
1021+
1022+
LiveCode.prototype.init = function(opts) {
1023+
ActiveCode.prototype.init.apply(this,arguments)
1024+
1025+
var orig = opts.orig;
1026+
this.stdin = $(orig).data('stdin');
1027+
this.datafile = $(orig).data('datafile');
1028+
this.sourcefile = $(orig).data('sourcefile');
1029+
1030+
this.API_KEY = "67033pV7eUUvqo07OJDIV8UZ049aLEK1"
1031+
this.USE_API_KEY = true;
1032+
this.JOBE_SERVER = 'http://jobe2.cosc.canterbury.ac.nz';
1033+
this.resource = '/jobe/index.php/restapi/runs/';
1034+
this.div2id = {}
1035+
if (this.stdin) {
1036+
this.createInputElement();
1037+
}
1038+
this.createErrorOutput();
1039+
};
1040+
1041+
LiveCode.prototype.outputfun = function (a) {};
1042+
1043+
LiveCode.prototype.createInputElement = function () {
1044+
1045+
var label = document.createElement('label');
1046+
label.for = this.divid + "_stdin";
1047+
$(label).text("Input for Program")
1048+
var input = document.createElement('input')
1049+
input.id = this.divid + "_stdin";
1050+
input.type = "text";
1051+
input.size = "35";
1052+
input.value = this.stdin;
1053+
this.outerDiv.appendChild(label);
1054+
this.outerDiv.appendChild(input);
1055+
this.stdin_el = input;
1056+
};
1057+
1058+
LiveCode.prototype.createErrorOutput = function () {
1059+
1060+
}
1061+
1062+
LiveCode.prototype.runProg = function() {
1063+
var xhr, stdin;
1064+
var runspec = {};
1065+
var data, host, source, editor;
1066+
var sfilemap = {java: '', cpp: 'test.cpp', c: 'test.c', python3: 'test.py', python2: 'test.py'}
1067+
1068+
xhr = new XMLHttpRequest();
1069+
source = this.editor.getValue();
1070+
1071+
if (this.stdin) {
1072+
stdin = $(this.stdin_el).val();
1073+
}
1074+
1075+
if (! this.sourcefile ) {
1076+
this.sourcefile = sfilemap[this.language];
1077+
}
1078+
1079+
runspec = {
1080+
language_id: this.language,
1081+
sourcecode: source,
1082+
sourcefilename: this.sourcefile
1083+
};
1084+
1085+
1086+
if (stdin) {
1087+
runspec.input = stdin
1088+
}
1089+
1090+
if (this.datafile) {
1091+
runspec['file_list'] = [[this.div2id[datafile],datafile]];
1092+
}
1093+
data = JSON.stringify({'run_spec': runspec});
1094+
host = this.JOBE_SERVER + this.resource
1095+
1096+
var odiv = this.output;
1097+
$(this.runButton).attr('disabled', 'disabled');
1098+
$(this.codeDiv).switchClass("col-md-12","col-md-6",{duration:500,queue:false});
1099+
$(this.outDiv).show({duration:700,queue:false});
1100+
$(this.errDiv).remove();
1101+
1102+
xhr.open("POST", host, true);
1103+
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
1104+
xhr.setRequestHeader('Accept', 'application/json');
1105+
xhr.setRequestHeader('X-API-KEY', this.API_KEY);
1106+
1107+
xhr.onload = (function () {
1108+
var logresult;
1109+
$(this.runButton).removeAttr('disabled');
1110+
try {
1111+
var result = JSON.parse(xhr.responseText);
1112+
} catch (e) {
1113+
result = {};
1114+
result.outcome = -1;
1115+
}
1116+
1117+
if (result.outcome === 15) {
1118+
logresult = 'success';
1119+
} else {
1120+
logresult = result.outcome;
1121+
}
1122+
logRunEvent({'div_id': this.divid, 'code': source, 'errinfo': logresult, 'event':'livecode'});
1123+
switch (result.outcome) {
1124+
case 15:
1125+
$(odiv).html(result.stdout.replace(/\n/g, "<br>"));
1126+
break;
1127+
case 11: // compiler error
1128+
$(odiv).html("There were errors compiling your code. See below.");
1129+
this.addJobeErrorMessage(result.cmpinfo);
1130+
break;
1131+
case 12: // run time error
1132+
$(odiv).html(result.stdout.replace(/\n/g, "<br>"));
1133+
if (result.stderr) {
1134+
this.addJobeErrorMessage(result.stderr);
1135+
}
1136+
break;
1137+
case 13: // time limit
1138+
$(odiv).html(result.stdout.replace(/\n/g, "<br>"));
1139+
this.addJobeErrorMessage("Time Limit Exceeded on your program");
1140+
break;
1141+
default:
1142+
if(result.stderr) {
1143+
$(odiv).html(result.stderr.replace(/\n/g, "<br>"));
1144+
} else {
1145+
this.addJobeErrorMessage("A server error occurred: " + xhr.status + " " + xhr.statusText);
1146+
}
1147+
}
1148+
1149+
// todo: handle server busy and timeout errors too
1150+
}).bind(this);
1151+
1152+
///$("#" + divid + "_errinfo").remove();
1153+
$(this.output).html("Compiling and Running your Code Now...")
1154+
1155+
xhr.onerror = function () {
1156+
this.addJobeErrorMessage("Error communicating with the server.");
1157+
$(this.runButton).removeAttr('disabled');
1158+
};
1159+
1160+
xhr.send(data);
1161+
};
1162+
LiveCode.prototype.addJobeErrorMessage = function (err) {
1163+
var errHead = $('<h3>').html('Error');
1164+
var eContainer = this.outerDiv.appendChild(document.createElement('div'));
1165+
this.errDiv = eContainer;
1166+
eContainer.className = 'error alert alert-danger';
1167+
eContainer.id = this.divid + '_errinfo';
1168+
eContainer.appendChild(errHead[0]);
1169+
var errText = eContainer.appendChild(document.createElement('pre'))
1170+
errText.innerHTML = err;
1171+
};
1172+
1173+
1174+
LiveCode.prototype.pushDataFile = function (datadiv) {
1175+
1176+
var file_id = 'runestone'+Math.floor(Math.random()*100000);
1177+
var contents = $(document.getElementById(datadiv)).text();
1178+
var contentsb64 = btoa(contents);
1179+
var data = JSON.stringify({ 'file_contents' : contentsb64 });
1180+
var resource = '/jobe/index.php/restapi/files/' + file_id
1181+
var host = JOBE_SERVER + resource
1182+
var xhr = new XMLHttpRequest();
1183+
1184+
if (this.div2id[datadiv] === undefined ) {
1185+
this.div2id[datadiv] = file_id;
1186+
1187+
xhr.open("PUT", host, true);
1188+
xhr.setRequestHeader('Content-type', 'application/json');
1189+
xhr.setRequestHeader('Accept', 'text/plain');
1190+
xhr.setRequestHeader('X-API-KEY', API_KEY);
1191+
1192+
xhr.onload = function () {
1193+
console.log("successfully sent file " + xhr.responseText);
1194+
}
1195+
1196+
xhr.onerror = function () {
1197+
console.log("error sending file" + xhr.responseText);
1198+
}
1199+
1200+
xhr.send(data)
1201+
}
1202+
};
1203+
1204+
//
10121205

10131206
$(document).ready(function() {
10141207
$('[data-component=activecode]').each( function(index ) {
10151208
if ($(this).data('lang') === "javascript") {
10161209
edList[this.id] = new JSActiveCode({'orig': this});
10171210
} else if ($(this).data('lang') === 'htmlmixed') {
10181211
edList[this.id] = new HTMLActiveCode({'orig': this});
1212+
} else if (['java', 'cpp', 'c', 'python3', 'python2'].includes($(this).data('lang'))) {
1213+
edList[this.id] = new LiveCode({'orig': this});
10191214
} else { // default is python
10201215
edList[this.id] = new ActiveCode({'orig': this});
1021-
}
1216+
}
10221217
});
10231218
});
10241219

0 commit comments

Comments
 (0)