summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/codemirror/python.js358
-rw-r--r--js/python.js331
2 files changed, 689 insertions, 0 deletions
diff --git a/js/codemirror/python.js b/js/codemirror/python.js
new file mode 100644
index 0000000..e5a0971
--- /dev/null
+++ b/js/codemirror/python.js
@@ -0,0 +1,358 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ function wordRegexp(words) {
+ return new RegExp("^((" + words.join(")|(") + "))\\b");
+ }
+
+ var wordOperators = wordRegexp(["and", "or", "not", "is"]);
+ var commonKeywords = ["as", "assert", "break", "class", "continue",
+ "def", "del", "elif", "else", "except", "finally",
+ "for", "from", "global", "if", "import",
+ "lambda", "pass", "raise", "return",
+ "try", "while", "with", "yield", "in"];
+ var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
+ "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
+ "enumerate", "eval", "filter", "float", "format", "frozenset",
+ "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
+ "input", "int", "isinstance", "issubclass", "iter", "len",
+ "list", "locals", "map", "max", "memoryview", "min", "next",
+ "object", "oct", "open", "ord", "pow", "property", "range",
+ "repr", "reversed", "round", "set", "setattr", "slice",
+ "sorted", "staticmethod", "str", "sum", "super", "tuple",
+ "type", "vars", "zip", "__import__", "NotImplemented",
+ "Ellipsis", "__debug__"];
+ var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
+ "file", "intern", "long", "raw_input", "reduce", "reload",
+ "unichr", "unicode", "xrange", "False", "True", "None"],
+ keywords: ["exec", "print"]};
+ var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
+ keywords: ["nonlocal", "False", "True", "None", "async", "await"]};
+
+ CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
+
+ function top(state) {
+ return state.scopes[state.scopes.length - 1];
+ }
+
+ CodeMirror.defineMode("python", function(conf, parserConf) {
+ var ERRORCLASS = "error";
+
+ var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]");
+ var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
+ var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
+ var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
+
+ if (parserConf.version && parseInt(parserConf.version, 10) == 3){
+ // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
+ var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!@]");
+ var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*");
+ } else {
+ var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
+ var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
+ }
+
+ var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
+
+ var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
+ if(parserConf.extra_keywords != undefined){
+ myKeywords = myKeywords.concat(parserConf.extra_keywords);
+ }
+ if(parserConf.extra_builtins != undefined){
+ myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
+ }
+ if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
+ myKeywords = myKeywords.concat(py3.keywords);
+ myBuiltins = myBuiltins.concat(py3.builtins);
+ var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
+ } else {
+ myKeywords = myKeywords.concat(py2.keywords);
+ myBuiltins = myBuiltins.concat(py2.builtins);
+ var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
+ }
+ var keywords = wordRegexp(myKeywords);
+ var builtins = wordRegexp(myBuiltins);
+
+ // tokenizers
+ function tokenBase(stream, state) {
+ // Handle scope changes
+ if (stream.sol() && top(state).type == "py") {
+ var scopeOffset = top(state).offset;
+ if (stream.eatSpace()) {
+ var lineOffset = stream.indentation();
+ if (lineOffset > scopeOffset)
+ pushScope(stream, state, "py");
+ else if (lineOffset < scopeOffset && dedent(stream, state))
+ state.errorToken = true;
+ return null;
+ } else {
+ var style = tokenBaseInner(stream, state);
+ if (scopeOffset > 0 && dedent(stream, state))
+ style += " " + ERRORCLASS;
+ return style;
+ }
+ }
+ return tokenBaseInner(stream, state);
+ }
+
+ function tokenBaseInner(stream, state) {
+ if (stream.eatSpace()) return null;
+
+ var ch = stream.peek();
+
+ // Handle Comments
+ if (ch == "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
+
+ // Handle Number Literals
+ if (stream.match(/^[0-9\.]/, false)) {
+ var floatLiteral = false;
+ // Floats
+ if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
+ if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
+ if (stream.match(/^\.\d+/)) { floatLiteral = true; }
+ if (floatLiteral) {
+ // Float literals may be "imaginary"
+ stream.eat(/J/i);
+ return "number";
+ }
+ // Integers
+ var intLiteral = false;
+ // Hex
+ if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
+ // Binary
+ if (stream.match(/^0b[01]+/i)) intLiteral = true;
+ // Octal
+ if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
+ // Decimal
+ if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
+ // Decimal literals may be "imaginary"
+ stream.eat(/J/i);
+ // TODO - Can you have imaginary longs?
+ intLiteral = true;
+ }
+ // Zero by itself with no other piece of number.
+ if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
+ if (intLiteral) {
+ // Integer literals may be "long"
+ stream.eat(/L/i);
+ return "number";
+ }
+ }
+
+ // Handle Strings
+ if (stream.match(stringPrefixes)) {
+ state.tokenize = tokenStringFactory(stream.current());
+ return state.tokenize(stream, state);
+ }
+
+ // Handle operators and Delimiters
+ if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
+ return null;
+
+ if (stream.match(doubleOperators) || stream.match(singleOperators))
+ return "operator";
+
+ if (stream.match(singleDelimiters))
+ return null;
+
+ if (stream.match(keywords) || stream.match(wordOperators))
+ return "keyword";
+
+ if (stream.match(builtins))
+ return "builtin";
+
+ if (stream.match(/^(self|cls)\b/))
+ return "variable-2";
+
+ if (stream.match(identifiers)) {
+ if (state.lastToken == "def" || state.lastToken == "class")
+ return "def";
+ return "variable";
+ }
+
+ // Handle non-detected items
+ stream.next();
+ return ERRORCLASS;
+ }
+
+ function tokenStringFactory(delimiter) {
+ while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
+ delimiter = delimiter.substr(1);
+
+ var singleline = delimiter.length == 1;
+ var OUTCLASS = "string";
+
+ function tokenString(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^'"\\]/);
+ if (stream.eat("\\")) {
+ stream.next();
+ if (singleline && stream.eol())
+ return OUTCLASS;
+ } else if (stream.match(delimiter)) {
+ state.tokenize = tokenBase;
+ return OUTCLASS;
+ } else {
+ stream.eat(/['"]/);
+ }
+ }
+ if (singleline) {
+ if (parserConf.singleLineStringErrors)
+ return ERRORCLASS;
+ else
+ state.tokenize = tokenBase;
+ }
+ return OUTCLASS;
+ }
+ tokenString.isString = true;
+ return tokenString;
+ }
+
+ function pushScope(stream, state, type) {
+ var offset = 0, align = null;
+ if (type == "py") {
+ while (top(state).type != "py")
+ state.scopes.pop();
+ }
+ offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
+ if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
+ align = stream.column() + 1;
+ state.scopes.push({offset: offset, type: type, align: align});
+ }
+
+ function dedent(stream, state) {
+ var indented = stream.indentation();
+ while (top(state).offset > indented) {
+ if (top(state).type != "py") return true;
+ state.scopes.pop();
+ }
+ return top(state).offset != indented;
+ }
+
+ function tokenLexer(stream, state) {
+ var style = state.tokenize(stream, state);
+ var current = stream.current();
+
+ // Handle '.' connected identifiers
+ if (current == ".") {
+ style = stream.match(identifiers, false) ? null : ERRORCLASS;
+ if (style == null && state.lastStyle == "meta") {
+ // Apply 'meta' style to '.' connected identifiers when
+ // appropriate.
+ style = "meta";
+ }
+ return style;
+ }
+
+ // Handle decorators
+ if (current == "@"){
+ if(parserConf.version && parseInt(parserConf.version, 10) == 3){
+ return stream.match(identifiers, false) ? "meta" : "operator";
+ } else {
+ return stream.match(identifiers, false) ? "meta" : ERRORCLASS;
+ }
+ }
+
+ if ((style == "variable" || style == "builtin")
+ && state.lastStyle == "meta")
+ style = "meta";
+
+ // Handle scope changes.
+ if (current == "pass" || current == "return")
+ state.dedent += 1;
+
+ if (current == "lambda") state.lambda = true;
+ if (current == ":" && !state.lambda && top(state).type == "py")
+ pushScope(stream, state, "py");
+
+ var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
+ if (delimiter_index != -1)
+ pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+
+ delimiter_index = "])}".indexOf(current);
+ if (delimiter_index != -1) {
+ if (top(state).type == current) state.scopes.pop();
+ else return ERRORCLASS;
+ }
+ if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
+ if (state.scopes.length > 1) state.scopes.pop();
+ state.dedent -= 1;
+ }
+
+ return style;
+ }
+
+ var external = {
+ startState: function(basecolumn) {
+ return {
+ tokenize: tokenBase,
+ scopes: [{offset: basecolumn || 0, type: "py", align: null}],
+ lastStyle: null,
+ lastToken: null,
+ lambda: false,
+ dedent: 0
+ };
+ },
+
+ token: function(stream, state) {
+ var addErr = state.errorToken;
+ if (addErr) state.errorToken = false;
+ var style = tokenLexer(stream, state);
+
+ state.lastStyle = style;
+
+ var current = stream.current();
+ if (current && style)
+ state.lastToken = current;
+
+ if (stream.eol() && state.lambda)
+ state.lambda = false;
+ return addErr ? style + " " + ERRORCLASS : style;
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != tokenBase)
+ return state.tokenize.isString ? CodeMirror.Pass : 0;
+
+ var scope = top(state);
+ var closing = textAfter && textAfter.charAt(0) == scope.type;
+ if (scope.align != null)
+ return scope.align - (closing ? 1 : 0);
+ else if (closing && state.scopes.length > 1)
+ return state.scopes[state.scopes.length - 2].offset;
+ else
+ return scope.offset;
+ },
+
+ closeBrackets: {triples: "'\""},
+ lineComment: "#",
+ fold: "indent"
+ };
+ return external;
+ });
+
+ CodeMirror.defineMIME("text/x-python", "python");
+
+ var words = function(str) { return str.split(" "); };
+
+ CodeMirror.defineMIME("text/x-cython", {
+ name: "python",
+ extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
+ "extern gil include nogil property public"+
+ "readonly struct union DEF IF ELIF ELSE")
+ });
+
+});
diff --git a/js/python.js b/js/python.js
new file mode 100644
index 0000000..f4703b2
--- /dev/null
+++ b/js/python.js
@@ -0,0 +1,331 @@
+/**
+ * Handler for a Python assignment. Works with the corresponding python.html content.
+ */
+
+(function () {
+ // a constant
+ var firstCharacterPos = {'line': 0, 'ch': 0};
+
+ var makePythonTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) {
+ var terminal = jqConsole.terminal(function (command, terminal) {
+ terminal.echo('Not implemented yet.');
+ }, {
+ 'history': true,
+ 'prompt': '>>> ',
+ 'greetings': 'CodeQ python terminal proxy',
+ 'exit': false,
+ 'clear': false
+ });
+
+ return {};
+ };
+
+ var makeActivityHandler = function (editor) {
+ var lastActivityMillis = Date.now(),
+ deltaActivityMillis = function deltaActivityMillisFunc () {
+ var now = Date.now(),
+ dt = Math.max(0, Math.min(30000, now - lastActivityMillis)); // 0 sec <= dt <= 30 sec
+ 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());
+ queue.length = 0;
+ return promise;
+ },
+ flush = function () {
+ clearTimeout(ts);
+ return timer();
+ };
+
+ return {
+ "trace": function (trace) {
+ trace['dt'] = deltaActivityMillis();
+ return trace;
+ },
+ 'queue': function (trace) {
+ queue.push(trace);
+ if (ts === null) setTimeout(timer, 10000); // flush every 10 seconds
+ },
+ 'queueTrace': function (trace) {
+ this.queue(this.trace(trace));
+ },
+ '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}}
+ */
+ codeq.createPythonHandler = function (info) {
+ var problem = info.problem,
+ jqDescription = $('#description'),
+ jqEditor = $('#code_editor'),
+ jqConsole = $('#console'),
+ jqHints = $('#info'),
+ editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true, mode: 'python' }),
+ activityHandler = makeActivityHandler(editor),
+ terminal = makePythonTerminalHandler(jqConsole, editor, problem.id, activityHandler),
+
+ /** Object.<string, HintDefinition> */
+ hintDefs = problem.hint,
+ hintCounter = 0, // for generating unique class-names
+ hintCleaners = [],
+ clearHints = function () {
+ var i;
+ for (i = hintCleaners.length - 1; i >= 0; i--) {
+ hintCleaners[i]();
+ }
+ hintCleaners.length = 0;
+ hintCounter = 0;
+ },
+ addMark = function (start, end) {
+ var posStart = editor.posFromIndex(start),
+ posEnd = editor.posFromIndex(end),
+ doc = editor.getDoc(),
+ mark = doc.markText(posStart, posEnd, {className: 'editor-mark _emark_' + hintCounter}),
+ result = {start: posStart, end: posEnd, mark: mark, className: '_emark_' + hintCounter};
+ hintCleaners.push(function () { mark.clear(); mark = null; doc = null; result.mark = null; });
+ hintCounter++;
+ return result;
+ },
+ hintHandlers = {
+ 'static': function (type, template, serverHint) {
+ codeq.log.debug('Processing static hint');
+ if (serverHint.args) {
+ template = template.replace(/\[%=(\w+)%\]/g, function(match, name) {
+ return serverHint.args[name];
+ });
+ }
+ jqHints.append('<div class="hint-static">' + template + '</div>'); // TODO: incorporate any serverHint.args
+ // no hint cleaner here, a static hint remains on the screen
+ },
+ 'popup': function (type, template, serverHint) {
+ codeq.log.debug('Processing popup hint');
+ var mark = addMark(serverHint.start, serverHint.end), // add the mark
+ jqMark = jqEditor.find('.' + mark.className);
+/* jqPopup = null,
+ onBlur = function () {
+ codeq.log.debug('Removing popup');
+ if (jqPopup) {
+ jqPopup.off('blur', onBlur);
+ jqPopup.remove();
+ jqPopup = null;
+ }
+ };
+
+ window.jqMark = jqMark; // TODO: DEBUG
+
+ jqMark.on('click', function () {
+ if (jqPopup) return;
+ codeq.log.debug('Showing popup');
+ var pos = mark.mark.find(), // results in {from: {line: number, ch: number}, to: {line: number, ch: number}}
+ left = pos.from.ch < pos.to.ch ? pos.from.ch : pos.to.ch,
+ down = pos.from.line < pos.to.line ? pos.to.line : pos.from.line;
+ jqPopup = $('<div style="position: absolute;" class="editor-popup ' + mark.className + '"></div>').html(template);
+ editor.addWidget({line: down, ch: left}, jqPopup[0], true);
+ jqPopup = jqEditor.find('.editor-popup.' + mark.className);
+ setTimeout(function () {jqPopup.trigger('focus'); jqPopup.on('click', onBlur);}, 50); // event handlers can be registered only when the DOM elements is part of the document
+ window.jqPopup = jqPopup; // TODO: DEBUG
+ });
+
+ hintCleaners.push(function () {
+ if (jqPopup) {
+ jqPopup.off('blur', onBlur);
+ jqPopup.remove();
+ jqPopup = null;
+ }
+ if (jqMark) {
+ jqMark = null;
+ }
+ mark = null;
+ });*/
+
+ jqMark.popover({content: template, html: true, placement: 'auto bottom', trigger: 'hover focus click', container: 'body'});
+ hintCleaners.push(function () { if (jqMark) { jqMark.popover('destroy'); jqMark = null; } });
+
+ mark.mark.on('', function () {});
+ },
+ 'dropdown': function (type, template, serverHint) {
+ codeq.log.debug('Processing dropdown hint');
+ var completion = null, // the completion object, created in showHint()
+ close = function () {
+ if (completion) {
+ completion.close();
+ completion = null;
+ }
+ };
+
+ if ((editor.listSelections().length > 1) || editor.somethingSelected()) {
+ // showHint() doesn't work if a selection is activeparts
+ }
+
+ editor.showHint({
+ hint: function () {
+ var hints = {
+ list: serverHint.choices,
+ from: editor.posFromIndex(serverHint.start),
+ to: editor.posFromIndex(serverHint.end)
+ };
+ completion = editor.state.completionActive;
+ return hints;
+ },
+ completeOnSingleClick: true,
+ completeSingle: false
+ });
+
+ hintCleaners.push(close);
+ }
+ };
+
+ editor.setValue(info.solution);
+ $('#title').text(problem.slug);
+ jqDescription.html(problem.description);
+
+ 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)});
+ }
+
+ if (changeObj.text) {
+ activityHandler.queueTrace({'typ': 'ins', 'off': pos, 'txt': changeObj.text});
+ }
+ });
+
+ var handler = {
+ destroy: function () {
+ jqDescription.empty();
+ jqEditor.empty(); // TODO: perhaps you do not want to "free" the editor, just empty it
+ jqConsole.empty(); // TODO: the same with the console
+ jqHints.empty(); // TODO: just make static references to these
+ jqDescription = null;
+ jqEditor = null;
+ jqConsole = null;
+ jqHints = null;
+ },
+
+ /**
+ * Processes and display appropriately the server hints.
+ * TODO: sort hints so static and popup hints come first, and a (single) drop-down hint last
+ *
+ * @param {ServerHint[]} serverHints an array of hints from the server
+ */
+ processServerHints: function (serverHints) {
+ var n = serverHints.length,
+ /** number */ i,
+ /** ServerHint */ serverHint,
+ /** HintDefinition */ hintDef,
+ hintType, hintTemplate, t, fn;
+ clearHints();
+ for (i = 0; i < n; i++) {
+ serverHint = serverHints[i];
+ hintDef = hintDefs[serverHint.id];
+ if (!hintDef) {
+ codeq.log.error('Undefined hint: ' + serverHint.id);
+ continue;
+ }
+ t = typeof hintDef;
+ if (t === 'object') {
+ hintType = hintDef.type;
+ hintTemplate = hintDef.message;
+ }
+ else if (t === 'string') {
+ hintType = 'static';
+ hintTemplate = hintDef;
+ }
+ else {
+ codeq.log.error('Unsupported hint definition: ' + t);
+ continue;
+ }
+
+ fn = hintHandlers[hintType];
+ if (!fn) codeq.log.error('Unsupported hint type: ' + hintType);
+ else fn(hintType, hintTemplate, serverHint);
+ }
+ }
+ };
+
+ var jqButtons = $('#block-toolbar button');
+// $(jqButtons.get(0)).on('click', function () { handler.processServerHints([{id:'x_must_be_female'}]); });
+// $(jqButtons.get(1)).on('click', function () { handler.processServerHints([{id:'popup_unknown', start: 20, end: 26}]); });
+// $(jqButtons.get(2)).on('click', function () { handler.processServerHints([{id:'drop_down', start: 20, end: 26, choices:['ena', 'dva', 'tri']}]); });
+
+ $('#btn_code_run').on('click', function () {
+ var doc = editor.getDoc();
+// codeq.comms.sendActivity({'typ': 'slv', 'dt': dt, 'qry': });
+// handler.processServerHints([{id:'list_empty'}]);
+ });
+ $('#btn_code_break').on('click', function () {
+// handler.processServerHints([{id:'popup_unknown', start: 20, end: 26}]);
+ });
+ $('#btn_code_hint').on('click', function () {
+// handler.processServerHints([{id:'drop_down', start: 20, end: 26, choices:['ena', 'dva', 'tri']}]);
+ jqConsole.echo('?- hint.');
+ jqConsole.pause();
+ var doc = editor.getDoc();
+ codeq.comms.sendHint({
+ 'language': 'python',
+ 'program': editor.getDoc().getValue(),
+ 'problem_id': problem.id
+ }).then(
+ function hintSuccess(data) {
+ jqConsole.resume();
+ if (data.code === 0)
+ handler.processServerHints(data.hints);
+ else
+ jqConsole.error(data.message);
+ },
+ function hintFailed (error) {
+ jqConsole.resume();
+ jqConsole.exception(error);
+ }
+ ).done();
+ });
+ $('#btn_code_test').on('click', function () {
+ jqConsole.echo('?- test.');
+ jqConsole.pause();
+ var doc = editor.getDoc();
+ codeq.comms.sendTest({
+ 'language': 'python',
+ 'program': editor.getDoc().getValue(),
+ 'problem_id': problem.id
+ }).then(
+ function testSuccess(data) {
+ jqConsole.resume();
+ if (data.code === 0)
+ handler.processServerHints(data.hints);
+ else
+ jqConsole.error(data.message);
+ },
+ function testFailed (error) {
+ jqConsole.resume();
+ jqConsole.exception(error);
+ }
+ ).done();
+ });
+
+ return handler;
+ };
+})();