rc-web@69: var handlebars = require('handlebars'), rc-web@69: async = require('async'), rc-web@69: cheerio = require('cheerio'), rc-web@69: marked = require('marked'), rc-web@69: path = require('path'), rc-web@69: fs = require('fs'), rc-web@69: _ = require('underscore'), rc-web@69: highlight = require('highlight.js'); rc-web@69: rc-web@69: var config = { rc-web@69: addIdsToHeadings: true, rc-web@69: gfm: true, //github flavored markdown rc-web@69: highlight: function(code, lang) { rc-web@69: return highlight.highlight('javascript', code).value; rc-web@69: } rc-web@69: }; rc-web@69: rc-web@69: var jqueryPath = './lib/jquery.js', rc-web@69: asyncTolkens = {}, rc-web@69: markdownCallbacks = []; rc-web@69: rc-web@69: var transforms = {}; rc-web@69: rc-web@69: function addTransform(name, callback) { rc-web@69: transforms[name] = callback; rc-web@69: } rc-web@69: rc-web@69: addTransform('html', function(buffer, complete, context, data) { rc-web@69: return complete(buffer.toString()); rc-web@69: }); rc-web@69: rc-web@69: addTransform('md', function(buffer, complete, context, data) { rc-web@69: var html = marked(buffer.toString(), { rc-web@69: gfm: config.gfm, rc-web@69: highlight: config.highlight rc-web@69: }); rc-web@69: async.series(_.map(markdownCallbacks, function(callback) { rc-web@69: return function(next) { rc-web@69: callback(html, function(modifiedHTML) { rc-web@69: html = modifiedHTML; rc-web@69: next(); rc-web@69: }); rc-web@69: } rc-web@69: }), function() { rc-web@69: complete(html); rc-web@69: }); rc-web@69: }); rc-web@69: rc-web@69: addTransform('hbs', function(buffer, complete, context, data) { rc-web@69: var output = handlebars.compile(buffer.toString(), { rc-web@69: data: true rc-web@69: })(context, { rc-web@69: data: data rc-web@69: }); rc-web@69: var filteredAsyncTolkens = {}; rc-web@69: _.each(asyncTolkens, function(tolkenData, tolken) { rc-web@69: if (data.file === tolkenData.file) { rc-web@69: filteredAsyncTolkens[tolken] = tolkenData; rc-web@69: } rc-web@69: }); rc-web@69: if (!_.keys(filteredAsyncTolkens).length) { rc-web@69: complete(output); rc-web@69: } else { rc-web@69: async.series(_.map(filteredAsyncTolkens, function(tolkenData, tolken) { rc-web@69: return function(next) { rc-web@69: var args = tolkenData.args; rc-web@69: args.push(function(callbackOutput) { rc-web@69: output = output.replace(tolken, callbackOutput.toString()); rc-web@69: next(); rc-web@69: }); rc-web@69: tolkenData.callback.apply(tolkenData.callback, args); rc-web@69: }; rc-web@69: }), function() { rc-web@69: complete(output); rc-web@69: }); rc-web@69: } rc-web@69: }); rc-web@69: rc-web@69: function $(html, callback) { rc-web@69: callback(cheerio.load(html)); rc-web@69: } rc-web@69: rc-web@69: function modifyDocumentFragment(html, callback, next) { rc-web@69: $(html, function($) { rc-web@69: callback($); rc-web@69: next($.html()); rc-web@69: }); rc-web@69: } rc-web@69: rc-web@69: function removeOuterBodyTag(html) { rc-web@69: return html.replace(/^\s*\/, '').replace(/\<\/body\>\s*$/, ''); rc-web@69: } rc-web@69: rc-web@69: handlebars.registerAsyncHelper = function(name, callback) { rc-web@69: handlebars.registerHelper(name, function() { rc-web@69: var tolken = String(new Date().getTime() + Math.random()); rc-web@69: var args = _.toArray(arguments), rc-web@69: data = args[args.length - 1].data; rc-web@69: asyncTolkens[tolken] = { rc-web@69: file: data.file, rc-web@69: args: args, rc-web@69: callback: callback rc-web@69: }; rc-web@69: return tolken; rc-web@69: }); rc-web@69: }; rc-web@69: rc-web@69: handlebars.registerHelper('require', function(file, options) { rc-web@69: var filePath = path.join(path.dirname(options.data.file), file); rc-web@69: require(filePath)(module.exports); rc-web@69: return ''; rc-web@69: }); rc-web@69: rc-web@69: handlebars.registerAsyncHelper('include', function(file, options, callback) { rc-web@69: var filePath = path.join(path.dirname(options.data.file), file); rc-web@69: transform(filePath, function(fileData) { rc-web@69: var selector = options.hash.select; rc-web@69: if (selector) { rc-web@69: $(fileData.toString(), function($) { rc-web@69: var generatedHTML = ''; rc-web@69: $(selector).each(function() { rc-web@69: // make more like a regular dom object rc-web@69: this.attributes = this[0].attribs; rc-web@69: this.id = this[0].attribs.id; rc-web@69: this.tagName = this[0].name; rc-web@69: this.innerHTML = this.html(); rc-web@69: generatedHTML += options.fn(this); rc-web@69: }); rc-web@69: callback(generatedHTML); rc-web@69: }); rc-web@69: } else { rc-web@69: callback(fileData.toString()); rc-web@69: } rc-web@69: }, options.hash, options.data); rc-web@69: }); rc-web@69: rc-web@69: function transform(source, callback, options) { rc-web@69: fs.readFile(source, function(err, data) { rc-web@69: if (err) { rc-web@69: console.trace(); rc-web@69: throw err; rc-web@69: } rc-web@69: var extensions = source.split('/').pop().split('.'); rc-web@69: var callbacks = _.filter(extensions, function(extension) { rc-web@69: return extension in transforms; rc-web@69: }).map(function(extension) { rc-web@69: return function(next) { rc-web@69: transforms[extension](data, next, options || {}, { rc-web@69: file: source rc-web@69: }); rc-web@69: }; rc-web@69: }); rc-web@69: async.series(callbacks, callback); rc-web@69: }); rc-web@69: } rc-web@69: rc-web@69: function onMarkdown(callback) { rc-web@69: markdownCallbacks.push(callback); rc-web@69: } rc-web@69: rc-web@69: onMarkdown(function(html, next) { rc-web@69: if (config.addIdsToHeadings) { rc-web@69: modifyDocumentFragment(html, function($) { rc-web@69: if (config.addIdsToHeadings) { rc-web@69: addIdsToHeadings($); rc-web@69: } rc-web@69: }, next); rc-web@69: } else { rc-web@69: next(html); rc-web@69: } rc-web@69: }); rc-web@69: rc-web@69: function addIdsToHeadings($) { rc-web@69: $('h1,h2,h3,h4,h5,h6').each(function() { rc-web@69: var text = $(this).html().split('<').shift(); rc-web@69: var id = text.replace(/(^\s+|\s+$)/g, '').replace(/[\s]+/g, '-').replace(/([a-z])([A-Z])/g, function() { rc-web@69: return arguments[1] + '-' + arguments[2].toLowerCase(); rc-web@69: }).toLowerCase(); rc-web@69: if (id.match(/^\s+$/) || !id) { rc-web@69: return; rc-web@69: } rc-web@69: $(this).attr('id', id); rc-web@69: }); rc-web@69: } rc-web@69: rc-web@69: module.exports = { rc-web@69: config: config, rc-web@69: transform: transform, rc-web@69: handlebars: handlebars, rc-web@69: $: $, rc-web@69: modifyDocumentFragment: modifyDocumentFragment, rc-web@69: onMarkdown: onMarkdown, rc-web@69: addTransform: addTransform rc-web@69: };