diff options
Diffstat (limited to 'js/codeq/python.js')
-rw-r--r-- | js/codeq/python.js | 658 |
1 files changed, 329 insertions, 329 deletions
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;
+ }
+ };
+ };
+})();
|