diff options
-rw-r--r-- | js/codeq/prolog.js | 728 | ||||
-rw-r--r-- | js/codeq/python.js | 668 |
2 files changed, 698 insertions, 698 deletions
diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js index cd3fcb4..980a60c 100644 --- a/js/codeq/prolog.js +++ b/js/codeq/prolog.js @@ -1,364 +1,364 @@ -/**
- * 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 (problemDef, commonHints, currentSolution) {
- $('#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(problemDef, commonHints, currentSolution);
- 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':
- case 'r':
- return ';'; // show next answer on space, semicolon, 'n' or 'r'
- 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;
- }
- };
- };
-
- codeq.on('init', function (args) {
- codeq.tr.registerDictionary('prolog', codeq.tr.emptyDictionary); // to make the translator happy, when this screen is not active
- });
-
- /**
- * Creates a new handler for the given Prolog assignment definition.
- *
- * @param {PrologTaskDef} info
- * @returns {{destroy: Function, processServerHints: Function}}
- */
- createPrologHandler = function (problemDef, commonHints, currentSolution) {
- 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, problemDef.id),
- terminal = makePrologTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler),
- hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'prolog_hints', problemDef.hint, commonHints, problemDef.plan),
- commError = function (error) {
- alert(error);
- };
-
- codeq.tr.registerDictionary('prolog', problemDef.translations);
- codeq.tr.translateDom(jqScreen);
- if (currentSolution) editor.setValue(currentSolution);
-// $('#screen_prolog .title').text(problem.slug);
-// jqDescriptionContent.html(problem.description);
- jqBtnPlan.prop('disabled', ((problemDef.plan && problemDef.plan.en) || []).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': problemDef.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': problemDef.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;
- codeq.tr.registerDictionary('prolog', codeq.tr.emptyDictionary);
- }
- };
- };
-})();
+/** + * 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 (problemDef, commonHints, currentSolution) { + $('#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(problemDef, commonHints, currentSolution); + 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': + case 'r': + return ';'; // show next answer on space, semicolon, 'n' or 'r' + 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; + } + }; + }; + + codeq.on('init', function (args) { + codeq.tr.registerDictionary('prolog', codeq.tr.emptyDictionary); // to make the translator happy, when this screen is not active + }); + + /** + * Creates a new handler for the given Prolog assignment definition. + * + * @param {PrologTaskDef} info + * @returns {{destroy: Function, processServerHints: Function}} + */ + createPrologHandler = function (problemDef, commonHints, currentSolution) { + 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, problemDef.id), + terminal = makePrologTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler), + hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'prolog_hints', problemDef.hint, commonHints, problemDef.plan), + commError = function (error) { + alert(error); + }; + + codeq.tr.registerDictionary('prolog', problemDef.translations); + codeq.tr.translateDom(jqScreen); + if (currentSolution) editor.setValue(currentSolution); +// $('#screen_prolog .title').text(problem.slug); +// jqDescriptionContent.html(problem.description); + jqBtnPlan.prop('disabled', ((problemDef.plan && problemDef.plan.en) || []).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': problemDef.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': problemDef.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; + codeq.tr.registerDictionary('prolog', codeq.tr.emptyDictionary); + } + }; + }; +})(); diff --git a/js/codeq/python.js b/js/codeq/python.js index 8b66881..009c4a5 100644 --- a/js/codeq/python.js +++ b/js/codeq/python.js @@ -1,334 +1,334 @@ -/**
- * 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 (problemDef, commonHints, currentSolution) {
- $('#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(problemDef, commonHints, currentSolution);
- 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;
- }
- };
- };
-
- codeq.on('init', function (args) {
- codeq.tr.registerDictionary('python', codeq.tr.emptyDictionary); // to make the translator happy, when this screen is not active
- });
-
- /**
- * Creates a new handler for the given Prolog assignment definition.
- *
- * @param {PrologTaskDef} info
- * @returns {{destroy: Function, processServerHints: Function}}
- */
- var createPythonHandler = function (problemDef, commonHints, currentSolution) {
- 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, problemDef.id),
- terminal = makePythonTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler),
- hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'python_hints', problemDef.hint, commonHints, problemDef.plan),
- commError = function (error) {
- alert(error);
- };
-
- codeq.tr.registerDictionary('python', problemDef.translations);
- codeq.tr.translateDom(jqScreen);
- if (currentSolution) editor.setValue(currentSolution);
-// $('#screen_python .title').text(problem.slug);
-// jqDescriptionContent.html(problem.description);
- jqBtnPlan.prop('disabled', ((problemDef.plan && problemDef.plan.en) || []).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': problemDef.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': problemDef.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;
- codeq.tr.registerDictionary('python', codeq.tr.emptyDictionary);
- }
- };
- };
-})();
+/** + * 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 (problemDef, commonHints, currentSolution) { + $('#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(problemDef, commonHints, currentSolution); + 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; + } + }; + }; + + codeq.on('init', function (args) { + codeq.tr.registerDictionary('python', codeq.tr.emptyDictionary); // to make the translator happy, when this screen is not active + }); + + /** + * Creates a new handler for the given Prolog assignment definition. + * + * @param {PrologTaskDef} info + * @returns {{destroy: Function, processServerHints: Function}} + */ + var createPythonHandler = function (problemDef, commonHints, currentSolution) { + 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, problemDef.id), + terminal = makePythonTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler), + hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'python_hints', problemDef.hint, commonHints, problemDef.plan), + commError = function (error) { + alert(error); + }; + + codeq.tr.registerDictionary('python', problemDef.translations); + codeq.tr.translateDom(jqScreen); + if (currentSolution) editor.setValue(currentSolution); +// $('#screen_python .title').text(problem.slug); +// jqDescriptionContent.html(problem.description); + jqBtnPlan.prop('disabled', ((problemDef.plan && problemDef.plan.en) || []).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': problemDef.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': problemDef.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; + codeq.tr.registerDictionary('python', codeq.tr.emptyDictionary); + } + }; + }; +})(); |