summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2016-02-20 17:18:18 +0100
committerSamy Pessé <samypesse@gmail.com>2016-02-20 17:18:18 +0100
commitcafbcd409774c383a26ce22b82b2104774c54c6d (patch)
tree07920323a4197211df483f8793fce0132ca040a5
parenta22539a91eea4101d244cdaeb5c61e31376ff4bc (diff)
downloadgitbook-cafbcd409774c383a26ce22b82b2104774c54c6d.zip
gitbook-cafbcd409774c383a26ce22b82b2104774c54c6d.tar.gz
gitbook-cafbcd409774c383a26ce22b82b2104774c54c6d.tar.bz2
Complete normalization of summary with level and multiples parts
-rw-r--r--lib/backbone/summary.js103
-rw-r--r--package.json4
-rw-r--r--test/summary.js58
3 files changed, 135 insertions, 30 deletions
diff --git a/lib/backbone/summary.js b/lib/backbone/summary.js
index 8e1a66d..d9253a7 100644
--- a/lib/backbone/summary.js
+++ b/lib/backbone/summary.js
@@ -20,7 +20,11 @@ function TOCArticle(def, parent) {
// As string indicating the overall position
// ex: '1.0.0'
- this.level = def.level;
+ this.level;
+
+ // When README has been automatically added
+ this.isAutoIntro = def.isAutoIntro;
+ this.isIntroduction = def.isIntroduction;
if (!def.title) {
throw error.ParsingError(new Error('SUMMARY entries should have an non-empty title'));
@@ -41,10 +45,16 @@ function TOCArticle(def, parent) {
}
// Iterate over all articles in this articles
-TOCArticle.prototype.walk = function(iter) {
- _.each(this.articles, function(article) {
- iter(article);
- article.walk(iter);
+TOCArticle.prototype.walk = function(iter, base) {
+ base = base || this.level;
+
+ _.each(this.articles, function(article, i) {
+ var level = levelId(base, i);
+
+ if (iter(article, level) === false) {
+ return false;
+ }
+ article.walk(iter, level);
});
};
@@ -69,6 +79,11 @@ TOCArticle.prototype.isExternal = function() {
return location.isExternal(this.ref);
};
+// Return true if this article is the introduction
+TOCArticle.prototype.isIntro = function() {
+ return Boolean(this.isIntroduction);
+};
+
// Return true if has children
TOCArticle.prototype.hasChildren = function() {
return this.articles.length > 0;
@@ -147,19 +162,37 @@ TOCArticle.prototype.map = function(iter) {
A part of a ToC is a composed of a tree of articles.
*/
function TOCPart(part) {
+ if (!(this instanceof TOCPart)) return new TOCPart(part);
+
this.articles = _.map(part.articles || part.chapters, function(article) {
return new TOCArticle(article, this);
}, this);
}
// Iterate over all entries of the part
-TOCPart.prototype.walk = function(iter) {
- _.each(this.articles, function(article) {
- if (iter(article) === false) {
+TOCPart.prototype.walk = function(iter, base) {
+ var articles = this.articles;
+
+ if (articles.length == 0) return;
+
+ // Has introduction?
+ if (articles[0].isIntro()) {
+ if (iter(articles[0], '0') === false) {
+ return;
+ }
+
+ articles = articles.slice(1);
+ }
+
+
+ _.each(articles, function(article, i) {
+ var level = levelId(base, i);
+
+ if (iter(article, level) === false) {
return false;
}
- article.walk(iter);
+ article.walk(iter, level);
});
};
@@ -187,15 +220,27 @@ Summary.prototype.parse = function(content) {
return this.parser.summary(content)
- // TODO: update GitBook's parsers to return a list of parts
- .then(function(part) {
- that.parts = [new TOCPart(part)];
+ .then(function(summary) {
+ that.parts = _.map(summary.parts, TOCPart);
+
+ // Create first part if none
+ if (that.parts.length == 0) {
+ that.parts.push(new TOCPart([]));
+ }
- // Update count of articles
- that._length = 0;
- that.walk(function() {
- that._length += 1;
- });
+ // Add README as first entry
+ var firstArticle = that.parts[0].articles[0];
+ if (!firstArticle || firstArticle.path != that.book.readme.path) {
+ that.parts[0].articles.unshift(new TOCArticle({
+ title: 'Introduction',
+ path: that.book.readme.path,
+ isAutoIntro: true
+ }, that.parts[0]));
+ }
+ that.parts[0].articles[0].isIntroduction = true;
+
+ // Update count of articles and create "level"
+ that.update();
});
};
@@ -224,8 +269,10 @@ Summary.prototype.getContext = function() {
// Iterate over all entries of the summary
// iter is called with an TOCArticle
Summary.prototype.walk = function(iter) {
- _.each(this.parts, function(part) {
- part.walk(iter);
+ var hasMultipleParts = this.parts.length > 1;
+
+ _.each(this.parts, function(part, i) {
+ part.walk(iter, hasMultipleParts? levelId('', i) : null);
});
};
@@ -264,4 +311,22 @@ Summary.prototype.count = function() {
return this._length;
};
+// Update the count and indexing of "level"
+Summary.prototype.update = function() {
+ var that = this;
+
+ that._length = 0;
+ that.walk(function(article, level) {
+ article.level = level;
+ that._length += 1;
+ });
+};
+
+
+// Return a level string from a base level and an index
+function levelId(base, i) {
+ i = i + 1;
+ return (base? [base || '', i] : [i]).join('.');
+}
+
module.exports = Summary;
diff --git a/package.json b/package.json
index 7163f15..6b923b9 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,8 @@
"resolve": "0.6.3",
"mkdirp": "0.5.1",
"error": "7.0.2",
- "gitbook-markdown": "1.0.0",
- "gitbook-asciidoc": "1.0.0",
+ "gitbook-markdown": "1.0.2",
+ "gitbook-asciidoc": "1.0.1",
"gitbook-plugin-highlight": "1.0.3",
"gitbook-plugin-sharing": "1.0.1",
"gitbook-plugin-search": "1.1.0",
diff --git a/test/summary.js b/test/summary.js
index a9aea5d..8805c2d 100644
--- a/test/summary.js
+++ b/test/summary.js
@@ -2,18 +2,32 @@ var should = require('should');
var mock = require('./mock');
+function mockSummary(files, summary) {
+ return mock.setupDefaultBook(files, summary)
+ .then(function(book) {
+ return book.readme.load()
+ .then(function() {
+ return book.summary.load();
+ })
+ .thenResolve(book);
+ });
+}
+
describe('Summary / Table of contents', function() {
describe('Empty summary list', function() {
var book;
before(function() {
- return mock.setupDefaultBook({})
+ return mockSummary({})
.then(function(_book) {
book = _book;
- return book.summary.load();
});
});
+ it('should add README as first entry', function() {
+ should(book.summary.getArticle('README.md')).be.ok();
+ });
+
it('should correctly count articles', function() {
book.summary.count().should.equal(1);
});
@@ -23,14 +37,13 @@ describe('Summary / Table of contents', function() {
var book;
before(function() {
- return mock.setupDefaultBook({
+ return mockSummary({
'SUMMARY.md': '# Summary\n\n'
+ '* [Hello](./hello.md)\n'
+ '* [World](./world.md)\n\n'
})
.then(function(_book) {
book = _book;
- return book.summary.load();
});
});
@@ -39,11 +52,40 @@ describe('Summary / Table of contents', function() {
});
});
+ describe('Levels', function() {
+ var book;
+
+ before(function() {
+ return mockSummary({
+ 'SUMMARY.md': '# Summary\n\n'
+ + '* [Hello](./hello.md)\n'
+ + ' * [Hello 2](./hello2.md)\n'
+ + '* [World](./world.md)\n\n'
+ + '## Part 2\n\n'
+ + '* [Hello 3](./hello.md)\n'
+ + ' * [Hello 4](./hello2.md)\n'
+ })
+ .then(function(_book) {
+ book = _book;
+ });
+ });
+
+ it('should correctly index levels', function() {
+ book.summary.getArticleByLevel('0').title.should.equal('Introduction');
+ book.summary.getArticleByLevel('1.1').title.should.equal('Hello');
+ book.summary.getArticleByLevel('1.1.1').title.should.equal('Hello 2');
+ book.summary.getArticleByLevel('1.2').title.should.equal('World');
+
+ book.summary.getArticleByLevel('2.1').title.should.equal('Hello 3');
+ book.summary.getArticleByLevel('2.1.1').title.should.equal('Hello 4');
+ });
+ });
+
describe('External', function() {
var book;
before(function() {
- return mock.setupDefaultBook({}, [
+ return mockSummary({}, [
{
title: 'Google',
path: 'https://www.google.fr'
@@ -51,7 +93,6 @@ describe('Summary / Table of contents', function() {
])
.then(function(_book) {
book = _book;
- return book.summary.load();
});
});
@@ -66,7 +107,7 @@ describe('Summary / Table of contents', function() {
should(article.path).not.be.ok();
article.title.should.equal('Google');
- article.ref.should.equal('https:/www.google.fr');
+ article.ref.should.equal('https://www.google.fr');
article.isExternal().should.be.ok;
});
});
@@ -75,7 +116,7 @@ describe('Summary / Table of contents', function() {
var book;
before(function() {
- return mock.setupDefaultBook({
+ return mockSummary({
'SUMMARY.md': '# Summary\n\n' +
'* [Hello](hello.md)\n' +
'* [Hello 2](hello2.md)\n' +
@@ -86,7 +127,6 @@ describe('Summary / Table of contents', function() {
})
.then(function(_book) {
book = _book;
- return book.summary.load();
});
});