From 1dd4d473bb136c607eeea3a2d655b3f56e5aeee8 Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Tue, 23 Feb 2016 17:31:56 +0100 Subject: Remove explicit "Hint" button Hints are now generated for each tested program, and the user can press a button to reveal the hints. --- css/codeq/hint.css | 23 +++++----- index.html | 2 - js/codeq/hint.js | 126 ++++++++++++++++++++++++++++++++++++----------------- js/codeq/prolog.js | 37 ++-------------- js/codeq/python.js | 34 +-------------- js/codeq/robot.js | 4 +- 6 files changed, 102 insertions(+), 124 deletions(-) diff --git a/css/codeq/hint.css b/css/codeq/hint.css index 05bd970..803479f 100644 --- a/css/codeq/hint.css +++ b/css/codeq/hint.css @@ -18,7 +18,8 @@ a.hint-static-link { cursor: pointer; } -.hint-static img { +div.hint-static img { + max-height: 12em; max-width: 80%; /* center img trick */ display: block; @@ -26,22 +27,18 @@ a.hint-static-link { margin-right: auto; } -.hints > div { - border: 1px solid gray; - border-radius: 4px; - margin-bottom: 0.5em; +div.hints > div.feedback { + border-top: 1px solid gray; + margin-bottom: 1.5em; margin-top: 0.5em; - opacity: 0.8; - padding: 0.5em; } - -.hints > div:first-child { - border: 2px solid black; - opacity: 1; +div.hints > div.feedback:first-child { + border-top: none; } -.hints > div:hover { - opacity: 1; +div.hints > div.feedback > div { + margin-bottom: 0.5em; + margin-top: 0.5em; } .popup-hint > :last-child { diff --git a/index.html b/index.html index ecd3b1b..040dd6b 100644 --- a/index.html +++ b/index.html @@ -345,7 +345,6 @@
-
@@ -384,7 +383,6 @@
- diff --git a/js/codeq/hint.js b/js/codeq/hint.js index bc42694..0550a41 100644 --- a/js/codeq/hint.js +++ b/js/codeq/hint.js @@ -24,7 +24,7 @@ along with this program. If not, see . */ var firstCharacterPos = {'line': 0, 'ch': 0}, sel_no_scroll = {'scroll': false}; - codeq.makeHinter = function (jqHints, jqEditor, editor, trNamespace, problemDef, commonDef) { + codeq.makeHinter = function (jqHints, jqEditor, editor, trNamespace, problemDef, commonDef, activityHandler) { var hintCounter = 0, // for generating unique class-names hintCleaners = [], popoverHintCleaners = [], // we require separate cleaners for popups, because they are rebuilt when the editor's DOM changes @@ -51,6 +51,7 @@ along with this program. If not, see . */ popoverHintCleaners[i](); } popoverHintCleaners.length = 0; + $('div.hints > div.feedback > button.display-hints').off().remove(); }, addMark = function (start, end, style) { @@ -180,7 +181,7 @@ along with this program. If not, see . */ }, typeHandlers = { - 'static': function (template, hint) { + 'static': function (template, hint, box) { var content = prepareStaticHintContent(template, hint.indices, hint.id), args = hint ? hint.args : null, hintIndex = 0, @@ -222,15 +223,9 @@ along with this program. If not, see . */ } } else { - jqContainer.prepend(jq); + jqContainer.append(jq); } codeq.tr.translateDom(jq); - //// scroll into view if overflowing - //deltaHeight = jqHints.height() - jqHintsContainer.height(); - //if (deltaHeight > 0) { - // jqHintsContainer.scrollTop(deltaHeight); - //} - jqHintsContainer.scrollTop(0); }, jqContainer, jqButton; @@ -238,7 +233,7 @@ along with this program. If not, see . */ // hint sequence jqContainer = $('
'); jqButton = $(''); - jqHints.prepend(jqContainer); + box.append(jqContainer); jqContainer.append(jqButton); jqButton.on('click', function () { nextJqHint(); @@ -246,7 +241,7 @@ along with this program. If not, see . */ } else { // a single hint - jqContainer = jqHints; + jqContainer = box; jqButton = null; } nextJqHint(); @@ -254,7 +249,7 @@ along with this program. If not, see . */ // no hint cleaner here, a static hint remains on the screen }, - 'popup': function (template, hint) { + 'popup': function (template, hint, box) { codeq.log.debug('Processing popup hint'); var args = hint.args, style = hint.style || '', @@ -297,7 +292,7 @@ along with this program. If not, see . */ return instFunc; }, - 'dropdown': function (template, hint) { + 'dropdown': function (template, hint, box) { codeq.log.debug('Processing dropdown hint'); var completion = null, // the completion object, created in showHint() close = function () { @@ -330,6 +325,46 @@ along with this program. If not, see . */ } }, + // process and append [hints] to the element [box] + appendHints = function (hints, box) { + var finalizers = [], + i, hint, hintDef, hintContent, hintType, t, fn, ret; + + activityHandler.queueTrace({'typ': 'hint', 'feedback': hints}); + for (i = 0; i < hints.length; i++) { + hint = hints[i]; + hintDef = hintProblemDefs[hint.id] || hintCommonDefs[hint.id]; + if (!hintDef) { + codeq.log.error('Undefined hint: ' + hint.id); + continue; + } + hintContent = hintProblemTr[hint.id] || hintCommonTr[hint.id]; + if (!hintContent) { + codeq.log.error('Hint without content: ' + hint.id); + continue; + } + + t = typeof hintDef; + if (t === 'string') hintType = hintDef; // currently a hint type is a string + else if ((t === 'object') && (hintDef !== null)) hintType = hintDef.type; // but in future we may use an object, if a definition becomes more complex + else { + codeq.log.error('Cannot determine the type of hint ' + hint.id + ' from: ' + hintDef); + continue; + } + + fn = typeHandlers[hintType]; + if (!fn) codeq.log.error('Unsupported hint type: ' + hintType); + else { + ret = fn(hintContent, hint, box); + if (typeof ret === 'function') finalizers.push(ret); + } + } + // invoke any finalizers + for (i = 0; i < finalizers.length; i++) { + finalizers[i](); + } + }, + /** * When the editor updates its DOM, we have to re-register any popup hints. */ @@ -349,7 +384,12 @@ along with this program. If not, see . */ */ 'planNext': function () { if (planIdx < planDef.length) { - typeHandlers['static'](planDef[planIdx], {'id': 'plan'}); + var jqHintBox = $(''); + activityHandler.queueTrace({'typ': 'plan', 'index': planIdx}); + clearHints(); + typeHandlers['static'](planDef[planIdx], {'id': 'plan'}, jqHintBox); + jqHints.prepend(jqHintBox); + jqHintsContainer.scrollTop(0); planIdx++; } return planIdx < planDef.length; @@ -366,42 +406,48 @@ along with this program. If not, see . */ * @param {ServerHint[]} hints an array of hints from the server */ 'handle': function (hints) { - var finalizers = [], - i, hint, hintDef, hintContent, hintType, t, fn, ret; + var i, hint, hintDef, hintContent, + n_correct = 0, n_all = 0, + jqHintBox = $(''), + jqHintBtn; + // clear any existing hints clearHints(); + + // display the test_results hint first if found for (i = 0; i < hints.length; i++) { hint = hints[i]; - hintDef = hintProblemDefs[hint.id] || hintCommonDefs[hint.id]; - if (!hintDef) { - codeq.log.error('Undefined hint: ' + hint.id); - continue; - } - hintContent = hintProblemTr[hint.id] || hintCommonTr[hint.id]; - if (!hintContent) { - codeq.log.error('Hint without content: ' + hint.id); - continue; + if (hint.id === 'test_results') { + activityHandler.queueTrace({'typ': 'test', 'feedback': hint}); + n_correct = hint.args.passed + n_all = hint.args.total + hintContent = hintProblemTr[hint.id] || hintCommonTr[hint.id]; + typeHandlers['static'](hintContent, hint, jqHintBox); + hints.splice(i, 1); + break; } + } - t = typeof hintDef; - if (t === 'string') hintType = hintDef; // currently a hint type is a string - else if ((t === 'object') && (hintDef !== null)) hintType = hintDef.type; // but in future we may use an object, if a definition becomes more complex - else { - codeq.log.error('Cannot determine the type of hint ' + hint.id + ' from: ' + hintDef); - continue; + // display remaining hints + if (hints.length > 0) { + if (n_all == 0 || n_correct == n_all) { + // no test_results or program correct: show all hints immediately + appendHints(hints, jqHintBox); } - - fn = typeHandlers[hintType]; - if (!fn) codeq.log.error('Unsupported hint type: ' + hintType); else { - ret = fn(hintContent, hint); - if (typeof ret === 'function') finalizers.push(ret); + // otherwise, hide hints behind a button + jqHintBtn = $(''); + codeq.tr.translateDom(jqHintBtn); + jqHintBtn.on('click', function (e) { + jqHintBtn.off().remove(); + appendHints(hints, jqHintBox); + + }); + jqHintBox.append(jqHintBtn); } } - // invoke any finalizers - for (i = 0; i < finalizers.length; i++) { - finalizers[i](); - } + jqHints.prepend(jqHintBox); + jqHintsContainer.scrollTop(0); }, 'destroy': function () { diff --git a/js/codeq/prolog.js b/js/codeq/prolog.js index b2ea3e1..3d34423 100644 --- a/js/codeq/prolog.js +++ b/js/codeq/prolog.js @@ -33,9 +33,8 @@ along with this program. If not, see . */ jqAllQuadrants = jqDescription.add(jqCode).add(jqConsole).add(jqInfo), // all the quadrants // buttons jqBtnPlan = jqScreen.find('.btn-plan'), - jqBtnHint = jqScreen.find('.btn-hint').ladda(), jqBtnTest = jqScreen.find('.btn-test').ladda(), - jqAllButtons = jqBtnPlan.add(jqBtnHint).add(jqBtnTest), // all the buttons + jqAllButtons = jqBtnPlan.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) @@ -251,7 +250,7 @@ along with this program. If not, see . */ }), activityHandler = codeq.makeActivityHandler(editor, problemDef.id), terminal = makePrologTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler), - hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'prolog_hints', problemDef, commonDef), + hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'prolog_hints', problemDef, commonDef, activityHandler), commError = function (error) { alert(error); }; @@ -279,42 +278,14 @@ along with this program. If not, see . */ }); jqBtnPlan.on('click', function () { - activityHandler.queueTrace({'typ': 'plan'}); if (!hinter.planNext()) { jqBtnPlan.prop('disabled', true).blur(); } }); - jqBtnHint.on('click', function () { - editor.setOption('readOnly', true); - terminal.inputDisable(); - jqBtnTest.prop('disabled', true); - jqBtnHint.ladda('start'); - codeq.comms.sendHint({ - 'program': editor.getDoc().getValue(), - 'problem_id': problemDef.id - }) - .then(function (data) { - if (data.code === 0) { - activityHandler.queueTrace({'typ': 'hint', 'feedback': data.hints}); - hinter.handle(data.hints); - } - else { - terminal.append(data.message + '\n', 'error'); - } - }) - .fail(commError) - .fin(function () { - editor.setOption('readOnly', false); - terminal.inputEnable(); - jqBtnHint.ladda('stop'); - jqBtnTest.prop('disabled', false); - }) - .done(); - }); + jqBtnTest.on('click', function () { editor.setOption('readOnly', true); terminal.inputDisable(); - jqBtnHint.prop('disabled', true); jqBtnTest.ladda('start'); codeq.comms.sendTest({ 'program': editor.getDoc().getValue(), @@ -322,7 +293,6 @@ along with this program. If not, see . */ }) .then(function (data) { if (data.code === 0) { - activityHandler.queueTrace({'typ': 'test', 'feedback': data.hints}); hinter.handle(data.hints); } else { @@ -334,7 +304,6 @@ along with this program. If not, see . */ editor.setOption('readOnly', false); terminal.inputEnable(); jqBtnTest.ladda('stop'); - jqBtnHint.prop('disabled', false); }) .done(); }); diff --git a/js/codeq/python.js b/js/codeq/python.js index 10032e8..f2b0184 100644 --- a/js/codeq/python.js +++ b/js/codeq/python.js @@ -35,11 +35,10 @@ along with this program. If not, see . */ jqAllQuadrants = jqDescription.add(jqCode).add(jqConsole).add(jqInfo), // all the quadrants // buttons jqBtnPlan = jqScreen.find('.btn-plan'), - jqBtnHint = jqScreen.find('.btn-hint').ladda(), jqBtnTest = jqScreen.find('.btn-test').ladda(), jqBtnRun = jqScreen.find('.btn-run'), jqBtnStop = jqScreen.find('.btn-stop'), - jqInfoButtons = jqBtnPlan.add(jqBtnHint).add(jqBtnTest), // all info-focusing buttons + jqInfoButtons = jqBtnPlan.add(jqBtnTest), // all info-focusing buttons jqAllButtons = jqInfoButtons.add(jqBtnRun).add(jqBtnStop), // all buttons // misc currentSubState = null, @@ -208,7 +207,7 @@ along with this program. If not, see . */ }), activityHandler = codeq.makeActivityHandler(editor, problemDef.id), terminal = makePythonTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler), - hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'python_hints', problemDef, commonDef), + hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'python_hints', problemDef, commonDef, activityHandler), commError = function (error) { alert(error); }; @@ -236,39 +235,12 @@ along with this program. If not, see . */ }); jqBtnPlan.on('click', function () { - activityHandler.queueTrace({'typ': 'plan'}); if (!hinter.planNext()) { jqBtnPlan.prop('disabled', true).blur(); } }); - jqBtnHint.on('click', function () { - editor.setOption('readOnly', true); - jqBtnTest.prop('disabled', true); - jqBtnHint.ladda('start'); - codeq.comms.sendHint({ - 'program': editor.getDoc().getValue(), - 'problem_id': problemDef.id - }) - .then(function (data) { - if (data.code === 0) { - activityHandler.queueTrace({'typ': 'hint', 'feedback': data.hints}); - hinter.handle(data.hints); - } - else { - terminal.append('error: ' + data.message); - } - }) - .fail(commError) - .fin(function () { - editor.setOption('readOnly', false); - jqBtnHint.ladda('stop'); - jqBtnTest.prop('disabled', false); - }) - .done(); - }); jqBtnTest.on('click', function () { editor.setOption('readOnly', true); - jqBtnHint.prop('disabled', true); jqBtnTest.ladda('start'); codeq.comms.sendTest({ 'program': editor.getDoc().getValue(), @@ -276,7 +248,6 @@ along with this program. If not, see . */ }) .then(function (data) { if (data.code === 0) { - activityHandler.queueTrace({'typ': 'test', 'feedback': data.hints}); hinter.handle(data.hints); } else { @@ -287,7 +258,6 @@ along with this program. If not, see . */ .fin(function () { editor.setOption('readOnly', false); jqBtnTest.ladda('stop'); - jqBtnHint.prop('disabled', false); }) .done(); }); diff --git a/js/codeq/robot.js b/js/codeq/robot.js index f5c04a1..42f336c 100644 --- a/js/codeq/robot.js +++ b/js/codeq/robot.js @@ -178,7 +178,7 @@ along with this program. If not, see . */ }), activityHandler = codeq.makeActivityHandler(editor, problemDef.id), terminal = makeRobotTerminalHandler(jqTerminal, editor, problemDef.id, activityHandler), - hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'robot_hints', problemDef, commonDef), + hinter = codeq.makeHinter(jqHints, jqEditor, editor, 'robot_hints', problemDef, commonDef, activityHandler), commError = function (error) { alert(error); }, @@ -236,7 +236,6 @@ along with this program. If not, see . */ }); jqBtnPlan.on('click', function () { - activityHandler.queueTrace({'typ': 'plan'}); if (!hinter.planNext()) { jqBtnPlan.prop('disabled', true).blur(); } @@ -250,7 +249,6 @@ along with this program. If not, see . */ }) .then(function (data) { if (data.code === 0) { - activityHandler.queueTrace({'typ': 'hint', 'feedback': data.hints}); hinter.handle(data.hints); } else { -- cgit v1.2.1