diff options
author | Aaron O'Mullan <aaron.omullan@gmail.com> | 2014-10-13 15:55:32 +0200 |
---|---|---|
committer | Aaron O'Mullan <aaron.omullan@gmail.com> | 2014-10-13 15:55:32 +0200 |
commit | c9ec911d0eab7296a808e12470abe90bf764cd6a (patch) | |
tree | 187c8af2fb58bb6a641f6c0639966dfefc1ada5b | |
parent | 32a64be407e05b6ce4275e6f3f9213e8f87f884a (diff) | |
parent | 84d7662fd8a2d1abfd0feb92f54064e5d9b9d072 (diff) | |
download | gitbook-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.js | 17 | ||||
-rw-r--r-- | lib/parse/is_quiz.js | 87 | ||||
-rw-r--r-- | lib/parse/lex.js | 56 | ||||
-rw-r--r-- | test/fixtures/FALSE_QUIZ.md | 120 | ||||
-rw-r--r-- | test/page.js | 8 |
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**. + + + + +### 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'); + }); +}); |