Show:

File: platform/classes/Q/Handlebars.js

/**
 * @module Q
 */
 
var Q = require('../Q');
var fs = require('fs');
var handlebars = require('handlebars');

var _loaders;
var _partials;
var _templates = {};

var _ext = Q.Config.get(['Q', 'extensions', 'handlebars'], '.handlebars');

function _loader(path) {
	return function(name) {
		if (name.slice(-_ext.length) !== _ext) {
			name += _ext;
		}
		if (fs.existsSync(path+Q.DS+name)) {
			return fs.readFileSync(path+Q.DS+name, 'utf8');
		}
	};
}

function _getLoaders() {
	if (_loaders === undefined) {
		_loaders = []; _partials = [];
		if (fs.existsSync(Q.VIEWS_DIR)) {
			_loaders.unshift(_loader(Q.VIEWS_DIR));
		}
		if (fs.existsSync(Q.VIEWS_DIR+Q.DS+'partials')) {
			_partials.unshift(_loader(Q.VIEWS_DIR+Q.DS+'partials'));
		}
		var plugins = Q.Config.get(['Q', 'plugins'], []);
		for (i=0; i<plugins.length; i++) {
			path = Q.pluginInfo[plugins[i]].VIEWS_DIR;
			if (fs.existsSync(path)) {
				_loaders.unshift(_loader(path));
			}
			if (fs.existsSync(path+Q.DS+'partials')) {
				_partials.unshift(_loader(path+Q.DS+'partials'));
			}
		}
		if (fs.existsSync(Q.app.VIEWS_DIR)) {
			_loaders.unshift(_loader(Q.app.VIEWS_DIR));
		}
		if (fs.existsSync(Q.app.VIEWS_DIR+Q.DS+'partials')) {
			_partials.unshift(_loader(Q.app.VIEWS_DIR+Q.DS+'partials'));
		}
	}
	return _loaders;
}

handlebars.registerHelper('call', function(path) {
	if (!path) {
		return "{{call missing method name}}";
	}
	var args = Array.prototype.slice.call(
		arguments, 1, arguments.length-1
	);
	var parts = path.split('.');
	var subparts = parts.slice(0, -1);
	var f = Q.getObject(parts, this);
	if (typeof f === 'function') {
		return f.apply(Q.getObject(subparts, this), args);
	}
	if (parts[0] === 'Q') {
		parts.shift();
		subpath.shift();
		f = Q.getObject(parts, Q);
		if (typeof f === 'function') {
			return f.apply(Q.getObject(subparts, Q), args);
		}
	}
	return "{{call \""+path+"\" not found}}";
});

handlebars.registerHelper('toUrl', function(url) {
	if (!url) {
		return "{{url missing}}";
	}
	return Q.url(url);
});

handlebars.registerHelper('toCapitalized', function(text) {
	text = text || '';
	return text.charAt(0).toUpperCase() + text.slice(1);
});

handlebars.registerHelper('interpolate', function(expression, fields) {
	return expression.interpolate(fields);
});

handlebars.registerHelper('option', function(value, html, selectedValue) {
	var attr = value == selectedValue ? ' selected="selected"' : '';
	return new Handlebars.SafeString(
		'<option value="'+value.encodeHTML()+'"'+attr+'>'+html+"</option>"
	);
});

/**
 * Creates a Q.Handlebars object
 * @class Handlebars
 * @namespace Q
 */
module.exports = {

	/**
	 * Load and return the content of a handlebars template (synchronously)
	 * @method template
	 * @param {string} path The template name
	 * @return {string|null} The content of the template, or null if it wasn't found
	 */
	template: function (path) {
		if (_templates[path]) {
			return _templates[path];
		}
		var i, tpl = null, loaders = _getLoaders();
		for (i=0; i<loaders.length; i++) {
			if ((tpl = loaders[i](path))) break;
		}
		return tpl ? (_templates[path] = tpl.toString()) : null;
	},

	/**
	 * Render handlebars template
	 * @method render
	 * @param {string} template The template name
	 * @param {object} data Optional. The data to render
	 * @param {Array} partials Optional. The names of partials to load and use for rendering.
	 * @return {string|null}
	 */
	render: function(tPath, data, partials) {
		try {
			if (!tPath) return null;
			var i, tpl = this.template(tPath), part = {}, path;

			if (!tpl) return null;

			if (partials) {
				for (path in partials) {
					var path = partials[i];
					for (j=0; j<_partials.length; j++) {
						if (part[path] = _partials[j](path)) {
							break;
						}
					}
				}
			}
			return handlebars.compile(tpl)(data, {partials: part});
		} catch(e) {
			console.warn(e);
			throw e;
		}
	},

	/**
	 * Render handlebars literal source string
	 * @method render
	 * @param {string} content The source content
	 * @param {object} data Optional. The data to render
	 * @param {Array} partials Optional. The names of partials to load and use for rendering.
	 * @return {string|null}
	 */
	renderSource: function(content, data, partials) {
		var i, j, path, part = {};

		if (partials) {
			_getLoaders();
			for (i=0; i<partials.length; i++) {
				path = partials[i];
				for (j=0; j<_partials.length; j++) {
					if (part[path] = _partials[j](path)) {
						break;
					}
				}
			}
		}
		return handlebars.compile(content)(data, {partials: part});
	}
};