Skip to content

Commit 676b228

Browse files
author
Aaron O'Mullan
committed
Rewrite quiz logic to be more robust
Fixes GitbookIO#470
1 parent d730a3a commit 676b228

File tree

2 files changed

+69
-32
lines changed

2 files changed

+69
-32
lines changed

lib/parse/is_quiz.js

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,79 @@ function isQuizNode(node) {
44
return (/^[(\[][ x][)\]]/).test(node.text || node);
55
}
66

7-
function isQuiz(nodes) {
8-
if (nodes.length < 3) {
9-
return false;
10-
}
7+
function isTableQuestion(nodes) {
8+
var block = questionBlock(nodes);
9+
return (
10+
block.length === 1 &&
11+
block[0].type === 'table' &&
12+
_.all(block[0].cells[0].slice(1), isQuizNode)
13+
);
14+
}
1115

12-
// Support having a first paragraph block
13-
// before our series of questions
14-
var quizNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0);
16+
function isListQuestion(nodes) {
17+
var block = questionBlock(nodes);
18+
// Counter of when we go in and out of lists
19+
var inlist = 0;
20+
// Number of lists we found
21+
var lists = 0;
22+
// Elements found outside a list
23+
var outsiders = 0;
24+
// Ensure that we have nothing except lists
25+
_.each(block, function(node) {
26+
if(node.type === 'list_start') {
27+
inlist++;
28+
} else if(node.type === 'list_end') {
29+
inlist--;
30+
lists++;
31+
} else if(inlist === 0) {
32+
// Found non list_start or list_end whilst outside a list
33+
outsiders++;
34+
}
35+
});
36+
return lists > 0 && outsiders === 0;
37+
}
1538

16-
// No questions
17-
if (!_.some(quizNodes, { type: 'blockquote_start' })) {
18-
return false;
19-
}
39+
function isQuestion(nodes) {
40+
return isListQuestion(nodes) || isTableQuestion(nodes);
41+
}
2042

21-
// Check if section has list of questions
22-
// or table of questions
23-
var listIdx = _.findIndex(quizNodes, { type: 'list_item_start' });
24-
var tableIdx = _.findIndex(quizNodes, { type: 'table' });
25-
26-
27-
if(
28-
// List of questions
29-
listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
30-
31-
// Table of questions
32-
(
33-
tableIdx !== -1 &&
34-
// Last entry
35-
tableIdx === nodes.length - 1 &&
36-
_.every(quizNodes[tableIdx].cells[0].slice(1), isQuizNode)
37-
)
38-
) {
39-
return true;
43+
// Remove (optional) paragraph header node and blockquote
44+
function questionBlock(nodes) {
45+
return nodes.slice(
46+
nodes[0].type === 'paragraph' ? 1 : 0,
47+
_.findIndex(nodes, { type: 'blockquote_start' })
48+
);
49+
}
50+
51+
function splitQuestions(nodes) {
52+
// Represents nodes in current question
53+
var buffer = [];
54+
return _.reduce(nodes, function(accu, node) {
55+
// Add node to buffer
56+
buffer.push(node);
57+
58+
// Flush buffer once we hit the end of a question
59+
if(node.type === 'blockquote_end') {
60+
accu.push(buffer);
61+
// Clear buffer
62+
buffer = [];
63+
}
64+
65+
return accu;
66+
}, []);
67+
}
68+
69+
function isQuiz(nodes) {
70+
// Extract potential questions
71+
var questions = splitQuestions(nodes);
72+
73+
// Nothing that looks like questions
74+
if(questions.length === 0) {
75+
return false;
4076
}
4177

42-
return false;
78+
// Ensure all questions are correctly structured
79+
return _.all(questions, isQuestion);
4380
}
4481

4582
module.exports = isQuiz;

test/page.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,6 @@ describe('Section parsing', function() {
127127
it('should not have false positive quiz parsing', function() {
128128
var LEXED = loadPage('FALSE_QUIZ');
129129

130-
assert.not.equal(LEXED[0].type, 'quiz');
130+
assert.equal(LEXED[0].type, 'normal');
131131
});
132132
});

0 commit comments

Comments
 (0)