diff options
Diffstat (limited to 'lib/template.js')
-rw-r--r-- | lib/template.js | 163 |
1 files changed, 102 insertions, 61 deletions
diff --git a/lib/template.js b/lib/template.js index 3a4985c..9f01d3c 100644 --- a/lib/template.js +++ b/lib/template.js @@ -4,45 +4,16 @@ var path = require("path"); var nunjucks = require("nunjucks"); var escapeStringRegexp = require("escape-string-regexp"); -var git = require("./utils/git"); -var fs = require("./utils/fs"); var batch = require("./utils/batch"); var pkg = require("../package.json"); +var defaultBlocks = require("./blocks"); +var BookLoader = require("./conrefs_loader") - -// The loader should handle relative and git url -var BookLoader = nunjucks.Loader.extend({ - async: true, - - init: function(book) { - this.book = book; - }, - - getSource: function(fileurl, callback) { - var that = this; - - git.resolveFile(fileurl) - .then(function(filepath) { - // Is local file - if (!filepath) filepath = path.resolve(that.book.root, fileurl); - else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath) - - // Read file from absolute path - return fs.readFile(filepath) - .then(function(source) { - return { - src: source.toString(), - path: filepath - } - }); - }) - .nodeify(callback); - }, - - resolve: function(from, to) { - return path.resolve(path.dirname(from), to); - } -}); +// Normalize result from a block +function normBlockResult(blk) { + if (_.isString(blk)) blk = { body: blk }; + return blk; +} var TemplateEngine = function(book) { @@ -71,22 +42,21 @@ var TemplateEngine = function(book) { // List of tags shortcuts this.shortcuts = []; - // Map of blocks + // Map of blocks bodies (that requires post-processing) + this.blockBodies = {}; + + // Map of added blocks this.blocks = {}; // Bind methods _.bindAll(this); - // Default block "html" that return html not parsed - this.addBlock("html", { - process: _.identity - }); + // Add default blocks + this.addBlocks(defaultBlocks); }; -// Process a block in a context +// Process the result of block in a context TemplateEngine.prototype.processBlock = function(blk) { - if (_.isString(blk)) blk = { body: blk }; - blk = _.defaults(blk, { parse: false, post: undefined @@ -96,24 +66,24 @@ TemplateEngine.prototype.processBlock = function(blk) { var toAdd = (!blk.parse) || (blk.post != undefined); // Add to global map - if (toAdd) this.blocks[blk.id] = blk; + if (toAdd) this.blockBodies[blk.id] = blk; //Parsable block, just return it if (blk.parse) { return blk.body; } - // Return it as a macro - return "%+%"+blk.id+"%+%"; + // Return it as a position marker + return "@%@"+blk.id+"@%@"; }; -// Replace blocks by body after processing -// This is done to avoid that markdown processer parse the block content +// Replace position markers of blocks by body after processing +// This is done to avoid that markdown/asciidoc processer parse the block content TemplateEngine.prototype.replaceBlocks = function(content) { var that = this; - return content.replace(/\%\+\%([\s\S]+?)\%\+\%/g, function(match, key) { - var blk = that.blocks[key]; + return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) { + var blk = that.blockBodies[key]; if (!blk) return match; var body = blk.body; @@ -160,9 +130,41 @@ TemplateEngine.prototype.addFilter = function(filterName, func) { return true; }; +// Add multiple filters +TemplateEngine.prototype.addFilters = function(filters) { + _.each(filters, function(filter, name) { + this.addFilter(name, filter); + }, this); +}; + +// Return nunjucks extension name of a block +TemplateEngine.prototype.blockExtName = function(name) { + return 'Block'+name+'Extension'; +}; + +// Test if a block is defined +TemplateEngine.prototype.hasBlock = function(name) { + return this.env.hasExtension(this.blockExtName(name)); +}; + +// Remove a block +TemplateEngine.prototype.removeBlock = function(name) { + if (!this.hasBlock(name)) return; + + // Remove nunjucks extension + this.env.removeExtension(this.blockExtName(name)); + + // Cleanup shortcuts + this.shortcuts = _.reject(this.shortcuts, { + block: name + }); +}; + // Add a block TemplateEngine.prototype.addBlock = function(name, block) { - var that = this; + var that = this, Ext, extName; + + if (_.isFunction(block)) block = { process: block }; block = _.defaults(block || {}, { shortcuts: [], @@ -171,13 +173,17 @@ TemplateEngine.prototype.addBlock = function(name, block) { blocks: [] }); - var extName = 'Block'+name+'Extension'; - if (this.env.getExtension(extName)) { + var extName = this.blockExtName(name); + + if (this.hasBlock(name) && !defaultBlocks[name]) { this.log.warn.ln("conflict in blocks, '"+name+"' is already defined"); - return false; } + // Cleanup previous block + this.removeBlock(name); + this.log.debug.ln("add block '"+name+"'"); + this.blocks[name] = block; var Ext = function () { this.tags = [name]; @@ -260,11 +266,9 @@ TemplateEngine.prototype.addBlock = function(name, block) { }; }); - var func = that.bindContext(block.process); - Q() .then(function() { - return func.call(context, { + return that.applyBlock(name, { body: body(), args: args, kwargs: kwargs, @@ -278,7 +282,6 @@ TemplateEngine.prototype.addBlock = function(name, block) { }; }; - // Add the Extension this.env.addExtension(extName, new Ext()); @@ -287,6 +290,7 @@ TemplateEngine.prototype.addBlock = function(name, block) { _.each(block.shortcuts, function(shortcut) { this.log.debug.ln("add template shortcut from '"+shortcut.start+"' to block '"+name+"' for parsers ", shortcut.parsers); this.shortcuts.push({ + block: name, parsers: shortcut.parsers, start: shortcut.start, end: shortcut.end, @@ -298,6 +302,40 @@ TemplateEngine.prototype.addBlock = function(name, block) { }, this); }; +// Add multiple blocks +TemplateEngine.prototype.addBlocks = function(blocks) { + _.each(blocks, function(block, name) { + this.addBlock(name, block); + }, this); +}; + +// Apply a block to some content +// This method result depends on the type of block (async or sync) +TemplateEngine.prototype.applyBlock = function(name, blk) { + var func, block, func, r; + + block = this.blocks[name]; + if (!block) throw new Error('Block not found "'+name+'"'); + if (_.isString(blk)) { + blk = { + body: blk + }; + } + + blk = _.defaults(blk, { + args: [], + kwargs: {}, + blocks: [] + }); + + // Bind and call block processor + func = this.bindContext(block.process); + r = func.call(context, blk); + + if (Q.isPromise(r)) return r.then(normBlockResult); + else return normBlockResult(r); +}; + // Apply a shortcut to a string TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) { if (!_.contains(shortcut.parsers, parser)) return content; @@ -316,6 +354,9 @@ TemplateEngine.prototype.renderString = function(content, context, options) { // Variables from book.json book: this.book.options.variables, + // Complete book.json + config: this.book.options, + // infos about gitbook gitbook: { version: pkg.version, @@ -380,7 +421,7 @@ TemplateEngine.prototype.postProcess = function(content) { return Q(content) .then(that.replaceBlocks) .then(function(_content) { - return batch.execEach(that.blocks, { + return batch.execEach(that.blockBodies, { max: 20, fn: function(blk, blkId) { return Q() @@ -389,7 +430,7 @@ TemplateEngine.prototype.postProcess = function(content) { return blk.post(); }) .then(function() { - delete that.blocks[blkId]; + delete that.blockBodies[blkId]; }); } }) |