diff options
Diffstat (limited to 'lib/parse')
-rw-r--r-- | lib/parse/index.js | 4 | ||||
-rw-r--r-- | lib/parse/page.js | 62 | ||||
-rw-r--r-- | lib/parse/summary.js | 112 |
3 files changed, 178 insertions, 0 deletions
diff --git a/lib/parse/index.js b/lib/parse/index.js new file mode 100644 index 0000000..2caf6a4 --- /dev/null +++ b/lib/parse/index.js @@ -0,0 +1,4 @@ +module.exports = { + summary: require('./summary'), + page: require('./page'), +}; diff --git a/lib/parse/page.js b/lib/parse/page.js new file mode 100644 index 0000000..047f3e4 --- /dev/null +++ b/lib/parse/page.js @@ -0,0 +1,62 @@ +var _ = require('lodash'); +var marked = require('marked'); + + +// Split a page up into sections (lesson, exercises, ...) +function splitSections(nodes) { + var section = []; + + return _.reduce(nodes, function(sections, el) { + if(el.type === 'hr') { + sections.push(section); + section = []; + } else { + section.push(el); + } + + return sections; + }, []).concat([section]); // Add remaining nodes +} + +// What is the type of this section +function sectionType(nodes) { + if(_.filter(nodes, { + type: 'code' + }).length === 3) { + return 'exercise'; + } + + return 'normal'; +} + +function parsePage(src) { + var nodes = marked.lexer(src); + + return _.chain(splitSections(nodes)) + .map(function(section) { + // Detect section type + section.type = sectionType(section); + return section; + }) + .map(function(section, idx) { + // Transform given type + if(section.type === 'exercise' && (idx % 2) == 1) { + return { + type: section.type, + }; + } + + // marked's Render expects this, we don't use it yet + section.links = {}; + + // Render normal pages + return { + type: section.type, + content: marked.parser(section) + }; + }) + .value(); +} + +// Exports +module.exports = parsePage; diff --git a/lib/parse/summary.js b/lib/parse/summary.js new file mode 100644 index 0000000..8787554 --- /dev/null +++ b/lib/parse/summary.js @@ -0,0 +1,112 @@ +var _ = require('lodash'); +var marked = require('marked'); + + +// Utility function for splitting a list into groups +function splitBy(list, starter, ender) { + var starts = 0; + var ends = 0; + var group = []; + + // Groups + return _.reduce(list, function(groups, value) { + // Ignore start and end delimiters in resulted groups + if(starter(value)) { + starts++; + } else if(ender(value)) { + ends++; + } + + // Add current value to group + group.push(value); + + // We've got a matching + if(starts === ends && starts !== 0) { + // Add group to end groups + // (remove starter and ender token) + groups.push(group.slice(1, -1)); + + // Reset group + group = []; + } + + return groups; + }, []); +} + +function listSplit(nodes, start_type, end_type) { + return splitBy(nodes, function(el) { + return el.type === start_type; + }, function(el) { + return el.type === end_type; + }); +} + +// Get the biggest list +// out of a list of marked nodes +function filterList(nodes) { + return _.chain(nodes) + .toArray() + .rest(function(el) { + // Get everything after list_start + return el.type !== 'list_start'; + }) + .reverse() + .rest(function(el) { + // Get everything after list_end (remember we're reversed) + return el.type !== 'list_end'; + }) + .reverse() + .value().slice(1, -1); +} + +// Parses an Article or Chapter title +// supports extracting links +function parseTitle(src) { + // Check if it's a link + var matches = marked.InlineLexer.rules.link.exec(src); + + // Not a link, return plain text + if(!matches) { + return { + title: src, + path: null, + }; + } + + return { + title: matches[1], + path: matches[2], + }; +} + +function parseArticle(nodes) { + return parseTitle(_.first(nodes).text); +} + +function parseChapter(nodes) { + return { + chapter: parseTitle(_.first(nodes).text), + articles: _.map(listSplit(filterList(nodes), 'list_item_start', 'list_item_end'), parseArticle) + }; +} + +function parseSummary(src) { + var nodes = marked.lexer(src); + + // Get out list of chapters + var chapterList = filterList(nodes); + + // Split out chapter sections + var chapters = _.chain(listSplit(chapterList, 'list_item_start', 'list_item_end')) + .map(parseChapter) + .value(); + + return { + chapters: chapters + }; +} + + +// Exports +module.exports = parseSummary; |