(function (scope) { 'use strict'; var TITLE_RE = /([\s\S]*?)<\/title>/i; var TEMPLATE_RE = /data-template="([a-z0-9_-]+)"/i; var TEMPLATES = './templates/'; var NAMES = [ 'base.njk', 'page.njk', 'book.njk', 'index.njk', 'version.njk', 'notice.njk' ]; scope.TEMPLATE_URLS = NAMES.map(function (name) { return TEMPLATES + name; }); scope.createRenderer = function (readText) { function Loader() {} Loader.prototype.async = true; Loader.prototype.getSource = function (name, callback) { readText(TEMPLATES + name).then( function (src) { callback(null, { src: src, path: name, noCache: false }); }, function (err) { callback(err); }); }; var env = new nunjucks.Environment(new Loader(), { autoescape: true }); var loaded = null; // Nunjucks resolves {% include %} asynchronously. A template that is not // compiled yet when the render reaches it is skipped, taking the rest of // the block it sits in with it, and the render still succeeds. function loadAll() { if (!loaded) { loaded = Promise.all(NAMES.map(function (name) { return new Promise(function (resolve) { env.getTemplate(name, true, null, false, function () { resolve(); }); }); })); } return loaded; } function render(name, context) { return loadAll().then(function () { return new Promise(function (resolve, reject) { env.render(name, context, function (err, out) { return err ? reject(err) : resolve(out); }); }); }); } return { render: render, compose: function (fragment) { var title = (TITLE_RE.exec(fragment) || ['', ''])[1].trim(); var content = fragment.replace(TITLE_RE, ''); var name = (TEMPLATE_RE.exec(content) || ['', 'page'])[1] + '.njk'; return render(name, { title: title, content: content }); }, notice: function (heading, text) { return render('notice.njk', { heading: heading, text: text }); } }; }; })(self);