210 lines · 4.1 KB · JavaScript
RawDownload
 /*! * Express - View * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var path = require('path')  , utils = require('../utils')  , extname = path.extname  , dirname = path.dirname  , basename = path.basename  , fs = require('fs')  , stat = fs.statSync; /** * Expose `View`. */ exports = module.exports = View; /** * Require cache. */ var cache = {}; /** * Initialize a new `View` with the given `view` path and `options`. * * @param {String} view * @param {Object} options * @api private */ function View(view, options) {  options = options || {};  this.view = view;  this.root = options.root;  this.relative = false !== options.relative;  this.defaultEngine = options.defaultEngine;  this.parent = options.parentView;  this.basename = basename(view);  this.engine = this.resolveEngine();  this.extension = '.' + this.engine;  this.name = this.basename.replace(this.extension, '');  this.path = this.resolvePath();  this.dirname = dirname(this.path);  if (options.attempts) {    if (!~options.attempts.indexOf(this.path))      options.attempts.push(this.path);  }}; /** * Check if the view path exists. * * @return {Boolean} * @api public */ View.prototype.__defineGetter__('exists', function(){  try {    stat(this.path);    return true;  } catch (err) {    return false;  }}); /** * Resolve view engine. * * @return {String} * @api private */ View.prototype.resolveEngine = function(){  // Explicit  if (~this.basename.indexOf('.')) return extname(this.basename).substr(1);  // Inherit from parent  if (this.parent) return this.parent.engine;  // Default  return this.defaultEngine;}; /** * Resolve view path. * * @return {String} * @api private */ View.prototype.resolvePath = function(){  var path = this.view;  // Implicit engine  if (!~this.basename.indexOf('.')) path += this.extension;  // Absolute  if (utils.isAbsolute(path)) return path;  // Relative to parent  if (this.relative && this.parent) return this.parent.dirname + '/' + path;  // Relative to root  return this.root    ? this.root + '/' + path    : path;}; /** * Get view contents. This is a one-time hit, so we * can afford to be sync. * * @return {String} * @api public */ View.prototype.__defineGetter__('contents', function(){  return fs.readFileSync(this.path, 'utf8');}); /** * Get template engine api, cache exports to reduce * require() calls. * * @return {Object} * @api public */ View.prototype.__defineGetter__('templateEngine', function(){  var ext = this.extension;  return cache[ext] || (cache[ext] = require(this.engine));}); /** * Return root path alternative. * * @return {String} * @api public */ View.prototype.__defineGetter__('rootPath', function(){  this.relative = false;  return this.resolvePath();}); /** * Return index path alternative. * * @return {String} * @api public */ View.prototype.__defineGetter__('indexPath', function(){  return this.dirname    + '/' + this.basename.replace(this.extension, '')    + '/index' + this.extension;}); /** * Return ../<name>/index path alternative. * * @return {String} * @api public */ View.prototype.__defineGetter__('upIndexPath', function(){  return this.dirname + '/../' + this.name + '/index' + this.extension;}); /** * Return _ prefix path alternative * * @return {String} * @api public */ View.prototype.__defineGetter__('prefixPath', function(){  return this.dirname + '/_' + this.basename;}); /** * Register the given template engine `exports` * as `ext`. For example we may wish to map ".html" * files to jade: * *    app.register('.html', require('jade')); * * or * *    app.register('html', require('jade')); * * This is also useful for libraries that may not * match extensions correctly. For example my haml.js * library is installed from npm as "hamljs" so instead * of layout.hamljs, we can register the engine as ".haml": * *    app.register('.haml', require('haml-js')); * * @param {String} ext * @param {Object} obj * @api public */ exports.register = function(ext, exports) {  if ('.' != ext[0]) ext = '.' + ext;  cache[ext] = exports;};