diff options
-rw-r--r-- | CHANGES.md | 1 | ||||
-rw-r--r-- | lib/models/parser.js | 4 | ||||
-rw-r--r-- | lib/models/summary.js | 34 | ||||
-rw-r--r-- | lib/modifiers/summary/__tests__/mergeAtLevel.js | 45 | ||||
-rw-r--r-- | lib/modifiers/summary/__tests__/moveArticle.js | 68 | ||||
-rw-r--r-- | lib/modifiers/summary/editArticleTitle.js | 4 | ||||
-rw-r--r-- | lib/modifiers/summary/editPartTitle.js | 1 | ||||
-rw-r--r-- | lib/modifiers/summary/index.js | 3 | ||||
-rw-r--r-- | lib/modifiers/summary/insertArticle.js | 56 | ||||
-rw-r--r-- | lib/modifiers/summary/mergeAtLevel.js (renamed from lib/modifiers/summary/editArticle.js) | 37 | ||||
-rw-r--r-- | lib/modifiers/summary/moveArticle.js | 82 | ||||
-rw-r--r-- | lib/modifiers/summary/removeArticle.js | 37 | ||||
-rw-r--r-- | lib/modifiers/summary/unshiftArticle.js | 2 | ||||
-rw-r--r-- | lib/output/__tests__/website.js | 9 | ||||
-rw-r--r-- | lib/parse/parseIgnore.js | 3 | ||||
-rw-r--r-- | package.json | 2 |
16 files changed, 327 insertions, 61 deletions
@@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix `uk` translation - Fix heading ID including dashes - Fix error in code highlighting for unknown languages +- Fix data-uri images being handled as external images - Accept SSH url as plugin version - Add templating blocks `markdown`, `asciidoc` and `markup` - Better search experience diff --git a/lib/models/parser.js b/lib/models/parser.js index e776582..d28a4e2 100644 --- a/lib/models/parser.js +++ b/lib/models/parser.js @@ -43,6 +43,10 @@ Parser.prototype.parseGlossary = function(content) { Parser.prototype.preparePage = function(content) { var page = this.get('page'); + if (!page.prepare) { + return Promise(content); + } + return Promise(page.prepare(content)); }; diff --git a/lib/models/summary.js b/lib/models/summary.js index ec7e05f..8a4afc7 100644 --- a/lib/models/summary.js +++ b/lib/models/summary.js @@ -56,7 +56,7 @@ Summary.prototype.getArticle = function(iter, partIter) { Return a part/article by its level @param {String} level - @return {Article} + @return {Article|Part} */ Summary.prototype.getByLevel = function(level) { function iterByLevel(article) { @@ -130,6 +130,27 @@ Summary.prototype.getPrevArticle = function(current) { }; /** + Return the parent article, or parent part of an article + + @param {String|Article} current + @return {Article|Part|Null} +*/ +Summary.prototype.getParent = function (level) { + // Coerce to level + level = is.string(level)? level : level.getLevel(); + + // Get parent level + var parentLevel = getParentLevel(level); + if (!parentLevel) { + return null; + } + + // Get parent of the position + var parentArticle = this.getByLevel(parentLevel); + return parentArticle || null; +}; + +/** Render summary as text @param {String} parseExt Extension of the parser to use @@ -188,4 +209,15 @@ Summary.createFromParts = function createFromParts(file, parts) { }); }; +/** + Returns parent level of a level + + @param {String} level + @return {String} +*/ +function getParentLevel(level) { + var parts = level.split('.'); + return parts.slice(0, -1).join('.'); +} + module.exports = Summary; diff --git a/lib/modifiers/summary/__tests__/mergeAtLevel.js b/lib/modifiers/summary/__tests__/mergeAtLevel.js new file mode 100644 index 0000000..e2635ec --- /dev/null +++ b/lib/modifiers/summary/__tests__/mergeAtLevel.js @@ -0,0 +1,45 @@ +var Immutable = require('immutable'); +var Summary = require('../../../models/summary'); +var File = require('../../../models/file'); + +describe('mergeAtLevel', function() { + var mergeAtLevel = require('../mergeAtLevel'); + var summary = Summary.createFromParts(File(), [ + { + articles: [ + { + title: '1.1', + path: '1.1' + }, + { + title: '1.2', + path: '1.2' + } + ] + }, + { + title: 'Part I', + articles: [] + } + ]); + + it('should edit a part', function() { + var beforeChildren = summary.getByLevel('1').getArticles(); + var newSummary = mergeAtLevel(summary, '1', {title: 'Part O'}); + var edited = newSummary.getByLevel('1'); + + expect(edited.getTitle()).toBe('Part O'); + // Same children + expect(Immutable.is(beforeChildren, edited.getArticles())).toBe(true); + }); + + it('should edit a part', function() { + var beforePath = summary.getByLevel('1.2').getPath(); + var newSummary = mergeAtLevel(summary, '1.2', {title: 'Renamed article'}); + var edited = newSummary.getByLevel('1.2'); + + expect(edited.getTitle()).toBe('Renamed article'); + // Same children + expect(Immutable.is(beforePath, edited.getPath())).toBe(true); + }); +}); diff --git a/lib/modifiers/summary/__tests__/moveArticle.js b/lib/modifiers/summary/__tests__/moveArticle.js new file mode 100644 index 0000000..9a101f6 --- /dev/null +++ b/lib/modifiers/summary/__tests__/moveArticle.js @@ -0,0 +1,68 @@ +var Immutable = require('immutable'); +var Summary = require('../../../models/summary'); +var File = require('../../../models/file'); + +describe('moveArticle', function() { + var moveArticle = require('../moveArticle'); + var summary = Summary.createFromParts(File(), [ + { + articles: [ + { + title: '1.1', + path: '1.1' + }, + { + title: '1.2', + path: '1.2' + } + ] + }, + { + title: 'Part I', + articles: [ + { + title: '2.1', + path: '2.1', + articles: [ + { + title: '2.1.1', + path: '2.1.1' + }, + { + title: '2.1.2', + path: '2.1.2' + } + ] + }, + { + title: '2.2', + path: '2.2' + } + ] + } + ]); + + it('should move an article at in place', function() { + var newSummary = moveArticle(summary, '2.1', '2.1'); + + expect(Immutable.is(summary, newSummary)).toBe(true); + }); + + it('should move an article to an previous level', function() { + var newSummary = moveArticle(summary, '2.2', '2.1'); + var moved = newSummary.getByLevel('2.1'); + var other = newSummary.getByLevel('2.2'); + + expect(moved.getTitle()).toBe('2.2'); + expect(other.getTitle()).toBe('2.1'); + }); + + it('should move an article to a next level', function() { + var newSummary = moveArticle(summary, '2.1', '2.2'); + var moved = newSummary.getByLevel('2.1'); + var other = newSummary.getByLevel('2.2'); + + expect(moved.getTitle()).toBe('2.2'); + expect(other.getTitle()).toBe('2.1'); + }); +}); diff --git a/lib/modifiers/summary/editArticleTitle.js b/lib/modifiers/summary/editArticleTitle.js index bd9b6f2..4edee83 100644 --- a/lib/modifiers/summary/editArticleTitle.js +++ b/lib/modifiers/summary/editArticleTitle.js @@ -1,4 +1,4 @@ -var editArticle = require('./editArticle'); +var mergeAtLevel = require('./mergeAtLevel'); /** Edit title of an article @@ -9,7 +9,7 @@ var editArticle = require('./editArticle'); @return {Summary} */ function editArticleTitle(summary, level, newTitle) { - return editArticle(summary, level, { + return mergeAtLevel(summary, level, { title: newTitle }); } diff --git a/lib/modifiers/summary/editPartTitle.js b/lib/modifiers/summary/editPartTitle.js index 472399b..b79ac1e 100644 --- a/lib/modifiers/summary/editPartTitle.js +++ b/lib/modifiers/summary/editPartTitle.js @@ -1,4 +1,3 @@ - /** Edit title of a part in the summary diff --git a/lib/modifiers/summary/index.js b/lib/modifiers/summary/index.js index 855d7cc..4498287 100644 --- a/lib/modifiers/summary/index.js +++ b/lib/modifiers/summary/index.js @@ -1,6 +1,7 @@ - module.exports = { insertArticle: require('./insertArticle'), + moveArticle: require('./moveArticle'), + removeArticle: require('./removeArticle'), unshiftArticle: require('./unshiftArticle'), editPartTitle: require('./editPartTitle'), diff --git a/lib/modifiers/summary/insertArticle.js b/lib/modifiers/summary/insertArticle.js index ae920c2..849f39e 100644 --- a/lib/modifiers/summary/insertArticle.js +++ b/lib/modifiers/summary/insertArticle.js @@ -1,25 +1,14 @@ var is = require('is'); var SummaryArticle = require('../../models/summaryArticle'); -var editArticle = require('./editArticle'); +var mergeAtLevel = require('./mergeAtLevel'); var indexArticleLevels = require('./indexArticleLevels'); - -/** - Get level of parent of an article - - @param {String} level - @return {String} -*/ -function getParentLevel(level) { - var parts = level.split('.'); - return parts.slice(0, -1).join('.'); -} - /** - Insert an article in a summary at a specific position + Returns a new Summary with the article at the given level, with + subsequent article shifted. @param {Summary} summary - @param {String|Article} level: level to insert after + @param {String|Article} level: level to insert at @param {Article} article @return {Summary} */ @@ -27,37 +16,34 @@ function insertArticle(summary, level, article) { article = SummaryArticle(article); level = is.string(level)? level : level.getLevel(); - var parentLevel = getParentLevel(level); - - if (!parentLevel) { - // todo: insert new part - return summary; - } - - // Get parent of the position - var parentArticle = summary.getByLevel(parentLevel); - if (!parentLevel) { + var parent = summary.getParent(level); + if (!parent) { return summary; } // Find the index to insert at - var articles = parentArticle.getArticles(); - var index = articles.findIndex(function(art) { - return art.getLevel() === level; - }); - if (!index) { - return summary; - } + var articles = parent.getArticles(); + var index = getLeafIndex(level); // Insert the article at the right index articles = articles.insert(index, article); // Reindex the level from here - parentArticle = parentArticle.set('articles', articles); - parentArticle = indexArticleLevels(parentArticle); + parent = parent.set('articles', articles); + parent = indexArticleLevels(parent); - return editArticle(summary, parentLevel, parentArticle); + return mergeAtLevel(summary, parent.getLevel(), parent); +} +/** + @param {String} + @return {Number} The index of this level within its parent's children + */ +function getLeafIndex(level) { + var arr = level.split('.').map(function (char) { + return parseInt(char, 10); + }); + return arr[arr.length - 1] - 1; } module.exports = insertArticle; diff --git a/lib/modifiers/summary/editArticle.js b/lib/modifiers/summary/mergeAtLevel.js index 1625398..9a95ffc 100644 --- a/lib/modifiers/summary/editArticle.js +++ b/lib/modifiers/summary/mergeAtLevel.js @@ -11,16 +11,17 @@ function editArticleInList(articles, level, newArticle) { return articles.map(function(article) { var articleLevel = article.getLevel(); - if (articleLevel == level) { + if (articleLevel === level) { + // it is the article to edit return article.merge(newArticle); - } - - if (level.indexOf(articleLevel) === 0) { + } else if (level.indexOf(articleLevel) === 0) { + // it is a parent var articles = editArticleInList(article.getArticles(), level, newArticle); return article.set('articles', articles); + } else { + // This is not the article you are looking for + return article; } - - return article; }); } @@ -35,36 +36,40 @@ function editArticleInList(articles, level, newArticle) { */ function editArticleInPart(part, level, newArticle) { var articles = part.getArticles(); - articles = editArticleInList(articles); + articles = editArticleInList(articles, level, newArticle); return part.set('articles', articles); } /** - Edit an article in a summary + Edit an article, or a part, in a summary. Does a shallow merge. @param {Summary} summary @param {String} level - @param {Article} newArticle + @param {Article|Part} newValue @return {Summary} */ -function editArticle(summary, level, newArticle) { - var parts = summary.getParts(); - +function mergeAtLevel(summary, level, newValue) { var levelParts = level.split('.'); - var partIndex = Number(levelParts[0]); + var partIndex = Number(levelParts[0]) -1; + var parts = summary.getParts(); var part = parts.get(partIndex); if (!part) { return summary; } - part = editArticleInPart(part, level, newArticle); - parts = parts.set(partIndex, part); + var isEditingPart = levelParts.length < 2; + if (isEditingPart) { + part = part.merge(newValue); + } else { + part = editArticleInPart(part, level, newValue); + } + parts = parts.set(partIndex, part); return summary.set('parts', parts); } -module.exports = editArticle; +module.exports = mergeAtLevel; diff --git a/lib/modifiers/summary/moveArticle.js b/lib/modifiers/summary/moveArticle.js new file mode 100644 index 0000000..06d82ca --- /dev/null +++ b/lib/modifiers/summary/moveArticle.js @@ -0,0 +1,82 @@ +var is = require('is'); +var removeArticle = require('./removeArticle'); +var insertArticle = require('./insertArticle'); + +/** + Returns a new summary, with the given article removed from its + origin level, and placed at the given target level. + + @param {Summary} summary + @param {String|SummaryArticle} origin: level to remove + @param {String|SummaryArticle} target: the level where the article will be found + @return {Summary} +*/ +function moveArticle(summary, origin, target) { + // Coerce to level + var originLevel = is.string(origin)? origin : origin.getLevel(); + var targetLevel = is.string(target)? target : target.getLevel(); + + var article = summary.getByLevel(originLevel); + + // Remove + var removed = removeArticle(summary, origin); + + // Adjust targetLevel if removing impacted it + targetLevel = arrayToLevel( + shiftLevel(levelToArray(originLevel), + levelToArray(targetLevel))); + // Re-insert + return insertArticle(removed, target, article); +} + +/** + @param {Array<Number>} removedLevel + @param {Array<Number>} level The level to udpate + @return {Array<Number>} + */ +function shiftLevel(removedLevel, level) { + if (level.length === 0) { + // `removedLevel` is under level, so no effect + return level; + } else if (removedLevel.length === 0) { + // Either `level` is a child of `removedLevel`... or they are equal + // This is undefined behavior. + return level; + } + + var removedRoot = removedLevel[0]; + var root = level[0]; + var removedRest = removedLevel.slice(1); + var rest = level.slice(1); + + if (removedRoot < root) { + // It will shift levels at this point. The rest is unchanged. + return Array.prototype.concat(root - 1, rest); + } else if (removedRoot === root) { + // Look deeper + return Array.prototype.concat(root, shiftLevel(removedRest, rest)); + } else { + // No impact + return level; + } +} + +/** + @param {String} + @return {Array<Number>} + */ +function levelToArray(l) { + return l.split('.').map(function (char) { + return parseInt(char, 10); + }); +} + +/** + @param {Array<Number>} + @return {String} + */ +function arrayToLevel(a) { + return a.join('.'); +} + +module.exports = moveArticle; diff --git a/lib/modifiers/summary/removeArticle.js b/lib/modifiers/summary/removeArticle.js new file mode 100644 index 0000000..8a30d0a --- /dev/null +++ b/lib/modifiers/summary/removeArticle.js @@ -0,0 +1,37 @@ +var is = require('is'); +var mergeAtLevel = require('./mergeAtLevel'); +var indexArticleLevels = require('./indexArticleLevels'); + +/** + Remove an article from a level. + + @param {Summary} summary + @param {String|SummaryArticle} level: level to remove + @return {Summary} +*/ +function removeArticle(summary, level) { + // Coerce to level + level = is.string(level)? level : level.getLevel(); + + var parent = summary.getParent(level); + + var articles = parent.getArticles(); + // Find the index to remove + var index = articles.findIndex(function(art) { + return art.getLevel() === level; + }); + if (index === -1) { + return summary; + } + + // Remove from children + articles = articles.remove(index); + parent = parent.set('articles', articles); + + // Reindex the level from here + parent = indexArticleLevels(parent); + + return mergeAtLevel(summary, parent.getLevel(), parent); +} + +module.exports = removeArticle; diff --git a/lib/modifiers/summary/unshiftArticle.js b/lib/modifiers/summary/unshiftArticle.js index 3f2ae4d..d1ebc05 100644 --- a/lib/modifiers/summary/unshiftArticle.js +++ b/lib/modifiers/summary/unshiftArticle.js @@ -4,7 +4,7 @@ var SummaryPart = require('../../models/summaryPart'); var indexLevels = require('./indexLevels'); /** - Insert an article at the + Insert an article at the beginning of summary @param {Summary} summary @param {Article} article diff --git a/lib/output/__tests__/website.js b/lib/output/__tests__/website.js index 6b949a4..f9fcdae 100644 --- a/lib/output/__tests__/website.js +++ b/lib/output/__tests__/website.js @@ -12,6 +12,15 @@ describe('WebsiteGenerator', function() { }); }); + pit('should generate an index.html for AsciiDoc', function() { + return generateMock(WebsiteGenerator, { + 'README.adoc': 'Hello World' + }) + .then(function(folder) { + expect(folder).toHaveFile('index.html'); + }); + }); + pit('should generate an HTML file for each articles', function() { return generateMock(WebsiteGenerator, { 'README.md': 'Hello World', diff --git a/lib/parse/parseIgnore.js b/lib/parse/parseIgnore.js index b23bfd8..fafcc6f 100644 --- a/lib/parse/parseIgnore.js +++ b/lib/parse/parseIgnore.js @@ -27,9 +27,6 @@ function parseIgnore(book) { // Skip book outputs '_book', - '*.pdf', - '*.epub', - '*.mobi', // Ignore files in the templates folder '_layouts' diff --git a/package.json b/package.json index f72b523..4dd61dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitbook", - "version": "3.0.0-pre.10", + "version": "3.0.0-pre.11", "homepage": "https://www.gitbook.com", "description": "Library and cmd utility to generate GitBooks", "main": "lib/index.js", |