var defaultCreateOut = require('./createOut'); var extend = require('raptor-util/extend'); function safeRender(renderFunc, finalData, finalOut, shouldEnd) { try { renderFunc(finalData, finalOut); if (shouldEnd) { finalOut.end(); } } catch(err) { var actualEnd = finalOut.end; finalOut.end = function() {}; setTimeout(function() { finalOut.end = actualEnd; finalOut.error(err); }, 0); } return finalOut; } module.exports = function(target, renderer) { var renderFunc = renderer && (renderer.renderer || renderer.render || renderer); var createOut = target.createOut || renderer.createOut || defaultCreateOut; return extend(target, { createOut: createOut, renderToString: function(data, callback) { var localData = data || {}; var render = renderFunc || this._; var globalData = localData.$global; var out = createOut(globalData); out.global.template = this; if (globalData) { localData.$global = undefined; } if (callback) { out.on('finish', function() { callback(null, out.toString(), out); }) .once('error', callback); return safeRender(render, localData, out, true); } else { out.sync(); render(localData, out); return out.toString(); } }, renderSync: function(data) { var localData = data || {}; var render = renderFunc || this._; var globalData = localData.$global; var out = createOut(globalData); out.sync(); out.global.template = this; if (globalData) { localData.$global = undefined; } render(localData, out); return out.$__getResult(); }, /** * Renders a template to either a stream (if the last * argument is a Stream instance) or * provides the output to a callback function (if the last * argument is a Function). * * Supported signatures: * * render(data) * render(data, out) * render(data, stream) * render(data, callback) * * @param {Object} data The view model data for the template * @param {AsyncStream/AsyncVDOMBuilder} out A Stream, an AsyncStream/AsyncVDOMBuilder instance, or a callback function * @return {AsyncStream/AsyncVDOMBuilder} Returns the AsyncStream/AsyncVDOMBuilder instance that the template is rendered to */ render: function(data, out) { var callback; var finalOut; var finalData; var globalData; var render = renderFunc || this._; var shouldBuffer = this.$__shouldBuffer; var shouldEnd = true; if (data) { finalData = data; if ((globalData = data.$global)) { finalData.$global = undefined; } } else { finalData = {}; } if (out && out.$__isOut) { finalOut = out; shouldEnd = false; extend(out.global, globalData); } else if (typeof out == 'function') { finalOut = createOut(globalData); callback = out; } else { finalOut = createOut( globalData, // global out, // writer(AsyncStream) or parentNode(AsyncVDOMBuilder) null, // state shouldBuffer // ignored by AsyncVDOMBuilder ); } if (callback) { finalOut .on('finish', function() { callback(null, finalOut.$__getResult()); }) .once('error', callback); } globalData = finalOut.global; globalData.template = globalData.template || this; return safeRender(render, finalData, finalOut, shouldEnd); } }); };