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