summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--css/codeq.css51
-rw-r--r--index.html68
-rw-r--r--js/codeq/prolog.js712
-rw-r--r--js/codeq/python.js658
-rw-r--r--res/eve.pngbin0 -> 147225 bytes
-rw-r--r--res/prolog.pngbin0 -> 46745 bytes
-rw-r--r--res/python.pngbin0 -> 26237 bytes
7 files changed, 771 insertions, 718 deletions
diff --git a/css/codeq.css b/css/codeq.css
index d293787..153e870 100644
--- a/css/codeq.css
+++ b/css/codeq.css
@@ -3,11 +3,58 @@ body {
}
-#title {
- color: whitesmoke;
+.title {
+ /*color: whitesmoke;*/
margin-left: 5px;
}
+/* form-signin */
+.form-signin {
+ max-width: 330px;
+ padding: 15px;
+ margin: 0 auto;
+}
+.form-signin .form-signin-heading,
+.form-signin .checkbox {
+ margin-bottom: 10px;
+}
+.form-signin .checkbox {
+ font-weight: normal;
+}
+.form-signin .form-control {
+ position: relative;
+ height: auto;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 10px;
+ font-size: 16px;
+}
+.form-signin .form-control:focus {
+ z-index: 2;
+}
+.form-signin input[type="text"] {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+
+/* screen language */
+#screen_language {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+#screen_language .col-lg-4 {
+ cursor: pointer;
+}
+
+/* screen problems code*/
.block {
border-right: 1px solid #e5e5e5;
diff --git a/index.html b/index.html
index 1dfe0e0..3613ad9 100644
--- a/index.html
+++ b/index.html
@@ -22,6 +22,7 @@
<!-- the status bar at the top of each page -->
<div class="navbar navbar-inverse navbar-fixed-top" id="topbar">
<div class="container-fluid">
+ <!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
@@ -29,6 +30,11 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#" title="Intelligent tutor system">CodeQ</a>
+ <!--a class="navbar-brand" id="title"></a-->
+ </div>
+
+ <!-- Collect the nav links, forms, and other content for toggling -->
+ <div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li style="display: none;" id="navigation-login"><a href="">Login</a></li>
<li style="display: none;" id="navigation-language"><a href="">Language</a></li>
@@ -36,9 +42,6 @@
<li style="display: none;" id="navigation-python"><a href="">Python</a></li>
<li style="display: none;" id="navigation-prolog"><a href="">Prolog</a></li>
</ul>
- <a class="navbar-brand" id="title"></a>
- </div>
- <div class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<p class="navbar-text" id="signed-in-title">Signed in as Franc Jožef</p>
<li class="dropdown lang-selection">
@@ -65,42 +68,43 @@
</div>
<!-- login screen -->
- <div id="screen_login" style="text-align: center;">
- <h1>CodeQ Login</h1>
- <hr>
- <table style="margin: 0 auto;">
- <tbody>
- <tr>
- <td style="text-align: right;">Username:</td>
- <td><input type="text" name="username" id="username"></td>
- </tr>
- <tr>
- <td style="text-align: right;">Password:</td>
- <td><input type="password" name="password" id="password"></td>
- </tr>
- <tr>
- <td colspan="2" style="text-align: center;">
- <button type="button" id="submit">Login</button>
- </td>
- </tr>
- </tbody>
- </table>
+ <div class="container" id="screen_login">
+ <form class="form-signin">
+ <h2 class="form-signin-heading">Please login</h2>
+ <label for="username" class="sr-only">Username</label>
+ <input type="text" id="username" class="form-control" placeholder="Username" required="" autofocus="">
+ <label for="password" class="sr-only">Password</label>
+ <input type="password" id="password" class="form-control" placeholder="Password" required="">
+ <button class="btn btn-lg btn-default btn-block" type="button" id="submit">Login</button>
+ </form>
</div>
<!-- main screen: programming language selection, settings, etc. -->
- <div id="screen_language" style="text-align: center; display: none;">
- <h1>CodeQ Select Language</h1>
- <hr>
- <br>
- <a id="choose-prolog">Prolog</a><br>
- <a id="choose-python">Python</a>
+ <div class="container" id="screen_language" style="text-align: center; display: none;">
+ <div class="row">
+ <div class="col-lg-4" id="choose-prolog">
+ <img class="img-circle" src=res/prolog.png alt="Generic placeholder image" width="140" height="140">
+ <h2>Prolog</h2>
+ <p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4" id="choose-python">
+ <img class="img-circle" src=res/python.png alt="Generic placeholder image" width="140" height="140">
+ <h2>Python</h2>
+ <p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p>
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4" id="choose-python">
+ <img class="img-circle" src=res/eve.png alt="Generic placeholder image" width="140" height="140">
+ <h2>Robot</h2>
+ <p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p>
+ </div><!-- /.col-lg-4 -->
+ </div><!-- /.row -->
</div>
<!-- problem selection screen for a specific language: groups of problems, with descriptions -->
- <div id="screen_problem" style="display: none;">
+ <div class="container" id="screen_problem" style="display: none;">
<h1 class="language-title"></h1>
<hr>
- <div class="language-description"></div>
+ <h3 class="language-description"></h3>
<ul class="language-problems"></ul>
</div>
@@ -108,6 +112,7 @@
<div class="container-fluid quadrants block1" id="screen_prolog" style="display: none;">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-12 block block1">
+ <h2 class="title"></h2>
<div class="description"></div>
<div class="block-label">Instructions</div>
</div>
@@ -137,6 +142,7 @@
<div class="container-fluid quadrants block1" id="screen_python" style="display: none;">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-12 block block1">
+ <h2 class="title"></h2>
<div class="description"></div>
<div class="block-label">Instructions</div>
</div>
diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js
index 66ab970..f2f060f 100644
--- a/js/codeq/prolog.js
+++ b/js/codeq/prolog.js
@@ -1,356 +1,356 @@
-/**
- * Created by robert on 9/17/15.
- *
- * The prolog state of the state machine. When it is entered it'll prepare the console and code editor and load a sub-state machine which represents the 4 different parts fo the screen.
- */
-
-(function() {
- var subScreens, //this will be the actual (sub)state machine
- stateNameTag = 'stateName', //a tag for data which is added to some html elements
- jqScreen = $('#screen_prolog'), // the screen container element
- //quadrants
- jqDescription = jqScreen.find('.block1'),
- jqCode = jqScreen.find('.block2'),
- jqConsole = jqScreen.find('.block3'),
- jqInfo = jqScreen.find('.block4'),
- jqAllQuadrants = jqDescription.add(jqCode).add(jqConsole).add(jqInfo), // all the quadrants
- // buttons
- jqBtnPlan = jqScreen.find('.btn-plan'),
- jqBtnHint = jqScreen.find('.btn-hint'),
- jqBtnTest = jqScreen.find('.btn-test'),
- jqAllButtons = jqBtnPlan.add(jqBtnHint).add(jqBtnTest), // all the buttons
- // misc
- currentSubState = null,
- transitionEventName = 'mousedown',//event name of the event which will trigger the transition between these substates - the most common transition at least (there are some corner cases on the hint and test buttons -> see the code below)
- substates = {
- 'description': {
- 'enter': function () {
- currentSubState = 'block1';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- },
- 'code': {
- 'enter': function () {
- currentSubState = 'block2';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- },
- 'info': {
- 'enter': function () {
- currentSubState = 'block4';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- },
- 'console': {
- 'enter': function () {
- currentSubState = 'block3';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- }
- };
- var prologHandler; //created when we enter the prolog state and destroyed once we leave it
- codeq.globalStateMachine.register('prolog', {
- 'enter': function (data) {
- $('#navigation-login').css('display', '');
- $('#navigation-language').css('display', '');
- $('#navigation-problem').css('display', '');
- $("#navigation-prolog").addClass("active");
- $('#navigation-prolog').css('display', '');
-
- jqScreen.css('display', '');//we have to show the screen now so the code editor shows its initial values correctly
- prologHandler = createPrologHandler(data.data);
- subScreens = codeq.makeStateMachine(substates);
- subScreens.transition(jqDescription.data(stateNameTag));
-/* Q.delay(100).then(function(){
- jqAllQuadrants.addClass('transition');//for smooth animations - need to be delayed, because otherwise we get some weird "animations" while the page is loading
- }).done();*/
- jqAllButtons.on(transitionEventName, function (event) {
- subScreens.transition('info'); // set focus on the hints quadrant
- event.stopPropagation(); // don't allow the event to go on and trigger further transition
- });
- jqAllQuadrants.on(transitionEventName, function () {
- subScreens.transition($(this).data(stateNameTag));
- });
- },
- 'exit': function () {
- jqAllButtons.off(); // unregister all event handlers
- jqAllQuadrants.off();
- jqScreen.css('display', 'none');
-// jqAllQuadrants.removeClass('transition');
- prologHandler.destroy();
- prologHandler = null;
- subScreens.destroy();
- subScreens = null;
- jqScreen.addClass('block1');
-
- $('#navigation-login').css('display', 'none');
- $('#navigation-language').css('display', 'none');
- $('#navigation-problem').css('display', 'none');
- $("#navigation-prolog").removeClass("active");
- $('#navigation-prolog').css('display', 'none');
- }
- });
-
- jqDescription.data(stateNameTag, 'description');
- jqCode.data(stateNameTag, 'code');
- jqConsole.data(stateNameTag, 'console');
- jqInfo.data(stateNameTag, 'info');
-
- // a constant
- var firstCharacterPos = {'line': 0, 'ch': 0};
-
- var makePrologTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) {
- var promptMode = true, // default: query composition; alternative: query result browsing
- manualStop = false,// if the user stopped showing next answers (false) or if there are no more answers (true)
- terminal = codeq.makeConsole(jqConsole, {
- 'greeting': 'CodeQ Prolog terminal proxy',
- 'autoHistory': true
- }),
- tcs = function terminalCommandSuccess (data) {
- var t, lines, i;
- if (data.code === 0) {
- t = data.terminal;
- terminal.append(t.messages.join('\n'), 'output');
- promptMode = !t.have_more;
- }
- else {
- terminal.append(data.message, 'error');
- promptMode = true;
- }
- if (promptMode) {
- terminal.setLineBuffered();
- terminal.append(manualStop ? '?- ' : '.\n?- ', 'output');
- }
- },
- tcf = function terminalCommandFailed (error) {
- promptMode = true;
- terminal.setLineBuffered();
- terminal.append(error + '\n', 'error');
- terminal.append('?- ', 'output');
- };
-
- terminal.onKeypress = function (c) {
- if (promptMode) return c; // query composition: return the character unchanged
- switch (c) {
- case ' ':
- case ';':
- case '\n':
- return ';'; // show next answer on space, semicolon or enter
- default:
- return '.'; // everything else: stop searching for answers
- }
- };
-
- terminal.onInput = function (command) {
- if (promptMode) {
- promptMode = false;
- manualStop = false;
- terminal.setNotBuffered();
- return codeq.comms.sendQuery({
- 'problem_id': problem_id,
- 'step': 'run',
- 'program': editor.getDoc().getValue(),
- 'query': command,
- 'trace': activityHandler.addAndPurge({'typ': 'slv', 'qry': command})
- }, problem_id).then(tcs, tcf);
- }
- else {
- terminal.append('\n', 'input');
- if (command == ';') {
- // show next answer
- return codeq.comms.sendQuery({
- 'problem_id': problem_id,
- 'step': 'next',
- 'trace': activityHandler.addAndPurge({'typ': 'nxt'})
- }, problem_id).then(tcs, tcf);
- }
- else {
- // stop searching for answers
- manualStop = true;
- return codeq.comms.sendQuery({
- 'problem_id': problem_id,
- 'step': 'end',
- 'trace': activityHandler.addAndPurge({'typ': 'stp'})
- }, problem_id).then(tcs, tcf);
- }
-
- }
- };
-
- terminal.leftmostCol = 3;
- terminal.append('?- ', 'output');
-
- return terminal;
- };
-
- var makeActivityHandler = function (editor, problem_id) {
- var lastActivityMillis = Date.now(),
- deltaActivityMillis = function deltaActivityMillisFunc () {
- var now = Date.now(),
- dt = now - lastActivityMillis;
- lastActivityMillis = now;
- return dt;
- },
- queue = [],
- ts = null,
- timer = function () {
- var promise;
- ts = null;
- if (queue.length === 0) return Q(true);
- promise = codeq.comms.sendActivity(queue, editor.getDoc().getValue(), problem_id);
- queue.length = 0;
- return promise;
- },
- flush = function () {
- clearTimeout(ts);
- return timer();
- };
-
- return {
- 'queueTrace': function (trace) {
- trace['dt'] = deltaActivityMillis();
- queue.push(trace);
- if (ts === null) ts = setTimeout(timer, 10000); // flush every 10 seconds
- return this;
- },
- 'flush': flush,
- 'addAndPurge': function (trace) {
- var accumulatedTrace = queue;
- queue = [];
- trace['dt'] = deltaActivityMillis();
- accumulatedTrace.push(trace);
- if (ts !== null) {
- clearTimeout(ts);
- ts = null;
- }
- return accumulatedTrace;
- }
- };
- };
-
- /**
- * Creates a new handler for the given Prolog assignment definition.
- *
- * @param {PrologTaskDef} info
- * @returns {{destroy: Function, processServerHints: Function}}
- */
- createPrologHandler = function (info) {
- var problem = info.problem,
- jqDescriptionContent = jqDescription.find('.description'),
- jqEditor = jqCode.find('.code_editor'),
- jqTerminal = jqConsole.find('.console'),
- jqHints = jqInfo.find('.hints'),
- editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }),
- activityHandler = makeActivityHandler(editor, problem.id),
- terminal = makePrologTerminalHandler(jqTerminal, editor, problem.id, activityHandler),
- hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint, problem.plan),
- commError = function (error) {
- alert(error);
- };
-
- editor.setValue(info.solution);
- $('#title').text(problem.slug);
- jqDescriptionContent.html(problem.description);
- jqBtnPlan.prop('disabled', (problem.plan || '').length == 0);
-
- editor.on('change', function (instance, changeObj) {
- var doc = editor.getDoc(),
- pos = codeq.codePointCount(doc.getRange(firstCharacterPos, changeObj.from));
-
- if (changeObj.removed) {
- activityHandler.queueTrace({'typ': 'r', 'off': pos, 'len': codeq.codePointCount(changeObj.removed.join(''))});
- }
-
- if (changeObj.text) {
- activityHandler.queueTrace({'typ': 'ins', 'off': pos, 'txt': changeObj.text});
- }
- });
-
- jqBtnPlan.on('click', function () {
- if (!hinter.planNext()) {
- jqBtnPlan.prop('disabled', true).blur();
- }
- });
- jqBtnHint.on('click', function () {
- terminal.append('hint.\n', 'input');
- terminal.inputDisable();
- var doc = editor.getDoc();
- codeq.comms.sendHint({
- 'language': 'prolog',
- 'program': editor.getDoc().getValue(),
- 'problem_id': problem.id
- })
- .then(function (data) {
- if (data.code === 0) {
- hinter.handle(data.hints);
- }
- else {
- terminal.append(data.message + '\n', 'error');
- }
- })
- .fail(commError)
- .fin(function () {
- terminal.inputEnable();
- terminal.append('?- ', 'output');
- })
- .done();
- });
- jqBtnTest.on('click', function () {
- terminal.append('test.\n', 'input');
- terminal.inputDisable();
- var doc = editor.getDoc();
- codeq.comms.sendTest({
- 'language': 'prolog',
- 'program': editor.getDoc().getValue(),
- 'problem_id': problem.id
- })
- .then(function (data) {
- if (data.code === 0) {
- hinter.handle(data.hints);
- }
- else {
- terminal.append(data.message + '\n', 'error');
- }
- })
- .fail(commError)
- .fin(function () {
- terminal.inputEnable();
- terminal.append('?- ', 'output');
- })
- .done();
- });
-
- return {
- destroy: function () {
- $('#title').text('');//empty the title text
- jqAllButtons.off();
- editor.off('change');
- hinter.destroy();
- terminal.destroy();
- jqDescriptionContent.empty();
- jqEditor.empty(); // TODO: perhaps you do not want to "free" the editor, just empty it
- jqTerminal.empty(); // TODO: the same with the console
- jqDescriptionContent = null;
- jqEditor = null;
- jqTerminal = null;
- jqHints = null;
- }
- };
- };
-})();
+/**
+ * Created by robert on 9/17/15.
+ *
+ * The prolog state of the state machine. When it is entered it'll prepare the console and code editor and load a sub-state machine which represents the 4 different parts fo the screen.
+ */
+
+(function() {
+ var subScreens, //this will be the actual (sub)state machine
+ stateNameTag = 'stateName', //a tag for data which is added to some html elements
+ jqScreen = $('#screen_prolog'), // the screen container element
+ //quadrants
+ jqDescription = jqScreen.find('.block1'),
+ jqCode = jqScreen.find('.block2'),
+ jqConsole = jqScreen.find('.block3'),
+ jqInfo = jqScreen.find('.block4'),
+ jqAllQuadrants = jqDescription.add(jqCode).add(jqConsole).add(jqInfo), // all the quadrants
+ // buttons
+ jqBtnPlan = jqScreen.find('.btn-plan'),
+ jqBtnHint = jqScreen.find('.btn-hint'),
+ jqBtnTest = jqScreen.find('.btn-test'),
+ jqAllButtons = jqBtnPlan.add(jqBtnHint).add(jqBtnTest), // all the buttons
+ // misc
+ currentSubState = null,
+ transitionEventName = 'mousedown',//event name of the event which will trigger the transition between these substates - the most common transition at least (there are some corner cases on the hint and test buttons -> see the code below)
+ substates = {
+ 'description': {
+ 'enter': function () {
+ currentSubState = 'block1';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ },
+ 'code': {
+ 'enter': function () {
+ currentSubState = 'block2';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ },
+ 'info': {
+ 'enter': function () {
+ currentSubState = 'block4';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ },
+ 'console': {
+ 'enter': function () {
+ currentSubState = 'block3';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ }
+ };
+ var prologHandler; //created when we enter the prolog state and destroyed once we leave it
+ codeq.globalStateMachine.register('prolog', {
+ 'enter': function (data) {
+ $('#navigation-login').css('display', '');
+ $('#navigation-language').css('display', '');
+ $('#navigation-problem').css('display', '');
+ $("#navigation-prolog").addClass("active");
+ $('#navigation-prolog').css('display', '');
+
+ jqScreen.css('display', '');//we have to show the screen now so the code editor shows its initial values correctly
+ prologHandler = createPrologHandler(data.data);
+ subScreens = codeq.makeStateMachine(substates);
+ subScreens.transition(jqDescription.data(stateNameTag));
+/* Q.delay(100).then(function(){
+ jqAllQuadrants.addClass('transition');//for smooth animations - need to be delayed, because otherwise we get some weird "animations" while the page is loading
+ }).done();*/
+ jqAllButtons.on(transitionEventName, function (event) {
+ subScreens.transition('info'); // set focus on the hints quadrant
+ event.stopPropagation(); // don't allow the event to go on and trigger further transition
+ });
+ jqAllQuadrants.on(transitionEventName, function () {
+ subScreens.transition($(this).data(stateNameTag));
+ });
+ },
+ 'exit': function () {
+ jqAllButtons.off(); // unregister all event handlers
+ jqAllQuadrants.off();
+ jqScreen.css('display', 'none');
+// jqAllQuadrants.removeClass('transition');
+ prologHandler.destroy();
+ prologHandler = null;
+ subScreens.destroy();
+ subScreens = null;
+ jqScreen.addClass('block1');
+
+ $('#navigation-login').css('display', 'none');
+ $('#navigation-language').css('display', 'none');
+ $('#navigation-problem').css('display', 'none');
+ $("#navigation-prolog").removeClass("active");
+ $('#navigation-prolog').css('display', 'none');
+ }
+ });
+
+ jqDescription.data(stateNameTag, 'description');
+ jqCode.data(stateNameTag, 'code');
+ jqConsole.data(stateNameTag, 'console');
+ jqInfo.data(stateNameTag, 'info');
+
+ // a constant
+ var firstCharacterPos = {'line': 0, 'ch': 0};
+
+ var makePrologTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) {
+ var promptMode = true, // default: query composition; alternative: query result browsing
+ manualStop = false,// if the user stopped showing next answers (false) or if there are no more answers (true)
+ terminal = codeq.makeConsole(jqConsole, {
+ 'greeting': 'CodeQ Prolog terminal proxy',
+ 'autoHistory': true
+ }),
+ tcs = function terminalCommandSuccess (data) {
+ var t, lines, i;
+ if (data.code === 0) {
+ t = data.terminal;
+ terminal.append(t.messages.join('\n'), 'output');
+ promptMode = !t.have_more;
+ }
+ else {
+ terminal.append(data.message, 'error');
+ promptMode = true;
+ }
+ if (promptMode) {
+ terminal.setLineBuffered();
+ terminal.append(manualStop ? '?- ' : '.\n?- ', 'output');
+ }
+ },
+ tcf = function terminalCommandFailed (error) {
+ promptMode = true;
+ terminal.setLineBuffered();
+ terminal.append(error + '\n', 'error');
+ terminal.append('?- ', 'output');
+ };
+
+ terminal.onKeypress = function (c) {
+ if (promptMode) return c; // query composition: return the character unchanged
+ switch (c) {
+ case ' ':
+ case ';':
+ case '\n':
+ return ';'; // show next answer on space, semicolon or enter
+ default:
+ return '.'; // everything else: stop searching for answers
+ }
+ };
+
+ terminal.onInput = function (command) {
+ if (promptMode) {
+ promptMode = false;
+ manualStop = false;
+ terminal.setNotBuffered();
+ return codeq.comms.sendQuery({
+ 'problem_id': problem_id,
+ 'step': 'run',
+ 'program': editor.getDoc().getValue(),
+ 'query': command,
+ 'trace': activityHandler.addAndPurge({'typ': 'slv', 'qry': command})
+ }, problem_id).then(tcs, tcf);
+ }
+ else {
+ terminal.append('\n', 'input');
+ if (command == ';') {
+ // show next answer
+ return codeq.comms.sendQuery({
+ 'problem_id': problem_id,
+ 'step': 'next',
+ 'trace': activityHandler.addAndPurge({'typ': 'nxt'})
+ }, problem_id).then(tcs, tcf);
+ }
+ else {
+ // stop searching for answers
+ manualStop = true;
+ return codeq.comms.sendQuery({
+ 'problem_id': problem_id,
+ 'step': 'end',
+ 'trace': activityHandler.addAndPurge({'typ': 'stp'})
+ }, problem_id).then(tcs, tcf);
+ }
+
+ }
+ };
+
+ terminal.leftmostCol = 3;
+ terminal.append('?- ', 'output');
+
+ return terminal;
+ };
+
+ var makeActivityHandler = function (editor, problem_id) {
+ var lastActivityMillis = Date.now(),
+ deltaActivityMillis = function deltaActivityMillisFunc () {
+ var now = Date.now(),
+ dt = now - lastActivityMillis;
+ lastActivityMillis = now;
+ return dt;
+ },
+ queue = [],
+ ts = null,
+ timer = function () {
+ var promise;
+ ts = null;
+ if (queue.length === 0) return Q(true);
+ promise = codeq.comms.sendActivity(queue, editor.getDoc().getValue(), problem_id);
+ queue.length = 0;
+ return promise;
+ },
+ flush = function () {
+ clearTimeout(ts);
+ return timer();
+ };
+
+ return {
+ 'queueTrace': function (trace) {
+ trace['dt'] = deltaActivityMillis();
+ queue.push(trace);
+ if (ts === null) ts = setTimeout(timer, 10000); // flush every 10 seconds
+ return this;
+ },
+ 'flush': flush,
+ 'addAndPurge': function (trace) {
+ var accumulatedTrace = queue;
+ queue = [];
+ trace['dt'] = deltaActivityMillis();
+ accumulatedTrace.push(trace);
+ if (ts !== null) {
+ clearTimeout(ts);
+ ts = null;
+ }
+ return accumulatedTrace;
+ }
+ };
+ };
+
+ /**
+ * Creates a new handler for the given Prolog assignment definition.
+ *
+ * @param {PrologTaskDef} info
+ * @returns {{destroy: Function, processServerHints: Function}}
+ */
+ createPrologHandler = function (info) {
+ var problem = info.problem,
+ jqDescriptionContent = jqDescription.find('.description'),
+ jqEditor = jqCode.find('.code_editor'),
+ jqTerminal = jqConsole.find('.console'),
+ jqHints = jqInfo.find('.hints'),
+ editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }),
+ activityHandler = makeActivityHandler(editor, problem.id),
+ terminal = makePrologTerminalHandler(jqTerminal, editor, problem.id, activityHandler),
+ hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint, problem.plan),
+ commError = function (error) {
+ alert(error);
+ };
+
+ editor.setValue(info.solution);
+ $('#screen_prolog .title').text(problem.slug);
+ jqDescriptionContent.html(problem.description);
+ jqBtnPlan.prop('disabled', (problem.plan || '').length == 0);
+
+ editor.on('change', function (instance, changeObj) {
+ var doc = editor.getDoc(),
+ pos = codeq.codePointCount(doc.getRange(firstCharacterPos, changeObj.from));
+
+ if (changeObj.removed) {
+ activityHandler.queueTrace({'typ': 'r', 'off': pos, 'len': codeq.codePointCount(changeObj.removed.join(''))});
+ }
+
+ if (changeObj.text) {
+ activityHandler.queueTrace({'typ': 'ins', 'off': pos, 'txt': changeObj.text});
+ }
+ });
+
+ jqBtnPlan.on('click', function () {
+ if (!hinter.planNext()) {
+ jqBtnPlan.prop('disabled', true).blur();
+ }
+ });
+ jqBtnHint.on('click', function () {
+ terminal.append('hint.\n', 'input');
+ terminal.inputDisable();
+ var doc = editor.getDoc();
+ codeq.comms.sendHint({
+ 'language': 'prolog',
+ 'program': editor.getDoc().getValue(),
+ 'problem_id': problem.id
+ })
+ .then(function (data) {
+ if (data.code === 0) {
+ hinter.handle(data.hints);
+ }
+ else {
+ terminal.append(data.message + '\n', 'error');
+ }
+ })
+ .fail(commError)
+ .fin(function () {
+ terminal.inputEnable();
+ terminal.append('?- ', 'output');
+ })
+ .done();
+ });
+ jqBtnTest.on('click', function () {
+ terminal.append('test.\n', 'input');
+ terminal.inputDisable();
+ var doc = editor.getDoc();
+ codeq.comms.sendTest({
+ 'language': 'prolog',
+ 'program': editor.getDoc().getValue(),
+ 'problem_id': problem.id
+ })
+ .then(function (data) {
+ if (data.code === 0) {
+ hinter.handle(data.hints);
+ }
+ else {
+ terminal.append(data.message + '\n', 'error');
+ }
+ })
+ .fail(commError)
+ .fin(function () {
+ terminal.inputEnable();
+ terminal.append('?- ', 'output');
+ })
+ .done();
+ });
+
+ return {
+ destroy: function () {
+ $('#screen_prolog .title').text('');//empty the title text
+ jqAllButtons.off();
+ editor.off('change');
+ hinter.destroy();
+ terminal.destroy();
+ jqDescriptionContent.empty();
+ jqEditor.empty(); // TODO: perhaps you do not want to "free" the editor, just empty it
+ jqTerminal.empty(); // TODO: the same with the console
+ jqDescriptionContent = null;
+ jqEditor = null;
+ jqTerminal = null;
+ jqHints = null;
+ }
+ };
+ };
+})();
diff --git a/js/codeq/python.js b/js/codeq/python.js
index b745a20..e80bbb7 100644
--- a/js/codeq/python.js
+++ b/js/codeq/python.js
@@ -1,329 +1,329 @@
-/**
- * Created by robert on 9/17/15.
- *
- * The python state of the state machine. When it is entered it'll prepare the console and code editor and load a sub-state machine which represents the 4 different parts fo the screen.
- *
- * Currentyl it is mostly a copy of the prolog state (the exception is of course the makePythonTerminalHandler instead of makePrologTerminalHandler)
- */
-
-(function() {
- var subScreens, //this will be the actual (sub)state machine
- stateNameTag = 'stateName', //a tag for data which is added to some html elements
- jqScreen = $('#screen_python'), // the screen container element
- //quadrants
- jqDescription = jqScreen.find('.block1'),
- jqCode = jqScreen.find('.block2'),
- jqConsole = jqScreen.find('.block3'),
- jqInfo = jqScreen.find('.block4'),
- jqAllQuadrants = jqDescription.add(jqCode).add(jqConsole).add(jqInfo), // all the quadrants
- // buttons
- jqBtnPlan = jqScreen.find('.btn-plan'),
- jqBtnHint = jqScreen.find('.btn-hint'),
- jqBtnTest = jqScreen.find('.btn-test'),
- jqBtnRun = jqScreen.find('.btn-run'),
- jqBtnStop = jqScreen.find('.btn-stop'),
- jqInfoButtons = jqBtnPlan.add(jqBtnHint).add(jqBtnTest), // all info-focusing buttons
- jqAllButtons = jqInfoButtons.add(jqBtnRun).add(jqBtnStop), // all buttons
- // misc
- currentSubState = null,
- transitionEventName = 'mousedown',//event name of the event which will trigger the transition between these substates - the most common transition at least (there are some corner cases on the hint and test buttons -> see the code below)
- substates = {
- 'description': {
- 'enter': function () {
- currentSubState = 'block1';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- },
- 'code': {
- 'enter': function () {
- currentSubState = 'block2';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- },
- 'info': {
- 'enter': function () {
- currentSubState = 'block4';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- },
- 'console': {
- 'enter': function () {
- currentSubState = 'block3';
- jqScreen.addClass(currentSubState);
- },
- 'exit': function () {
- jqScreen.removeClass(currentSubState);
- currentSubState = null;
- }
- }
- };
- var pythonHandler; //created when we enter the python state and destroyed once we leave it
- codeq.globalStateMachine.register('python', {
- 'enter': function (data) {
- $('#navigation-login').css('display', '');
- $('#navigation-language').css('display', '');
- $('#navigation-problem').css('display', '');
- $("#navigation-python").addClass("active");
- $('#navigation-python').css('display', '');
-
- jqScreen.css('display', '');//we have to show the screen now so the code editor shows its initial values correctly
- pythonHandler = createPythonHandler(data.data);
- subScreens = codeq.makeStateMachine(substates);
- subScreens.transition(jqDescription.data(stateNameTag));
-/* Q.delay(100).then(function(){
- jqAllQuadrants.addClass('transition');//for smooth animations - need to be delayed, because otherwise we get some weird "animations" while the page is loading
- }).done();*/
- jqInfoButtons.on(transitionEventName, function (event) {
- subScreens.transition('info'); // set focus on the hints quadrant
- event.stopPropagation(); // don't allow the event to go on and trigger further transition
- });
- jqBtnRun.on(transitionEventName, function (event) {
- subScreens.transition('console'); // set focus on the hints quadrant
- event.stopPropagation(); // don't allow the event to go on and trigger further transition
- });
-
- jqAllQuadrants.on(transitionEventName, function () {
- subScreens.transition($(this).data(stateNameTag));
- });
- },
- 'exit': function () {
- jqAllButtons.off(); // unregister all event handlers
- jqAllQuadrants.off();
- jqScreen.css('display', 'none');
-// jqAllQuadrants.removeClass('transition');
- pythonHandler.destroy();
- pythonHandler = null;
- subScreens.destroy();
- subScreens = null;
- jqScreen.addClass('block1');
-
- $('#navigation-login').css('display', 'none');
- $('#navigation-language').css('display', 'none');
- $('#navigation-problem').css('display', 'none');
- $("#navigation-python").removeClass("active");
- $('#navigation-python').css('display', 'none');
- }
- });
-
- jqDescription.data(stateNameTag, 'description');
- jqCode.data(stateNameTag, 'code');
- jqConsole.data(stateNameTag, 'console');
- jqInfo.data(stateNameTag, 'info');
-
- // a constant
- var firstCharacterPos = {'line': 0, 'ch': 0};
-
- var makePythonTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) {
- var terminal = codeq.makeConsole(jqConsole, {
- 'greeting': 'CodeQ Python terminal proxy',
- 'autoHistory': true
- }),
- tcs = function terminalCommandSuccess (data) {
- if (data.code !== 0) {
- terminal.append(data.message, 'error');
- }
- },
- tcf = function terminalCommandFailed (error) {
- terminal.append(error + '\n', 'error');
- };
-
- terminal.onInput = function (text) {
- terminal.leftmostCol = 0;
- return codeq.comms.sendPythonPush({
- 'text': text + '\n'
- }).then(tcs, tcf);
- };
-
- codeq.comms.on('terminal_output', function (data) {
- var text = data.text,
- lines = text.split('\n');
- terminal.append(text, 'output');
- terminal.leftmostCol = lines[lines.length-1].length;
- });
-
- terminal.leftmostCol = 1;
-
- return terminal;
- };
-
- var makeActivityHandler = function (editor, problem_id) {
- var lastActivityMillis = Date.now(),
- deltaActivityMillis = function deltaActivityMillisFunc () {
- var now = Date.now(),
- dt = now - lastActivityMillis;
- lastActivityMillis = now;
- return dt;
- },
- queue = [],
- ts = null,
- timer = function () {
- var promise;
- ts = null;
- if (queue.length === 0) return Q(true);
- promise = codeq.comms.sendActivity(queue, editor.getDoc().getValue(), problem_id);
- queue.length = 0;
- return promise;
- },
- flush = function () {
- clearTimeout(ts);
- return timer();
- };
-
- return {
- 'queueTrace': function (trace) {
- trace['dt'] = deltaActivityMillis();
- queue.push(trace);
- if (ts === null) ts = setTimeout(timer, 10000); // flush every 10 seconds
- return this;
- },
- 'flush': flush,
- 'addAndPurge': function (trace) {
- var accumulatedTrace = queue;
- queue = [];
- trace['dt'] = deltaActivityMillis();
- accumulatedTrace.push(trace);
- if (ts !== null) {
- clearTimeout(ts);
- ts = null;
- }
- return accumulatedTrace;
- }
- };
- };
-
-
-
- /**
- * Creates a new handler for the given Prolog assignment definition.
- *
- * @param {PrologTaskDef} info
- * @returns {{destroy: Function, processServerHints: Function}}
- */
- var createPythonHandler = function (info) {
- var problem = info.problem,
- jqDescriptionContent = jqDescription.find('.description'),
- jqEditor = jqCode.find('.code_editor'),
- jqTerminal = jqConsole.find('.console'),
- jqHints = jqInfo.find('.hints'),
- editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true, mode: 'python' }),
- activityHandler = makeActivityHandler(editor, problem.id),
- terminal = makePythonTerminalHandler(jqTerminal, editor, problem.id, activityHandler),
- hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint, problem.plan),
- commError = function (error) {
- alert(error);
- };
-
- editor.setValue(info.solution);
- $('#title').text(problem.slug);
- jqDescriptionContent.html(problem.description);
- jqBtnPlan.prop('disabled', (problem.plan || '').length == 0);
-
- editor.on('change', function (instance, changeObj) {
- var doc = editor.getDoc(),
- pos = codeq.codePointCount(doc.getRange(firstCharacterPos, changeObj.from));
-
- if (changeObj.removed) {
- activityHandler.queueTrace({'typ': 'rm', 'off': pos, 'len': codeq.codePointCount(changeObj.removed.join(''))});
- }
-
- if (changeObj.text) {
- activityHandler.queueTrace({'typ': 'ins', 'off': pos, 'txt': changeObj.text});
- }
- });
-
- jqBtnPlan.on('click', function () {
- if (!hinter.planNext()) {
- jqBtnPlan.prop('disabled', true).blur();
- }
- });
- jqBtnHint.on('click', function () {
- var doc = editor.getDoc();
- codeq.comms.sendHint({
- 'language': 'python',
- 'program': editor.getDoc().getValue(),
- 'problem_id': problem.id
- })
- .then(function (data) {
- if (data.code === 0) {
- hinter.handle(data.hints);
- }
- else {
- terminal.append('error: ' + data.message);
- }
- })
- .fail(commError)
- .done();
- });
- jqBtnTest.on('click', function () {
- var doc = editor.getDoc();
- codeq.comms.sendTest({
- 'language': 'python',
- 'program': editor.getDoc().getValue(),
- 'problem_id': problem.id
- })
- .then(function (data) {
- if (data.code === 0) {
- hinter.handle(data.hints);
- }
- else {
- terminal.append('error: ' + data.message);
- }
- })
- .fail(commError)
- .done();
- });
- jqBtnRun.on('click', function () {
- var program = editor.getDoc().getValue();
- codeq.comms.sendPythonStop({})
- .then(function () {
- codeq.comms.sendPythonExec({
- 'program': program
- });
- })
- .fail(commError)
- .done();
- // focus the terminal
- jqTerminal.click();
- });
- jqBtnStop.on('click', function () {
- codeq.comms.sendPythonStop({})
- .fail(commError)
- .done();
- });
-
- // TODO first line of interpreter output is buffered without this, why?
- codeq.comms.sendPythonPush({
- 'text': ''
- });
-
- return {
- destroy: function () {
- $('#title').text('');//empty the title text
- jqAllButtons.off();
- editor.off('change');
- codeq.comms.off('terminal_output'); // stop listening for the terminal events from server
- hinter.destroy();
- terminal.destroy();
- jqDescriptionContent.empty();
- jqEditor.empty(); // TODO: perhaps you do not want to "free" the editor, just empty it
- jqTerminal.empty(); // TODO: the same with the console
- jqDescriptionContent = null;
- jqEditor = null;
- jqTerminal = null;
- jqHints = null;
- }
- };
- };
-})();
+/**
+ * Created by robert on 9/17/15.
+ *
+ * The python state of the state machine. When it is entered it'll prepare the console and code editor and load a sub-state machine which represents the 4 different parts fo the screen.
+ *
+ * Currentyl it is mostly a copy of the prolog state (the exception is of course the makePythonTerminalHandler instead of makePrologTerminalHandler)
+ */
+
+(function() {
+ var subScreens, //this will be the actual (sub)state machine
+ stateNameTag = 'stateName', //a tag for data which is added to some html elements
+ jqScreen = $('#screen_python'), // the screen container element
+ //quadrants
+ jqDescription = jqScreen.find('.block1'),
+ jqCode = jqScreen.find('.block2'),
+ jqConsole = jqScreen.find('.block3'),
+ jqInfo = jqScreen.find('.block4'),
+ jqAllQuadrants = jqDescription.add(jqCode).add(jqConsole).add(jqInfo), // all the quadrants
+ // buttons
+ jqBtnPlan = jqScreen.find('.btn-plan'),
+ jqBtnHint = jqScreen.find('.btn-hint'),
+ jqBtnTest = jqScreen.find('.btn-test'),
+ jqBtnRun = jqScreen.find('.btn-run'),
+ jqBtnStop = jqScreen.find('.btn-stop'),
+ jqInfoButtons = jqBtnPlan.add(jqBtnHint).add(jqBtnTest), // all info-focusing buttons
+ jqAllButtons = jqInfoButtons.add(jqBtnRun).add(jqBtnStop), // all buttons
+ // misc
+ currentSubState = null,
+ transitionEventName = 'mousedown',//event name of the event which will trigger the transition between these substates - the most common transition at least (there are some corner cases on the hint and test buttons -> see the code below)
+ substates = {
+ 'description': {
+ 'enter': function () {
+ currentSubState = 'block1';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ },
+ 'code': {
+ 'enter': function () {
+ currentSubState = 'block2';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ },
+ 'info': {
+ 'enter': function () {
+ currentSubState = 'block4';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ },
+ 'console': {
+ 'enter': function () {
+ currentSubState = 'block3';
+ jqScreen.addClass(currentSubState);
+ },
+ 'exit': function () {
+ jqScreen.removeClass(currentSubState);
+ currentSubState = null;
+ }
+ }
+ };
+ var pythonHandler; //created when we enter the python state and destroyed once we leave it
+ codeq.globalStateMachine.register('python', {
+ 'enter': function (data) {
+ $('#navigation-login').css('display', '');
+ $('#navigation-language').css('display', '');
+ $('#navigation-problem').css('display', '');
+ $("#navigation-python").addClass("active");
+ $('#navigation-python').css('display', '');
+
+ jqScreen.css('display', '');//we have to show the screen now so the code editor shows its initial values correctly
+ pythonHandler = createPythonHandler(data.data);
+ subScreens = codeq.makeStateMachine(substates);
+ subScreens.transition(jqDescription.data(stateNameTag));
+/* Q.delay(100).then(function(){
+ jqAllQuadrants.addClass('transition');//for smooth animations - need to be delayed, because otherwise we get some weird "animations" while the page is loading
+ }).done();*/
+ jqInfoButtons.on(transitionEventName, function (event) {
+ subScreens.transition('info'); // set focus on the hints quadrant
+ event.stopPropagation(); // don't allow the event to go on and trigger further transition
+ });
+ jqBtnRun.on(transitionEventName, function (event) {
+ subScreens.transition('console'); // set focus on the hints quadrant
+ event.stopPropagation(); // don't allow the event to go on and trigger further transition
+ });
+
+ jqAllQuadrants.on(transitionEventName, function () {
+ subScreens.transition($(this).data(stateNameTag));
+ });
+ },
+ 'exit': function () {
+ jqAllButtons.off(); // unregister all event handlers
+ jqAllQuadrants.off();
+ jqScreen.css('display', 'none');
+// jqAllQuadrants.removeClass('transition');
+ pythonHandler.destroy();
+ pythonHandler = null;
+ subScreens.destroy();
+ subScreens = null;
+ jqScreen.addClass('block1');
+
+ $('#navigation-login').css('display', 'none');
+ $('#navigation-language').css('display', 'none');
+ $('#navigation-problem').css('display', 'none');
+ $("#navigation-python").removeClass("active");
+ $('#navigation-python').css('display', 'none');
+ }
+ });
+
+ jqDescription.data(stateNameTag, 'description');
+ jqCode.data(stateNameTag, 'code');
+ jqConsole.data(stateNameTag, 'console');
+ jqInfo.data(stateNameTag, 'info');
+
+ // a constant
+ var firstCharacterPos = {'line': 0, 'ch': 0};
+
+ var makePythonTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) {
+ var terminal = codeq.makeConsole(jqConsole, {
+ 'greeting': 'CodeQ Python terminal proxy',
+ 'autoHistory': true
+ }),
+ tcs = function terminalCommandSuccess (data) {
+ if (data.code !== 0) {
+ terminal.append(data.message, 'error');
+ }
+ },
+ tcf = function terminalCommandFailed (error) {
+ terminal.append(error + '\n', 'error');
+ };
+
+ terminal.onInput = function (text) {
+ terminal.leftmostCol = 0;
+ return codeq.comms.sendPythonPush({
+ 'text': text + '\n'
+ }).then(tcs, tcf);
+ };
+
+ codeq.comms.on('terminal_output', function (data) {
+ var text = data.text,
+ lines = text.split('\n');
+ terminal.append(text, 'output');
+ terminal.leftmostCol = lines[lines.length-1].length;
+ });
+
+ terminal.leftmostCol = 1;
+
+ return terminal;
+ };
+
+ var makeActivityHandler = function (editor, problem_id) {
+ var lastActivityMillis = Date.now(),
+ deltaActivityMillis = function deltaActivityMillisFunc () {
+ var now = Date.now(),
+ dt = now - lastActivityMillis;
+ lastActivityMillis = now;
+ return dt;
+ },
+ queue = [],
+ ts = null,
+ timer = function () {
+ var promise;
+ ts = null;
+ if (queue.length === 0) return Q(true);
+ promise = codeq.comms.sendActivity(queue, editor.getDoc().getValue(), problem_id);
+ queue.length = 0;
+ return promise;
+ },
+ flush = function () {
+ clearTimeout(ts);
+ return timer();
+ };
+
+ return {
+ 'queueTrace': function (trace) {
+ trace['dt'] = deltaActivityMillis();
+ queue.push(trace);
+ if (ts === null) ts = setTimeout(timer, 10000); // flush every 10 seconds
+ return this;
+ },
+ 'flush': flush,
+ 'addAndPurge': function (trace) {
+ var accumulatedTrace = queue;
+ queue = [];
+ trace['dt'] = deltaActivityMillis();
+ accumulatedTrace.push(trace);
+ if (ts !== null) {
+ clearTimeout(ts);
+ ts = null;
+ }
+ return accumulatedTrace;
+ }
+ };
+ };
+
+
+
+ /**
+ * Creates a new handler for the given Prolog assignment definition.
+ *
+ * @param {PrologTaskDef} info
+ * @returns {{destroy: Function, processServerHints: Function}}
+ */
+ var createPythonHandler = function (info) {
+ var problem = info.problem,
+ jqDescriptionContent = jqDescription.find('.description'),
+ jqEditor = jqCode.find('.code_editor'),
+ jqTerminal = jqConsole.find('.console'),
+ jqHints = jqInfo.find('.hints'),
+ editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true, mode: 'python' }),
+ activityHandler = makeActivityHandler(editor, problem.id),
+ terminal = makePythonTerminalHandler(jqTerminal, editor, problem.id, activityHandler),
+ hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint, problem.plan),
+ commError = function (error) {
+ alert(error);
+ };
+
+ editor.setValue(info.solution);
+ $('#screen_python .title').text(problem.slug);
+ jqDescriptionContent.html(problem.description);
+ jqBtnPlan.prop('disabled', (problem.plan || '').length == 0);
+
+ editor.on('change', function (instance, changeObj) {
+ var doc = editor.getDoc(),
+ pos = codeq.codePointCount(doc.getRange(firstCharacterPos, changeObj.from));
+
+ if (changeObj.removed) {
+ activityHandler.queueTrace({'typ': 'rm', 'off': pos, 'len': codeq.codePointCount(changeObj.removed.join(''))});
+ }
+
+ if (changeObj.text) {
+ activityHandler.queueTrace({'typ': 'ins', 'off': pos, 'txt': changeObj.text});
+ }
+ });
+
+ jqBtnPlan.on('click', function () {
+ if (!hinter.planNext()) {
+ jqBtnPlan.prop('disabled', true).blur();
+ }
+ });
+ jqBtnHint.on('click', function () {
+ var doc = editor.getDoc();
+ codeq.comms.sendHint({
+ 'language': 'python',
+ 'program': editor.getDoc().getValue(),
+ 'problem_id': problem.id
+ })
+ .then(function (data) {
+ if (data.code === 0) {
+ hinter.handle(data.hints);
+ }
+ else {
+ terminal.append('error: ' + data.message);
+ }
+ })
+ .fail(commError)
+ .done();
+ });
+ jqBtnTest.on('click', function () {
+ var doc = editor.getDoc();
+ codeq.comms.sendTest({
+ 'language': 'python',
+ 'program': editor.getDoc().getValue(),
+ 'problem_id': problem.id
+ })
+ .then(function (data) {
+ if (data.code === 0) {
+ hinter.handle(data.hints);
+ }
+ else {
+ terminal.append('error: ' + data.message);
+ }
+ })
+ .fail(commError)
+ .done();
+ });
+ jqBtnRun.on('click', function () {
+ var program = editor.getDoc().getValue();
+ codeq.comms.sendPythonStop({})
+ .then(function () {
+ codeq.comms.sendPythonExec({
+ 'program': program
+ });
+ })
+ .fail(commError)
+ .done();
+ // focus the terminal
+ jqTerminal.click();
+ });
+ jqBtnStop.on('click', function () {
+ codeq.comms.sendPythonStop({})
+ .fail(commError)
+ .done();
+ });
+
+ // TODO first line of interpreter output is buffered without this, why?
+ codeq.comms.sendPythonPush({
+ 'text': ''
+ });
+
+ return {
+ destroy: function () {
+ $('#screen_python .title').text('');//empty the title text
+ jqAllButtons.off();
+ editor.off('change');
+ codeq.comms.off('terminal_output'); // stop listening for the terminal events from server
+ hinter.destroy();
+ terminal.destroy();
+ jqDescriptionContent.empty();
+ jqEditor.empty(); // TODO: perhaps you do not want to "free" the editor, just empty it
+ jqTerminal.empty(); // TODO: the same with the console
+ jqDescriptionContent = null;
+ jqEditor = null;
+ jqTerminal = null;
+ jqHints = null;
+ }
+ };
+ };
+})();
diff --git a/res/eve.png b/res/eve.png
new file mode 100644
index 0000000..3544c5e
--- /dev/null
+++ b/res/eve.png
Binary files differ
diff --git a/res/prolog.png b/res/prolog.png
new file mode 100644
index 0000000..90ae432
--- /dev/null
+++ b/res/prolog.png
Binary files differ
diff --git a/res/python.png b/res/python.png
new file mode 100644
index 0000000..f89e6c5
--- /dev/null
+++ b/res/python.png
Binary files differ