Merge pull request #472 from GitbookIO/fix/quizzes

Fix/quizzes
This commit is contained in:
Aaron O'Mullan
2014-10-13 15:55:32 +02:00
5 changed files with 235 additions and 53 deletions
+17
View File
@@ -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;
+87
View File
@@ -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;
+3 -53
View File
@@ -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)) {
+120
View File
@@ -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
+8
View File
@@ -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');
});
});