summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2016-02-21 11:56:14 +0100
committerSamy Pessé <samypesse@gmail.com>2016-02-21 11:56:14 +0100
commit884540e56530b8e1c68c94604c0b45f0489d5020 (patch)
tree13f58c99828c1745497e889b516795a99562803a
parentca1313e891f7cfbdffaf6a47bb75a8bc1d77c1a4 (diff)
downloadgitbook-884540e56530b8e1c68c94604c0b45f0489d5020.zip
gitbook-884540e56530b8e1c68c94604c0b45f0489d5020.tar.gz
gitbook-884540e56530b8e1c68c94604c0b45f0489d5020.tar.bz2
Fix .next and .prev for summary's articles to use parts
-rw-r--r--lib/backbone/summary.js119
-rw-r--r--test/summary.js37
2 files changed, 122 insertions, 34 deletions
diff --git a/lib/backbone/summary.js b/lib/backbone/summary.js
index f3994ca..5cc1418 100644
--- a/lib/backbone/summary.js
+++ b/lib/backbone/summary.js
@@ -8,8 +8,9 @@ var BackboneFile = require('./file');
/*
-An article represent an entry in the Summary.
-It's defined by a title, a reference, and children articles, the reference (ref) can be a filename + anchor or an external file (optional)
+ An article represent an entry in the Summary.
+ It's defined by a title, a reference, and children articles,
+ the reference (ref) can be a filename + anchor or an external file (optional)
*/
function TOCArticle(def, parent) {
// Title
@@ -26,16 +27,17 @@ function TOCArticle(def, parent) {
this.isAutoIntro = def.isAutoIntro;
this.isIntroduction = def.isIntroduction;
- if (!def.title) {
- throw error.ParsingError(new Error('SUMMARY entries should have an non-empty title'));
- }
+ this.validate();
- var parts = url.parse(def.path);
+ // Path can be a relative path or an url, or nothing
this.ref = def.path;
+ if (this.ref) {
+ var parts = url.parse(this.ref);
- if (!this.isExternal()) {
- this.path = parts.pathname;
- this.anchor = parts.hash;
+ if (!this.isExternal()) {
+ this.path = parts.pathname;
+ this.anchor = parts.hash;
+ }
}
this.articles = _.map(def.articles || [], function(article) {
@@ -44,6 +46,13 @@ function TOCArticle(def, parent) {
}, this);
}
+// Validate the article
+TOCArticle.prototype.validate = function() {
+ if (!this.title) {
+ throw error.ParsingError(new Error('SUMMARY entries should have an non-empty title'));
+ }
+};
+
// Iterate over all articles in this articles
TOCArticle.prototype.walk = function(iter, base) {
base = base || this.level;
@@ -91,11 +100,29 @@ TOCArticle.prototype.hasChildren = function() {
// Return true if has an article as parent
TOCArticle.prototype.hasParent = function() {
- return (this.parent instanceof TOCArticle);
+ return !(this.parent instanceof TOCPart);
+};
+
+// Return true if has a part as parent
+TOCArticle.prototype.hasParentPart = function() {
+ return (this.parent instanceof TOCPart);
+};
+
+// Return first article
+TOCArticle.prototype.first = function() {
+ return _.first(this.articles);
+};
+
+// Return last article
+TOCArticle.prototype.last = function() {
+ var last = _.last(this.articles);
+ if (!last) return null;
+
+ return last.last() || last;
};
// Return a sibling (next or prev) in the parent
-// Withotu taking in consideration children/parent
+// Without taking in consideration children/parent
TOCArticle.prototype.sibling = function(direction) {
var parentsArticles = this.parent.articles;
var pos = _.findIndex(parentsArticles, this);
@@ -115,17 +142,28 @@ TOCArticle.prototype._sibling = function(direction) {
return _.first(this.articles);
}
+ var part;
var parentsArticles = this.parent.articles;
var pos = _.findIndex(parentsArticles, this);
// First child and has parent
- if (pos == 0 && direction < 0 && this.hasParent()) {
- return this.parent;
+ if (pos == 0 && direction < 0) {
+ if (this.hasParent()) {
+ return this.parent;
+ } else if (this.hasParentPart()) {
+ part = this.parent.sibling(-1);
+ return part? part.last() : null;
+ }
}
// Last child and has parent
- if(pos == (parentsArticles.length - 1) && direction > 0 && this.hasParent()) {
- return this.parent.sibling(1);
+ if(pos == (parentsArticles.length - 1) && direction > 0) {
+ if (this.hasParent()) {
+ return this.parent.sibling(1);
+ } else if (this.hasParentPart()) {
+ part = this.parent.sibling(1);
+ return part? part.first() : null;
+ }
}
if (parentsArticles[pos + direction]) {
@@ -159,16 +197,31 @@ TOCArticle.prototype.map = function(iter) {
/*
-A part of a ToC is a composed of a tree of articles.
+ A part of a ToC is a composed of a tree of articles and an optiona title
*/
-function TOCPart(part) {
+function TOCPart(part, summary) {
if (!(this instanceof TOCPart)) return new TOCPart(part);
- this.title = part.title;
- this.articles = _.map(part.articles || part.chapters, function(article) {
- return new TOCArticle(article, this);
- }, this);
+ TOCArticle.apply(this, arguments);
+
+ this.summary = summary;
}
+util.inherits(TOCPart, TOCArticle);
+
+// Validate the part
+TOCPart.prototype.validate = function() { };
+
+// Return a sibling (next or prev) of this part
+TOCPart.prototype.sibling = function(direction) {
+ var parts = this.summary.parts;
+ var pos = _.findIndex(parts, this);
+
+ if (parts[pos + direction]) {
+ return parts[pos + direction];
+ }
+
+ return null;
+};
// Iterate over all entries of the part
TOCPart.prototype.walk = function(iter, base) {
@@ -197,9 +250,16 @@ TOCPart.prototype.walk = function(iter, base) {
});
};
-// Map over all articles
-TOCPart.prototype.map = function(iter) {
- return _.map(this.articles, iter);
+// Return templating context for a part
+TOCPart.prototype.getContext = function(onArticle) {
+ onArticle = onArticle || function(article) {
+ return article.getContext();
+ };
+
+ return {
+ title: this.title,
+ articles: this.map(onArticle)
+ };
};
/*
@@ -222,11 +282,13 @@ Summary.prototype.parse = function(content) {
return this.parser.summary(content)
.then(function(summary) {
- that.parts = _.map(summary.parts, TOCPart);
+ that.parts = _.map(summary.parts, function(part) {
+ return new TOCPart(part, that);
+ });
// Create first part if none
if (that.parts.length == 0) {
- that.parts.push(new TOCPart({}));
+ that.parts.push(new TOCPart({}, that));
}
// Add README as first entry
@@ -259,10 +321,7 @@ Summary.prototype.getContext = function() {
return {
summary: {
parts: _.map(this.parts, function(part) {
- return {
- title: part.title,
- articles: part.map(onArticle)
- };
+ return part.getContext(onArticle);
})
}
};
diff --git a/test/summary.js b/test/summary.js
index 8805c2d..f7a975f 100644
--- a/test/summary.js
+++ b/test/summary.js
@@ -123,7 +123,10 @@ describe('Summary / Table of contents', function() {
' * [Hello 3](hello3.md)\n' +
' * [Hello 4](hello4.md)\n' +
' * [Hello 5](hello5.md)\n' +
- '* [Hello 6](hello6.md)\n'
+ '* [Hello 6](hello6.md)\n\n\n' +
+ '### Part 2\n\n' +
+ '* [Hello 7](hello7.md)\n' +
+ '* [Hello 8](hello8.md)\n\n'
})
.then(function(_book) {
book = _book;
@@ -136,7 +139,7 @@ describe('Summary / Table of contents', function() {
var prev = article.prev();
var next = article.next();
- should(prev).equal(null);
+ should(prev).not.be.ok();
should(next).be.ok();
next.path.should.equal('hello.md');
@@ -207,16 +210,42 @@ describe('Summary / Table of contents', function() {
next.path.should.equal('hello6.md');
});
- it('should return prev for last', function() {
+ it('should return next/prev for a joint <- parts', function() {
+ var article = book.summary.getArticle('hello7.md');
+
+ var prev = article.prev();
+ var next = article.next();
+
+ should(prev).be.ok();
+ should(next).be.ok();
+
+ prev.path.should.equal('hello6.md');
+ next.path.should.equal('hello8.md');
+ });
+
+ it('should return next/prev for a joint -> parts', function() {
var article = book.summary.getArticle('hello6.md');
var prev = article.prev();
var next = article.next();
should(prev).be.ok();
- should(next).be.not.ok();
+ should(next).be.ok();
prev.path.should.equal('hello5.md');
+ next.path.should.equal('hello7.md');
+ });
+
+ it('should return only prev for last', function() {
+ var article = book.summary.getArticle('hello8.md');
+
+ var prev = article.prev();
+ var next = article.next();
+
+ should(prev).be.ok();
+ should(next).be.not.ok();
+
+ prev.path.should.equal('hello7.md');
});
});
});