function renderFile(file, options, fn){
// Express used to set options.locals for us, but now we do it ourselves
// (EJS does some __proto__ magic to expose these funcs/values in the template)
if (!options.locals) {
options.locals = {};
}
if (!options.locals.blocks) {
// one set of blocks no matter how often we recurse
var blocks = {};
options.locals.blocks = blocks;
options.locals.block = block.bind(blocks);
}
// override locals for layout/partial bound to current options
options.locals.layout = layout.bind(options);
options.locals.partial = partial.bind(options);
ejs.renderFile(file, options, function(err, html) {
if (err) {
return fn(err,html);
}
var layout = options.locals._layoutFile;
// for backward-compatibility, allow options to
// set a default layout file for the view or the app
// (NB:- not called `layout` any more so it doesn't
// conflict with the layout() function)
if (layout === undefined) {
layout = options._layoutFile;
}
if (layout) {
// use default extension
var engine = options.settings['view engine'] || 'ejs',
desiredExt = '.'+engine;
// apply default layout if only "true" was set
if (layout === true) {
layout = path.sep + 'layout' + desiredExt;
}
if (extname(layout) !== desiredExt) {
layout += desiredExt;
}
// clear to make sure we don't recurse forever (layouts can be nested)
delete options.locals._layoutFile;
delete options._layoutFile;
// make sure caching works inside ejs.renderFile/render
delete options.filename;
if (layout.length > 0) {
var views = options.settings.views;
var l = layout;
if (!Array.isArray(views)) {
views = [views];
}
for (var i = 0; i < views.length; i++) {
layout = join(views[i], l);
// use the first found layout
if (exists(layout)) {
break;
}
}
}
// now recurse and use the current result as `body` in the layout:
options.locals.body = html;
renderFile(layout, options, fn);
} else {
// no layout, just do the default:
fn(null, html);
}
});
}n/a
function block(name, html) {
// bound to the blocks object in renderFile
var blk = this[name];
if (!blk) {
// always create, so if we request a
// non-existent block we'll get a new one
blk = this[name] = new Block();
}
if (html) {
blk.append(html);
}
return blk;
}n/a
function compile(file, options, cb) {
// Express used to set options.locals for us, but now we do it ourselves
// (EJS does some __proto__ magic to expose these funcs/values in the template)
if (!options.locals) {
options.locals = {};
}
if (!options.locals.blocks) {
// one set of blocks no matter how often we recurse
var blocks = {};
options.locals.blocks = blocks;
options.locals.block = block.bind(blocks);
}
// override locals for layout/partial bound to current options
options.locals.layout = layout.bind(options);
options.locals.partial = partial.bind(options);
try {
var fn = ejs.compile(file, options)
cb(null, fn.toString());
} catch(ex) {
cb(ex);
}
}n/a
function layout(view){
this.locals._layoutFile = view;
}n/a
function partial(view, options){
var collection
, object
, locals
, name;
// parse options
if( options ){
// collection
if( options.collection ){
collection = options.collection;
delete options.collection;
} else if( 'length' in options ){
collection = options;
options = {};
}
// locals
if( options.locals ){
locals = options.locals;
delete options.locals;
}
// object
if( 'Object' != options.constructor.name ){
object = options;
options = {};
} else if( options.object !== undefined ){
object = options.object;
delete options.object;
}
} else {
options = {};
}
// merge locals into options
if( locals )
options.__proto__ = locals;
// merge app locals into options
for(var k in this)
options[k] = options[k] || this[k];
// extract object name from view
name = options.as || resolveObjectName(view);
// find view, relative to this filename
// (FIXME: filename is set by ejs engine, other engines may need more help)
var root = dirname(options.filename)
, file = lookup(root, view, options)
, key = file + ':string';
if( !file )
throw new Error('Could not find partial ' + view);
// read view
var source = options.cache
? cache[key] || (cache[key] = fs.readFileSync(file, 'utf8'))
: fs.readFileSync(file, 'utf8');
options.filename = file;
// re-bind partial for relative partial paths
options.partial = partial.bind(options);
// render partial
function render(){
if (object) {
if ('string' == typeof name) {
options[name] = object;
} else if (name === global) {
// wtf?
// merge(options, object);
}
}
// TODO Support other templates (but it's sync now...)
var html = ejs.render(source, options);
return html;
}
// Collection support
if (collection) {
var len = collection.length
, buf = ''
, keys
, prop
, val
, i;
if ('number' == typeof len || Array.isArray(collection)) {
options.collectionLength = len;
for (i = 0; i < len; ++i) {
val = collection[i];
options.firstInCollection = i === 0;
options.indexInCollection = i;
options.lastInCollection = i === len - 1;
object = val;
buf += render();
}
} else {
keys = Object.keys(collection);
len = keys.length;
options.collectionLength = len;
options.collectionKeys = keys;
for (i = 0; i < len; ++i) {
prop = keys[i];
val = collection[prop];
options.keyInCollection = prop;
options.firstInCollection = i === 0;
options.indexInCollection = i;
options.lastInCollection = i === len - 1;
object = val;
buf += render();
}
}
return buf;
} else {
return render();
}
}n/a