summaryrefslogtreecommitdiffstats
path: root/lib/parse
diff options
context:
space:
mode:
authorAaron O'Mullan <aaron.omullan@friendco.de>2014-10-13 15:50:12 +0200
committerAaron O'Mullan <aaron.omullan@friendco.de>2014-10-13 15:50:12 +0200
commit676b2287ee2595843daa18bee4effeb6f021b2ed (patch)
treee2386d93c47f84e7c38b121f9b0c4cfa3344ce48 /lib/parse
parentd730a3aac601f000d770dbccca02ac4bb2d07245 (diff)
downloadgitbook-676b2287ee2595843daa18bee4effeb6f021b2ed.zip
gitbook-676b2287ee2595843daa18bee4effeb6f021b2ed.tar.gz
gitbook-676b2287ee2595843daa18bee4effeb6f021b2ed.tar.bz2
Rewrite quiz logic to be more robust
Fixes #470
Diffstat (limited to 'lib/parse')
-rw-r--r--lib/parse/is_quiz.js99
1 files changed, 68 insertions, 31 deletions
diff --git a/lib/parse/is_quiz.js b/lib/parse/is_quiz.js
index aa6125a..4a24cfc 100644
--- a/lib/parse/is_quiz.js
+++ b/lib/parse/is_quiz.js
@@ -4,42 +4,79 @@ function isQuizNode(node) {
return (/^[(\[][ x][)\]]/).test(node.text || node);
}
-function isQuiz(nodes) {
- if (nodes.length < 3) {
- return false;
- }
+function isTableQuestion(nodes) {
+ var block = questionBlock(nodes);
+ return (
+ block.length === 1 &&
+ block[0].type === 'table' &&
+ _.all(block[0].cells[0].slice(1), isQuizNode)
+ );
+}
- // Support having a first paragraph block
- // before our series of questions
- var quizNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0);
+function isListQuestion(nodes) {
+ var block = questionBlock(nodes);
+ // Counter of when we go in and out of lists
+ var inlist = 0;
+ // Number of lists we found
+ var lists = 0;
+ // Elements found outside a list
+ var outsiders = 0;
+ // Ensure that we have nothing except lists
+ _.each(block, function(node) {
+ if(node.type === 'list_start') {
+ inlist++;
+ } else if(node.type === 'list_end') {
+ inlist--;
+ lists++;
+ } else if(inlist === 0) {
+ // Found non list_start or list_end whilst outside a list
+ outsiders++;
+ }
+ });
+ return lists > 0 && outsiders === 0;
+}
- // No questions
- if (!_.some(quizNodes, { type: 'blockquote_start' })) {
- return false;
- }
+function isQuestion(nodes) {
+ return isListQuestion(nodes) || isTableQuestion(nodes);
+}
- // Check if section has list of questions
- // or table of questions
- var listIdx = _.findIndex(quizNodes, { type: 'list_item_start' });
- var tableIdx = _.findIndex(quizNodes, { type: 'table' });
-
-
- if(
- // List of questions
- listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
-
- // Table of questions
- (
- tableIdx !== -1 &&
- // Last entry
- tableIdx === nodes.length - 1 &&
- _.every(quizNodes[tableIdx].cells[0].slice(1), isQuizNode)
- )
- ) {
- return true;
+// Remove (optional) paragraph header node and blockquote
+function questionBlock(nodes) {
+ return nodes.slice(
+ nodes[0].type === 'paragraph' ? 1 : 0,
+ _.findIndex(nodes, { type: 'blockquote_start' })
+ );
+}
+
+function splitQuestions(nodes) {
+ // Represents nodes in current question
+ var buffer = [];
+ return _.reduce(nodes, function(accu, node) {
+ // Add node to buffer
+ buffer.push(node);
+
+ // Flush buffer once we hit the end of a question
+ if(node.type === 'blockquote_end') {
+ accu.push(buffer);
+ // Clear buffer
+ buffer = [];
+ }
+
+ return accu;
+ }, []);
+}
+
+function isQuiz(nodes) {
+ // Extract potential questions
+ var questions = splitQuestions(nodes);
+
+ // Nothing that looks like questions
+ if(questions.length === 0) {
+ return false;
}
- return false;
+ // Ensure all questions are correctly structured
+ return _.all(questions, isQuestion);
}
module.exports = isQuiz;