summaryrefslogtreecommitdiffstats
path: root/lib/parse
diff options
context:
space:
mode:
authorJames Phillpotts <jphillpotts@scottlogic.co.uk>2014-04-10 10:29:17 +0100
committerAaron O'Mullan <aaron.omullan@friendco.de>2014-04-12 01:26:03 -0700
commitb07681a16a291a38fdbdfa3065a747ab26f9a6c9 (patch)
treeffb07c822518e689e619dc1ac8780f10877f7b7f /lib/parse
parentf6595f51119baca04bf91e619f64518d400e5cae (diff)
downloadgitbook-b07681a16a291a38fdbdfa3065a747ab26f9a6c9.zip
gitbook-b07681a16a291a38fdbdfa3065a747ab26f9a6c9.tar.gz
gitbook-b07681a16a291a38fdbdfa3065a747ab26f9a6c9.tar.bz2
Quiz with GFM checkbox lists
Diffstat (limited to 'lib/parse')
-rw-r--r--lib/parse/lex.js21
-rw-r--r--lib/parse/page.js64
-rw-r--r--lib/parse/renderer.js24
3 files changed, 82 insertions, 27 deletions
diff --git a/lib/parse/lex.js b/lib/parse/lex.js
index b77b844..a9ff1ba 100644
--- a/lib/parse/lex.js
+++ b/lib/parse/lex.js
@@ -17,6 +17,10 @@ function splitSections(nodes) {
}, []).concat([section]); // Add remaining nodes
}
+function isQuizNode(node) {
+ return /^[(\[][ x][)\]]/.test(node.text || node);
+}
+
// What is the type of this section
function sectionType(nodes, idx) {
var codeType = { type: 'code' };
@@ -35,16 +39,13 @@ function sectionType(nodes, idx) {
}
if (nodes.length > 2) {
- var nonBlockquoteNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0);
- nonBlockquoteNodes.splice(_.findIndex(nonBlockquoteNodes, { type: 'blockquote_start' }),
- _.findIndex(nonBlockquoteNodes, { type: 'blockquote_end' }));
-
- if (nonBlockquoteNodes.length === 2) {
- if (_.every(nonBlockquoteNodes, { type: 'table' })) {
- if (_.every(nonBlockquoteNodes[0].cells, function(row) {
- return _.every(row.slice(1), function(cell) { return cell === "( )"; });
- })) {
- return 'quiz';
+ var potentialQuizNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0);
+ if (_.some(potentialQuizNodes, { type: 'blockquote_start' })) {
+ for (var i = 0; i < potentialQuizNodes.length; i++) {
+ var node = potentialQuizNodes[i];
+ if ((node.type === 'list_item_start' && isQuizNode(potentialQuizNodes[i+1])) ||
+ (node.type === 'table' && _.every(potentialQuizNodes[i].cells[0].slice(1), isQuizNode))){
+ return 'quiz'
}
}
}
diff --git a/lib/parse/page.js b/lib/parse/page.js
index 4e26455..eb118e4 100644
--- a/lib/parse/page.js
+++ b/lib/parse/page.js
@@ -36,8 +36,12 @@ function render(section, _options) {
return marked.parser(section, options);
}
-function quizNodesTest(node) {
- return node.type === 'table' || node.type === 'list';
+function quizQuestion(node) {
+ if (node.text) {
+ node.text = node.text.replace(/^([\[(])x([\])])/, "$1 $2");
+ } else {
+ return node.replace(/^([\[(])x([\])])/, "$1 $2");
+ }
}
function parsePage(src, options) {
@@ -81,18 +85,58 @@ function parsePage(src, options) {
}
};
} else if (section.type === 'quiz') {
- var nonQuizNodes = _.reject(section, quizNodesTest);
- var quizNodes = _.filter(section, quizNodesTest);
- var feedback = nonQuizNodes.splice(_.findIndex(nonQuizNodes, { type: 'blockquote_start' }), _.findIndex(nonQuizNodes, { type: 'blockquote_end' }));
+ var quiz = [], question, foundFeedback = false;
+ var nonQuizNodes = section[0].type === 'paragraph' && section[1].type !== 'list_start' ? [section[0]] : [];
+ var quizNodes = section.slice(0);
+ quizNodes.splice(0, nonQuizNodes.length);
+
+ for (var i = 0; i < quizNodes.length; i++) {
+ var node = quizNodes[i];
+
+ if (question && (((node.type === 'list_end' || node.type === 'blockquote_end') && i === quizNodes.length - 1)
+ || node.type === 'table' || (node.type === 'paragraph' && !foundFeedback))) {
+ quiz.push({
+ base: render(question.questionNodes),
+ solution: render(question.solutionNodes),
+ feedback: render(question.feedbackNodes)
+ });
+ }
+
+ if (node.type === 'table' || (node.type === 'paragraph' && !foundFeedback)) {
+ question = { questionNodes: [], solutionNodes: [], feedbackNodes: [] };
+ }
+
+ if (node.type === 'blockquote_start') {
+ foundFeedback = true;
+ } else if (node.type === 'blockquote_end') {
+ foundFeedback = false;
+ }
+
+ if (node.type === 'table') {
+ question.solutionNodes.push(_.cloneDeep(node));
+ node.cells = node.cells.map(function(row) {
+ return row.map(quizQuestion);
+ });
+ question.questionNodes.push(node);
+ } else if (!/blockquote/.test(node.type)) {
+ if (foundFeedback) {
+ question.feedbackNodes.push(node);
+ } else if (node.type === 'paragraph' || node.type === 'text'){
+ question.solutionNodes.push(_.cloneDeep(node));
+ quizQuestion(node);
+ question.questionNodes.push(node);
+ } else {
+ question.solutionNodes.push(node);
+ question.questionNodes.push(node);
+ }
+ }
+ }
+
return {
id: section.id,
type: section.type,
content: render(nonQuizNodes),
- quiz: {
- base: render([quizNodes[0]]),
- solution: render([quizNodes[1]]),
- feedback: render(feedback.slice(1, feedback.length - 1))
- }
+ quiz: quiz
};
}
diff --git a/lib/parse/renderer.js b/lib/parse/renderer.js
index ba543ee..2a72d48 100644
--- a/lib/parse/renderer.js
+++ b/lib/parse/renderer.js
@@ -91,14 +91,24 @@ GitBookRenderer.prototype.tablerow = function(content) {
return GitBookRenderer.super_.prototype.tablerow(content);
};
-GitBookRenderer.prototype.tablecell = function(content, flags) {
- var radioContent = content;
- if (content === "( )") {
- radioContent = "<input type='radio' name='quiz-row-" + this.id + "-" + this.quizRowId + "'/>";
- } else if (content === "(x)") {
- radioContent = "<input type='radio' name='quiz-row-" + this.id + "-" + this.quizRowId + "' checked/>";
+var fieldRegex = /^([(\[])([ x])[\])]/;
+GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
+ var match = fieldRegex.exec(text);
+ if (!match) {
+ return text;
}
- return GitBookRenderer.super_.prototype.tablecell(radioContent, flags);
+ var field = "<input name='quiz-row-" + this.id + "-" + this.quizRowId + "' type='";
+ field += match[1] === '(' ? "radio" : "checkbox";
+ field += match[2] === 'x' ? "' checked/>" : "'/>";
+ return text.replace(fieldRegex, field);
+}
+
+GitBookRenderer.prototype.tablecell = function(content, flags) {
+ return GitBookRenderer.super_.prototype.tablecell(this._createCheckboxAndRadios(content), flags);
+};
+
+GitBookRenderer.prototype.listitem = function(text) {
+ return GitBookRenderer.super_.prototype.listitem(this._createCheckboxAndRadios(text));
};
// Exports