diff options
author | Samy Pesse <samypesse@gmail.com> | 2016-02-11 22:12:07 +0100 |
---|---|---|
committer | Samy Pesse <samypesse@gmail.com> | 2016-02-11 22:12:07 +0100 |
commit | 756694c029218510592418deb8aaf6f3b36f95c3 (patch) | |
tree | 86d938cbf6360d46845758a7cc9575ca04aa3594 | |
parent | 669f3b39849890c48171d807225cd6eaa3c9086b (diff) | |
download | gitbook-756694c029218510592418deb8aaf6f3b36f95c3.zip gitbook-756694c029218510592418deb8aaf6f3b36f95c3.tar.gz gitbook-756694c029218510592418deb8aaf6f3b36f95c3.tar.bz2 |
Page output a simple html string
-rw-r--r-- | lib/book.js | 1 | ||||
-rw-r--r-- | lib/generators/json.js | 2 | ||||
-rw-r--r-- | lib/generators/website/index.js | 3 | ||||
-rw-r--r-- | lib/page/html.js | 36 | ||||
-rw-r--r-- | lib/page/index.js | 25 | ||||
-rw-r--r-- | lib/utils/location.js | 14 | ||||
-rw-r--r-- | test/all.js | 1 | ||||
-rw-r--r-- | test/assertions.js | 62 | ||||
-rw-r--r-- | test/mock.js | 18 | ||||
-rw-r--r-- | test/page.js | 26 |
10 files changed, 150 insertions, 38 deletions
diff --git a/lib/book.js b/lib/book.js index 0f73135..148706c 100644 --- a/lib/book.js +++ b/lib/book.js @@ -214,6 +214,7 @@ Book.prototype.addPage = function(filename) { if (this.pages[filename]) return; this.pages[filename] = new Page(this, filename); + return this.pages[filename]; }; // Return a page by its filename (or undefined) diff --git a/lib/generators/json.js b/lib/generators/json.js index 560f099..5ba2d16 100644 --- a/lib/generators/json.js +++ b/lib/generators/json.js @@ -21,7 +21,7 @@ JSONGenerator.prototype.writePage = function(page) { version: gitbook.version }, path: page.path, - sections: page.content.sections + sections: page.content }; return that.output.writeFile( diff --git a/lib/generators/website/index.js b/lib/generators/website/index.js index f474cbb..67c80b6 100644 --- a/lib/generators/website/index.js +++ b/lib/generators/website/index.js @@ -19,7 +19,4 @@ WebsiteGenerator.prototype.writePage = function(page) { }; - - - module.exports = WebsiteGenerator; diff --git a/lib/page/html.js b/lib/page/html.js index f828d11..b19d5ed 100644 --- a/lib/page/html.js +++ b/lib/page/html.js @@ -4,6 +4,7 @@ var domSerializer = require('dom-serializer'); var slug = require('github-slugid'); var Promise = require('../utils/promise'); +var location = require('../utils/location'); // Render a cheerio DOM as html function renderDOM($, dom, options) { @@ -18,7 +19,10 @@ function HTMLPipeline(htmlString, opts) { _.bindAll(this); this.opts = _.defaults(opts || {}, { - convertImages: true + convertImages: true, + + // Calcul new href for a relative link + onRelativeLink: _.identity }); this.$ = cheerio.load(htmlString, { @@ -31,15 +35,38 @@ function HTMLPipeline(htmlString, opts) { }); } +// Normalize links +HTMLPipeline.prototype.normalizeLinks = function() { + var that = this; + + this.$('a').each(function() { + var $a = that.$(this); + + var href = $a.attr('href'); + if (!href) return; + + if (location.isAnchor(href)) { + // Don't "change" anchor links + } else if (location.isRelative(href)) { + $a.attr('href', that.opts.onRelativeLink(href)); + } else { + // External links + $a.attr('target', '_blank'); + } + + }); +}; + // Add ID to headings HTMLPipeline.prototype.addHeadingIDs = function() { var that = this; this.$('h1,h2,h3,h4,h5,h6').each(function() { - // Already has an ID? - if (that.$(this).attr('id')) return; + var $h = that.$(this); - that.$(this).attr('id', slug(that.$(this).text())); + // Already has an ID? + if ($h.attr('id')) return; + $h.attr('id', slug($h.text())); }); }; @@ -48,6 +75,7 @@ HTMLPipeline.prototype.output = function() { var that = this; return Promise() + .then(this.normalizeLinks) .then(this.addHeadingIDs) .then(function() { return renderDOM(that.$); diff --git a/lib/page/index.js b/lib/page/index.js index 8f8819c..e7a4fec 100644 --- a/lib/page/index.js +++ b/lib/page/index.js @@ -3,7 +3,6 @@ var path = require('path'); var parsers = require('gitbook-parsers'); var error = require('../utils/error'); -var Promise = require('../utils/promise'); var HTMLPipeline = require('./html'); /* @@ -99,21 +98,23 @@ Page.prototype.parse = function(opts) { // Render markup using the parser .then(function() { return that.parser.page(that.content) - .then(that.update); + .then(function(out) { + var content = _.pluck(out.sections, 'content').join('\n'); + that.update(content); + }); }) // Normalize HTML output .then(function() { - return Promise.map(that.content.sections, function(section) { - var pipeline = new HTMLPipeline(section.content, opts); - - return pipeline.output() - .then(function(content) { - return { - content: content - }; - }); - }); + var pipelineOpts = _.extend({ + onRelativeLink: function(href) { + console.log('href', href); + } + }, opts); + var pipeline = new HTMLPipeline(that.content, pipelineOpts); + + return pipeline.output() + .then(that.update); }); }; diff --git a/lib/utils/location.js b/lib/utils/location.js index d57e84f..efe1425 100644 --- a/lib/utils/location.js +++ b/lib/utils/location.js @@ -9,13 +9,23 @@ function isExternal(href) { } } - // Inverse of isExternal function isRelative(href) { return !isExternal(href); } +// Return true if the link is an achor +function isAnchor(href) { + try { + var parsed = url.parse(href); + return !!(!parsed.protocol && !parsed.path && parsed.hash); + } catch(err) { + return false; + } +} + module.exports = { isExternal: isExternal, - isRelative: isRelative + isRelative: isRelative, + isAnchor: isAnchor }; diff --git a/test/all.js b/test/all.js index 827a182..08be793 100644 --- a/test/all.js +++ b/test/all.js @@ -5,6 +5,7 @@ require('./readme'); require('./summary'); require('./glossary'); require('./langs'); +require('./page'); require('./parse'); require('./git'); diff --git a/test/assertions.js b/test/assertions.js new file mode 100644 index 0000000..b936995 --- /dev/null +++ b/test/assertions.js @@ -0,0 +1,62 @@ +var fs = require('fs'); +var _ = require('lodash'); +var cheerio = require('cheerio'); +var should = require('should'); + +// Assertions to test if an Output has generated a file +should.Assertion.add('file', function(file, description) { + this.params = { + actual: this.obj.toString(), + operator: 'have file ' + file, + message: description + }; + + this.obj.should.have.property('resolve').which.is.a.Function; + this.assert(fs.existsSync(this.obj.resolve(file))); +}); + +should.Assertion.add('html', function(rules, description) { + this.params = { actual: 'HTML string', operator: 'valid html', message: description }; + var $ = cheerio.load(this.obj); + + _.each(rules, function(validations, query) { + validations = _.defaults(validations || {}, { + // Select a specific element in the list of matched elements + index: null, + + // Check that there is the correct count of elements + count: 1, + + // Check attribute values + attributes: {}, + + // Trim inner text + trim: false, + + // Check inner text + text: undefined + }); + + var $el = $(query); + + // Select correct element + if (_.isNumber(validations.index)) $el = $($el.get(validations.index)); + + // Test number of elements + $el.length.should.be.equal(validations.count); + + // Test text + if (validations.text !== undefined) { + var text = $el.text(); + if (validations.trim) text = text.trim(); + text.should.be.equal(validations.text); + } + + // Test attributes + _.each(validations.attributes, function(value, name) { + var attr = $el.attr(name); + should(attr).be.ok(); + attr.should.be.equal(value); + }); + }); +}); diff --git a/test/mock.js b/test/mock.js index 1f7e7d5..ce7b027 100644 --- a/test/mock.js +++ b/test/mock.js @@ -1,15 +1,14 @@ var Q = require('q'); var _ = require('lodash'); -var fs = require('fs'); var tmp = require('tmp'); var path = require('path'); -var should = require('should'); - var Book = require('../').Book; var Output = require('../lib/output'); var NodeFS = require('../lib/fs/node'); +require('./assertions'); + // Create filesystem instance for testing var nodeFS = new NodeFS(); @@ -74,19 +73,6 @@ function outputDefaultBook(generator, files, opts) { }); } -// Assertions to test if an Output has generated a file -should.Assertion.add('file', function(file, description) { - this.params = { - actual: this.obj.toString(), - operator: 'have file ' + file, - message: description - }; - - this.obj.should.have.property('resolve').which.is.a.Function; - this.assert(fs.existsSync(this.obj.resolve(file))); -}); - - module.exports = { setupBook: setupBook, setupDefaultBook: setupDefaultBook, diff --git a/test/page.js b/test/page.js new file mode 100644 index 0000000..66c06e1 --- /dev/null +++ b/test/page.js @@ -0,0 +1,26 @@ +var mock = require('./mock'); + +describe('Page', function() { + var book; + + before(function() { + return mock.setupDefaultBook({ + 'heading.md': '# Hello\n\n## World' + }) + .then(function(_book) { + book = _book; + return book.summary.load(); + }); + }); + + it.only('should add a default ID to headings', function() { + var page = book.addPage('heading.md'); + + return page.parse() + .then(function() { + console.log(page.content); + }); + + }); + +});
\ No newline at end of file |