summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleš Smodiš <aless@guru.si>2015-09-17 18:13:06 +0200
committerAleš Smodiš <aless@guru.si>2015-09-17 18:13:06 +0200
commitacad5db74baf202e26cb2ba2e8f32c3d6ae0ef63 (patch)
treefbfcbffd8fcea5547ef63563f6445564d3ac2d75
parentf32e5966ab598d0a7cad20e74a8d9f29edb1bdf5 (diff)
Refactored hint handling into a separate module.
-rw-r--r--index.html1
-rw-r--r--js/codeq/hint.js149
-rw-r--r--js/prolog.js199
-rw-r--r--js/python.js203
4 files changed, 184 insertions, 368 deletions
diff --git a/index.html b/index.html
index 0f5105d..6f109f3 100644
--- a/index.html
+++ b/index.html
@@ -131,6 +131,7 @@
<script src="js/codeq.js"></script>
<script src="js/codeq/comms.js"></script>
<script src="js/codeq/console.js"></script>
+ <script src="js/codeq/hint.js"></script>
<script src="js/def_parser.js"></script>
<script src="js/prolog.js"></script>
<script src="js/python.js"></script>
diff --git a/js/codeq/hint.js b/js/codeq/hint.js
new file mode 100644
index 0000000..ffae638
--- /dev/null
+++ b/js/codeq/hint.js
@@ -0,0 +1,149 @@
+/**
+ * Creates a hint handler, displaying hints inside the provided jqHints <div>.
+ */
+
+(function () {
+ // constants
+ var firstCharacterPos = {'line': 0, 'ch': 0},
+ sel_no_scroll = {'scroll': false};
+
+ codeq.makeHinter = function (jqHints, jqEditor, editor, hintDefs) {
+ var 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;
+ },
+ processTemplate = function (template, args) {
+ if (!args)
+ return template;
+ return template.replace(/\[%=(\w+)%\]/g, function(match, name) {
+ return args[name];
+ });
+ },
+ typeHandlers = {
+ 'static': function (type, template, serverHint) {
+ codeq.log.debug('Processing static hint');
+ var message = processTemplate(template, serverHint.args);
+ jqHints.append('<div class="hint-static">' + message + '</div>');
+ // no hint cleaner here, a static hint remains on the screen
+ },
+
+ 'popup': function (type, template, serverHint) {
+ codeq.log.debug('Processing popup hint');
+ var message = processTemplate(template, serverHint.args),
+ mark = addMark(serverHint.start, serverHint.end), // add the mark
+ jqMark = jqEditor.find('.' + mark.className);
+
+ jqMark.popover({'content': message, '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.setSelection(firstCharacterPos, firstCharacterPos, sel_no_scroll); // deselect anything
+ }
+
+ 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);
+ }
+ };
+
+ return {
+ /**
+ * 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
+ */
+ 'handle': function (serverHints) {
+ var n = serverHints.length,
+ /** number */ i,
+ /** ServerHint */ serverHint,
+ /** HintDefinition */ hintDef,
+ hintType, hintTemplate, t, fn, indices;
+ clearHints();
+ for (i = 0; i < n; i++) {
+ serverHint = serverHints[i];
+ hintDef = hintDefs[serverHint.id];
+ if (serverHint.indices) {
+ indices = serverHint.indices
+ for (i = 0; i < indices.length; i++) {
+ hintDef = hintDef[indices[i]];
+ if (!hintDef)
+ break;
+ }
+ }
+ if (!hintDef) {
+ codeq.log.error('Undefined hint ' + serverHint.id + ' with indices ' + serverHint.indices);
+ 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 = typeHandlers[hintType];
+ if (!fn) codeq.log.error('Unsupported hint type: ' + hintType);
+ else fn(hintType, hintTemplate, serverHint);
+ }
+ },
+
+ 'destroy': function () {
+ clearHints();
+ jqHints.empty();
+ jqHints = null;
+ jqEditor = null;
+ editor = null;
+ }
+ };
+ };
+})(); \ No newline at end of file
diff --git a/js/prolog.js b/js/prolog.js
index 085860c..1c811ed 100644
--- a/js/prolog.js
+++ b/js/prolog.js
@@ -149,119 +149,7 @@
editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }),
activityHandler = makeActivityHandler(editor, problem.id),
terminal = makePrologTerminalHandler(jqConsole, editor, problem.id, activityHandler),
- 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;
- },
- processTemplate = function (template, args) {
- if (!args)
- return template;
- return template.replace(/\[%=(\w+)%\]/g, function(match, name) {
- return args[name];
- });
- },
- hintHandlers = {
- 'static': function (type, template, serverHint) {
- codeq.log.debug('Processing static hint');
- var message = processTemplate(template, serverHint.args);
- jqHints.append('<div class="hint-static">' + message + '</div>');
- // no hint cleaner here, a static hint remains on the screen
- },
- 'popup': function (type, template, serverHint) {
- codeq.log.debug('Processing popup hint');
- var message = processTemplate(template, serverHint.args),
- 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: message, 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);
- }
- };
+ hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint);
editor.setValue(info.solution);
$('#title').text(problem.slug);
@@ -280,73 +168,6 @@
}
});
- var handler = {
- destroy: function () {
- terminal.destroy();
- 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 (serverHint.indices) {
- indices = serverHint.indices
- for (i = 0; i < indices.length; i++) {
- hintDef = hintDef[indices[i]];
- if (!hintDef)
- break;
- }
- }
- if (!hintDef) {
- codeq.log.error('Undefined hint ' + serverHint.id + ' with indices ' + serverHint.indices);
- 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_hint').on('click', function () {
terminal.append('hint.\n', 'input');
terminal.inputDisable();
@@ -359,7 +180,7 @@
.then(
function hintSuccess(data) {
if (data.code === 0)
- handler.processServerHints(data.hints);
+ hinter.handle(data.hints);
else
terminal.append(data.message + '\n', 'error');
},
@@ -385,7 +206,7 @@
.then(
function testSuccess(data) {
if (data.code === 0)
- handler.processServerHints(data.hints);
+ hinter.handle(data.hints);
else
terminal.append(data.message + '\n', 'error');
},
@@ -400,6 +221,18 @@
.done();
});
- return handler;
+ return {
+ destroy: function () {
+ hinter.destroy();
+ terminal.destroy();
+ 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
+ jqDescription = null;
+ jqEditor = null;
+ jqConsole = null;
+ jqHints = null;
+ }
+ };
};
})();
diff --git a/js/python.js b/js/python.js
index 7d7458e..9d051fe 100644
--- a/js/python.js
+++ b/js/python.js
@@ -64,7 +64,8 @@
'queueTrace': function (trace) {
trace['dt'] = deltaActivityMillis();
queue.push(trace);
- if (ts === null) setTimeout(timer, 10000); // flush every 10 seconds
+ if (ts === null) ts = setTimeout(timer, 10000); // flush every 10 seconds
+ return this;
},
'flush': flush,
'addAndPurge': function (trace) {
@@ -96,119 +97,7 @@
editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true, mode: 'python' }),
activityHandler = makeActivityHandler(editor, problem.id),
terminal = makePythonTerminalHandler(jqConsole, editor, problem.id, activityHandler),
- 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;
- },
- processTemplate = function (template, args) {
- if (!args)
- return template;
- return template.replace(/\[%=(\w+)%\]/g, function(match, name) {
- return args[name];
- });
- },
- hintHandlers = {
- 'static': function (type, template, serverHint) {
- codeq.log.debug('Processing static hint');
- var message = processTemplate(template, serverHint.args);
- jqHints.append('<div class="hint-static">' + message + '</div>');
- // no hint cleaner here, a static hint remains on the screen
- },
- 'popup': function (type, template, serverHint) {
- codeq.log.debug('Processing popup hint');
- var message = processTemplate(template, serverHint.args),
- 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: message, 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);
- }
- };
+ hinter = codeq.makeHinter(jqHints, jqEditor, editor, problem.hint);
editor.setValue(info.solution);
$('#title').text(problem.slug);
@@ -227,75 +116,7 @@
}
});
- var handler = {
- destroy: function () {
- terminal.destroy();
- 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 (serverHint.indices) {
- indices = serverHint.indices
- for (i = 0; i < indices.length; i++) {
- hintDef = hintDef[indices[i]];
- if (!hintDef)
- break;
- }
- }
- if (!hintDef) {
- codeq.log.error('Undefined hint ' + serverHint.id + ' with indices ' + serverHint.indices);
- 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_hint').on('click', function () {
-// handler.processServerHints([{id:'drop_down', start: 20, end: 26, choices:['ena', 'dva', 'tri']}]);
var doc = editor.getDoc();
codeq.comms.sendHint({
'language': 'python',
@@ -304,7 +125,7 @@
}).then(
function hintSuccess(data) {
if (data.code === 0)
- handler.processServerHints(data.hints);
+ hinter.handle(data.hints);
else
terminal.append('error: ' + data.message);
},
@@ -322,7 +143,7 @@
}).then(
function testSuccess(data) {
if (data.code === 0)
- handler.processServerHints(data.hints);
+ hinter.handle(data.hints);
else
terminal.append('error: ' + data.message);
},
@@ -337,6 +158,18 @@
'text': ''
});
- return handler;
+ return {
+ destroy: function () {
+ hinter.destroy();
+ terminal.destroy();
+ 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
+ jqDescription = null;
+ jqEditor = null;
+ jqConsole = null;
+ jqHints = null;
+ }
+ };
};
})();