You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
4.1 KiB
210 lines
4.1 KiB
|
|
/*! |
|
* 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; |
|
};
|
|
|