summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron O'Mullan <aaron.omullan@gmail.com>2014-10-13 15:55:32 +0200
committerAaron O'Mullan <aaron.omullan@gmail.com>2014-10-13 15:55:32 +0200
commitc9ec911d0eab7296a808e12470abe90bf764cd6a (patch)
tree187c8af2fb58bb6a641f6c0639966dfefc1ada5b
parent32a64be407e05b6ce4275e6f3f9213e8f87f884a (diff)
parent84d7662fd8a2d1abfd0feb92f54064e5d9b9d072 (diff)
downloadgitbook-c9ec911d0eab7296a808e12470abe90bf764cd6a.zip
gitbook-c9ec911d0eab7296a808e12470abe90bf764cd6a.tar.gz
gitbook-c9ec911d0eab7296a808e12470abe90bf764cd6a.tar.bz2
Merge pull request #472 from GitbookIO/fix/quizzes
Fix/quizzes
-rw-r--r--lib/parse/is_exercise.js17
-rw-r--r--lib/parse/is_quiz.js87
-rw-r--r--lib/parse/lex.js56
-rw-r--r--test/fixtures/FALSE_QUIZ.md120
-rw-r--r--test/page.js8
5 files changed, 235 insertions, 53 deletions
diff --git a/lib/parse/is_exercise.js b/lib/parse/is_exercise.js
new file mode 100644
index 0000000..74ed753
--- /dev/null
+++ b/lib/parse/is_exercise.js
@@ -0,0 +1,17 @@
+var _ = require('lodash');
+
+function isExercise(nodes) {
+ var codeType = { type: 'code' };
+
+ // Number of code nodes in section
+ var len = _.filter(nodes, codeType).length;
+
+ return (
+ // Got 3 or 4 code blocks
+ (len === 3 || len === 4) &&
+ // Ensure all nodes are at the end
+ _.all(_.last(nodes, len), codeType)
+ );
+}
+
+module.exports = isExercise;
diff --git a/lib/parse/is_quiz.js b/lib/parse/is_quiz.js
new file mode 100644
index 0000000..3322ff0
--- /dev/null
+++ b/lib/parse/is_quiz.js
@@ -0,0 +1,87 @@
+var _ = require('lodash');
+
+function isQuizNode(node) {
+ return (/^[(\[][ x][)\]]/).test(node.text || node);
+}
+
+function isTableQuestion(nodes) {
+ var block = questionBlock(nodes);
+ return (
+ block.length === 1 &&
+ block[0].type === 'table' &&
+ _.all(block[0].cells[0].slice(1), isQuizNode)
+ );
+}
+
+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;
+}
+
+function isQuestion(nodes) {
+ return isListQuestion(nodes) || isTableQuestion(nodes);
+}
+
+// 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(
+ // Skip quiz title if there
+ nodes.slice(
+ (nodes[0] && nodes[0].type) === 'paragraph' ? 1 : 0
+ )
+ );
+
+ // Nothing that looks like questions
+ if(questions.length === 0) {
+ return false;
+ }
+
+ // Ensure all questions are correctly structured
+ return _.all(questions, isQuestion);
+}
+
+module.exports = isQuiz;
diff --git a/lib/parse/lex.js b/lib/parse/lex.js
index d9f9fd9..3391acf 100644
--- a/lib/parse/lex.js
+++ b/lib/parse/lex.js
@@ -1,6 +1,9 @@
var _ = require('lodash');
var kramed = require('kramed');
+var isExercise = require('./is_exercise');
+var isQuiz = require('./is_quiz');
+
// Split a page up into sections (lesson, exercises, ...)
function splitSections(nodes) {
var section = [];
@@ -17,59 +20,6 @@ function splitSections(nodes) {
}, []).concat([section]); // Add remaining nodes
}
-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;
-
- 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)) {
diff --git a/test/fixtures/FALSE_QUIZ.md b/test/fixtures/FALSE_QUIZ.md
new file mode 100644
index 0000000..46f9a00
--- /dev/null
+++ b/test/fixtures/FALSE_QUIZ.md
@@ -0,0 +1,120 @@
+# Learn Node.js by Example
+
+
+## Requirements
+
+- [x] A computer with internet access
+- [ ] Time: 30h (e.g. 2 months 30 mins per day or **1 week intensive**)
+
+
+## What is Node.js ?
+
+Node.js lets you *easily* build networked software (websites, applications "apps",
+using JavaScript).
+
+Its not "*point-and-click*" like WordPress, SquareSpace or Salesforce;
+you will need to write some "code". But as I will demonstrate, that's
+a *lot* easier than it sounds and gives you more power/flexibility
+and puts you in full control.
+
+## Node.js is not "Version 1.0" yet can I used it in Production?
+
+Yes! Some of the biggest organisations/companies in the world
+are using Node.js in Production systems:
+
+[Alibaba](https://github.com/alibaba/node-hbase-client),
+[Ajax.org](Ajax.org),
+[Box.com](http://tech.blog.box.com/2014/06/node-js-high-availability-at-box/), British Sky Broadcasting (Sky/Now TV),
+CNN,
+[Cloudup](https://cloudup.com/),
+Conde Nast,
+[DirectTV](http://strongloop.com/strongblog/node-summit-media-companies-embrace-node-js-for-rapidly-developing-responsive-apps/),
+[Dow Jones](http://nodejs.org/industry),
+eBay,
+[FeedHenry](http://www.feedhenry.com/),
+[GitHub](https://twitter.com/github/status/16979699217465344),
+[Google](http://venturebeat.com/2012/01/24/node-at-google-mozilla-yahoo/),
+[Groupon](http://nodeup.com/fiftyeight),
+HBO,
+Help.com,
+[HP](https://github.com/joyent/node/wiki/Projects,-Applications,-and-Companies-Using-Node),
+iTV,
+[Joyent](https://www.joyent.com/) (duh!),
+[Klout](https://klout.com),
+LinkedIn,
+McDonalds,
+[Medium](https://medium.com/the-story),
+Mozilla,
+NetFlix,
+[OpenTable](http://hapijs.com/community),
+PayPal,
+Pearson,
+~~Q~~,
+[Revolt](http://revolt.tv/),
+[Square](https://modulus.io/companies-using-node),
+Tesco,
+ThomasCook,
+Trello,
+Uber,
+Voxer,
+Walmart,
+Wikimedia (in progress of moving to SOA with node!)
+Yahoo,
+Yammer,
+[Yandex](https://www.youtube.com/watch?v=zdCxgdH4wZo),
+[Zendesk](http://radar.zendesk.com/)
+
+Want more? See: http://nodejs.org/industry/ and <br />
+https://github.com/joyent/node/wiki/Projects,-Applications,-and-Companies-Using-Node
+
+
+# Try it!
+
+## Download & Install
+
+> http://nodejs.org/download/
+
+
+## Node.js (Core) API
+
+The node.js ("core") has many useful modules.
+
+Bookmark: [http://nodejs.org/api](http://nodejs.org/api/) (you will come back to it)
+
+
+
+## Stability (Can we use it?)
+
+> *Which node.js* ***core*** *package(s) can/should I use?*
+
+Every core module has a
+["***Stability Index***"](http://nodejs.org/api/documentation.html#documentation_stability_index)
+rating on the node.js API.
+
+**General rule**: If you are being *paid* to write code
+that runs in node.js, <br /> pick core modules/methods
+with stability **Stable**, **API Frozen** and **Locked**.
+
+![Node.js Stability Index](http://i.imgur.com/xIroFrS.png)
+
+
+### Examples
+
+- [**cluster**](http://nodejs.org/api/cluster.html) is ***Experimental*** - don't use
+- [**domain**](http://nodejs.org/api/domain.html) is ***Unstable*** - don't use
+- [**path**](http://nodejs.org/api/path.html) is ***Stable*** - use
+- [**events**](http://nodejs.org/api/events.html) is ***Frozen*** - use
+- [**assert**](http://nodejs.org/api/assert.html) is ***Locked*** - use
+
+Core Modules to Learn
+
+- path
+- os
+
+
+
+Community Modules to Learn:
+
+- [jscs](https://www.npmjs.org/package/jscs) - code style checker
+- [q](https://www.npmjs.org/package/q) - promises library
+- [nd](https://www.npmjs.org/package/nd) - view documentation for a module
diff --git a/test/page.js b/test/page.js
index 42243d8..cd843c6 100644
--- a/test/page.js
+++ b/test/page.js
@@ -122,3 +122,11 @@ describe('Relative images', function() {
assert(LEXED[0].content.indexOf('"preview2.png"') !== -1);
});
});
+
+describe('Section parsing', function() {
+ it('should not have false positive quiz parsing', function() {
+ var LEXED = loadPage('FALSE_QUIZ');
+
+ assert.equal(LEXED[0].type, 'normal');
+ });
+});