diff --git a/package-lock.json b/package-lock.json index aa315f782..6b09bf7cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "handsontable": "7.2.2", "jexcel": "^3.9.1", "jquery-ui": "1.10.4", - "micro-parsons": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.4/micro-parsons-0.1.4.tgz", + "micro-parsons": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.6/micro-parsons-0.1.6.tgz", "select2": "^4.1.0-rc.0", "sql.js": "1.5.0", "vega-embed": "3.14.0", @@ -2715,9 +2715,9 @@ } }, "node_modules/micro-parsons": { - "version": "0.1.4", - "resolved": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.4/micro-parsons-0.1.4.tgz", - "integrity": "sha512-3hg55GJpg779Vhyzk2Ij3Jz/e6UKZGltwpA60EegQrT4C0XR1ioWBoZutNy2BZ5bR6LCC8RXCky4gF9qxl7/ew==", + "version": "0.1.6", + "resolved": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.6/micro-parsons-0.1.6.tgz", + "integrity": "sha512-OEuMXyEnSN6o2rRvHhIkKTuGzwv0zlzJLjtyTF9X7cYgUff7iyH/gwNQmd0WifBw4x2eVs8gv7a/jE2YRERnvQ==", "license": "MIT", "dependencies": { "highlight.js": "^11.7.0", @@ -7844,8 +7844,8 @@ "dev": true }, "micro-parsons": { - "version": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.4/micro-parsons-0.1.4.tgz", - "integrity": "sha512-3hg55GJpg779Vhyzk2Ij3Jz/e6UKZGltwpA60EegQrT4C0XR1ioWBoZutNy2BZ5bR6LCC8RXCky4gF9qxl7/ew==", + "version": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.6/micro-parsons-0.1.6.tgz", + "integrity": "sha512-OEuMXyEnSN6o2rRvHhIkKTuGzwv0zlzJLjtyTF9X7cYgUff7iyH/gwNQmd0WifBw4x2eVs8gv7a/jE2YRERnvQ==", "requires": { "highlight.js": "^11.7.0", "sortablejs": "^1.14.0" diff --git a/package.json b/package.json index 952b4d372..df945aea0 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "handsontable": "7.2.2", "jexcel": "^3.9.1", "jquery-ui": "1.10.4", - "micro-parsons": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.4/micro-parsons-0.1.4.tgz", + "micro-parsons": "https://github.com/amy21206/micro-parsons-element/releases/download/v0.1.6/micro-parsons-0.1.6.tgz", "select2": "^4.1.0-rc.0", "sql.js": "1.5.0", "vega-embed": "3.14.0", diff --git a/runestone/hparsons/js/BlockFeedback.js b/runestone/hparsons/js/BlockFeedback.js index 3a779a42e..f158c2280 100644 --- a/runestone/hparsons/js/BlockFeedback.js +++ b/runestone/hparsons/js/BlockFeedback.js @@ -18,12 +18,17 @@ export default class BlockFeedback extends HParsonsFeedback { this.solved = false; // TODO: not sure what is the best way to do this this.grader = new BlockBasedGrader(); - let solutionBlocks = []; + let solutionContent = []; for (let i = 0; i < this.hparsons.blockAnswer.length; ++i) { - solutionBlocks.push(this.hparsons.originalBlocks[this.hparsons.blockAnswer[i]]); + if (this.hparsons.renderRaw) { + // if rendering raw (html): answer is text content instead of the original string + solutionContent.push(this.getContentFromRawString(this.hparsons.originalBlocks[this.hparsons.blockAnswer[i]])); + } else { + solutionContent.push(this.hparsons.originalBlocks[this.hparsons.blockAnswer[i]]); + } } - this.solution = solutionBlocks; - this.grader.solution = solutionBlocks; + this.solution = solutionContent; + this.grader.solution = solutionContent; this.answerArea = this.hparsons.hparsonsInput.querySelector('.drop-area'); } @@ -57,7 +62,14 @@ export default class BlockFeedback extends HParsonsFeedback { if (!this.solved) { this.checkCount++; this.clearFeedback(); - this.grader.answer = this.hparsons.hparsonsInput.getParsonsTextArray(); + if (this.hparsons.renderRaw) { + // when rendering raw: the answer is actually the text content. + this.grader.answer = this.hparsons.hparsonsInput.getParsonsTextArray().map((blockHTML) => { + return this.getContentFromRawString(blockHTML); + }); + } else { + this.grader.answer = this.hparsons.hparsonsInput.getParsonsTextArray(); + } this.grade = this.grader.grade(); if (this.grade == "correct") { $(this.hparsons.runButton).prop("disabled", true); @@ -101,7 +113,9 @@ export default class BlockFeedback extends HParsonsFeedback { var notInSolution = []; for (let i = 0; i < answerBlocks.length; i++) { var block = answerBlocks[i]; - var index = this.solution.indexOf(block.textContent); + var index = this.hparsons.renderRaw ? + this.solution.indexOf(this.getContentFromRawString(block.innerHTML)) + : this.solution.indexOf(block.textContent); if (index == -1) { notInSolution.push(block); } else { @@ -122,6 +136,11 @@ export default class BlockFeedback extends HParsonsFeedback { feedbackArea.html($.i18n("msg_parson_wrong_order")); } } + + getContentFromRawString(html) { + let doc = new DOMParser().parseFromString(html, "text/html"); + return doc.body.textContent; + } // Feedback UI for Block-based Feedback clearFeedback() { diff --git a/runestone/hparsons/js/hparsons.js b/runestone/hparsons/js/hparsons.js index 40c9c1288..dfb09e703 100644 --- a/runestone/hparsons/js/hparsons.js +++ b/runestone/hparsons/js/hparsons.js @@ -26,6 +26,7 @@ export default class HParsons extends RunestoneBase { this.randomize = $(orig).data("randomize") ? true : false; this.isBlockGrading = $(orig).data("blockanswer") ? true : false; this.language = $(orig).data("language"); + this.renderRaw = false; if (this.isBlockGrading) { this.blockAnswer = $(orig).data("blockanswer").split(" "); } @@ -80,6 +81,22 @@ export default class HParsons extends RunestoneBase { this.originalBlocks = this.processSingleContent(code, '--blocks--').split('\n').slice(1,-1); this.hiddenSuffix = this.processSingleContent(code, '--hiddensuffix--'); this.unittest = this.processSingleContent(code, '--unittest--'); + // (for pretext) if all blocks can be parsed as html but language is not html, + // ask micro parsons to render raw + if (this.language != 'html') { + this.renderRaw = true; + for (let i = 0; i < this.originalBlocks.length; ++i) { + if (!this.isHTML(this.originalBlocks[i])) { + this.renderRaw = false; + break; + } + } + } + } + + isHTML(block) { + let doc = new DOMParser().parseFromString(block, "text/html"); + return Array.from(doc.body.childNodes).some(node => node.nodeType === 1); } processSingleContent(code, delimitier) { @@ -116,7 +133,7 @@ export default class HParsons extends RunestoneBase { reuse: this.reuse, randomize: this.randomize, parsonsBlocks: [...this.originalBlocks], - language: this.language + language: this.renderRaw ? 'raw' : this.language } InitMicroParsons(props); this.hparsonsInput = $(this.outerDiv).find("micro-parsons")[0]; diff --git a/runestone/hparsons/test/_sources/index.rst b/runestone/hparsons/test/_sources/index.rst index 6ffa966cd..7b0e406b3 100644 --- a/runestone/hparsons/test/_sources/index.rst +++ b/runestone/hparsons/test/_sources/index.rst @@ -171,3 +171,54 @@ Randomized Block with Execution Based Feedback and Hidden Code + error in prefix assert 1,1 == final assert 1,3 == 90 assert 3,3 == 99 + + +Testing rendering raw blocks (pretext) +------------------------------------------ +.. hparsons:: test_hparsons_block_raw_render + :language: sql + :randomize: + :blockanswer: 0 1 2 3 + + Testing rendering raw blocks. + + Raw blocks will be rendered when: + + 1. the langauge is not html + + 2. all blocks contain html nodes + + In this case, it renders html as-is. + ~~~~ + --blocks-- + SELECT + * + FROM +

test

+ + +.. hparsons:: test_hparsons_block_raw_normal + :language: sql + :randomize: + :blockanswer: 0 1 2 3 + + Testing rendering raw blocks. + + Raw blocks will be rendered when: + + 1. the langauge is not html + + 2. all blocks contain html nodes + + In this case, one of the blocks does not contain html, so it is rendered as code. + + This is to prevent rendering some html strings as a part of a complete line, + + e.g. ``a = 'abc'`` in python. + + ~~~~ + --blocks-- + SELECT + * + FROM +

test