diff options
author | Aaron O'Mullan <aaron.omullan@gmail.com> | 2014-04-12 01:51:06 -0700 |
---|---|---|
committer | Aaron O'Mullan <aaron.omullan@gmail.com> | 2014-04-12 01:51:06 -0700 |
commit | 78cb9a56ba2cb89842d9940cf3dd81f582c6270a (patch) | |
tree | b73929b036c03ce961feee64e9e1044cdffb12d6 /lib/parse | |
parent | ab27725b9935b185a58b725e75aedeb579e87d8f (diff) | |
parent | 6173fb76ad3d9ad9357389021aa347df3b1bde92 (diff) | |
download | gitbook-78cb9a56ba2cb89842d9940cf3dd81f582c6270a.zip gitbook-78cb9a56ba2cb89842d9940cf3dd81f582c6270a.tar.gz gitbook-78cb9a56ba2cb89842d9940cf3dd81f582c6270a.tar.bz2 |
Merge pull request #85 from GitbookIO/pr/63
Pr/63
Diffstat (limited to 'lib/parse')
-rw-r--r-- | lib/parse/lex.js | 54 | ||||
-rw-r--r-- | lib/parse/page.js | 62 | ||||
-rw-r--r-- | lib/parse/renderer.js | 27 |
3 files changed, 138 insertions, 5 deletions
diff --git a/lib/parse/lex.js b/lib/parse/lex.js index cec6047..813dd33 100644 --- a/lib/parse/lex.js +++ b/lib/parse/lex.js @@ -17,21 +17,65 @@ function splitSections(nodes) { }, []).concat([section]); // Add remaining nodes } -// What is the type of this section -function sectionType(nodes, idx) { +function isQuizNode(node) { + return (/^[(\[][ x][)\]]/).test(node.text || node); +} + +function isExercise(nodes) { var codeType = { type: 'code' }; // Number of code nodes in section var len = _.filter(nodes, codeType).length; - if( + return ( // Got 3 or 4 code blocks (len === 3 || len === 4) && // Ensure all nodes are at the end _.all(_.last(nodes, len), codeType) - ) - { + ); +} + +function isQuiz(nodes) { + if (nodes.length < 3) { + return false; + } + + // Support having a first paragraph block + // before our series of questions + var quizNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0); + + // No questions + if (!_.some(quizNodes, { type: 'blockquote_start' })) { + return false; + } + + // 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 && + _.every(quizNodes[tableIdx].cells[0].slice(1), isQuizNode) + ) + ) { + return true; + } + + return false; +} + +// What is the type of this section +function sectionType(nodes, idx) { + if(isExercise(nodes)) { return 'exercise'; + } else if(isQuiz(nodes)) { + return 'quiz'; } return 'normal'; diff --git a/lib/parse/page.js b/lib/parse/page.js index 3479c85..eb118e4 100644 --- a/lib/parse/page.js +++ b/lib/parse/page.js @@ -36,6 +36,14 @@ function render(section, _options) { return marked.parser(section, options); } +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) { options = options || {}; @@ -76,6 +84,60 @@ function parsePage(src, options) { context: codeNodes[3] ? codeNodes[3].text : null, } }; + } else if (section.type === 'quiz') { + 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: quiz + }; } // Render normal pages diff --git a/lib/parse/renderer.js b/lib/parse/renderer.js index 6b45a22..2a72d48 100644 --- a/lib/parse/renderer.js +++ b/lib/parse/renderer.js @@ -5,6 +5,7 @@ var path = require('path'); var marked = require('marked'); +var rendererId = 0; function GitBookRenderer(options, extra_options) { if(!(this instanceof GitBookRenderer)) { @@ -13,6 +14,8 @@ function GitBookRenderer(options, extra_options) { GitBookRenderer.super_.call(this, options); this._extra_options = extra_options; + this.quizRowId = 0; + this.id = rendererId++; } inherits(GitBookRenderer, marked.Renderer); @@ -83,6 +86,30 @@ GitBookRenderer.prototype.image = function(href, title, text) { return GitBookRenderer.super_.prototype.image.call(this, _href, title, text); }; +GitBookRenderer.prototype.tablerow = function(content) { + this.quizRowId += 1; + return GitBookRenderer.super_.prototype.tablerow(content); +}; + +var fieldRegex = /^([(\[])([ x])[\])]/; +GitBookRenderer.prototype._createCheckboxAndRadios = function(text) { + var match = fieldRegex.exec(text); + if (!match) { + return text; + } + 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 module.exports = GitBookRenderer; |