UNPKG

handlebars

Version:

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

242 lines (201 loc) 6.7 kB
module Utils from "./utils"; import Exception from "./exception"; export var VERSION = "3.0.0"; export var COMPILER_REVISION = 6; export var REVISION_CHANGES = { 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 2: '== 1.0.0-rc.3', 3: '== 1.0.0-rc.4', 4: '== 1.x.x', 5: '== 2.0.0-alpha.x', 6: '>= 2.0.0-beta.1' }; var isArray = Utils.isArray, isFunction = Utils.isFunction, toString = Utils.toString, objectType = '[object Object]'; export function HandlebarsEnvironment(helpers, partials) { this.helpers = helpers || {}; this.partials = partials || {}; registerDefaultHelpers(this); } HandlebarsEnvironment.prototype = { constructor: HandlebarsEnvironment, logger: logger, log: log, registerHelper: function(name, fn) { if (toString.call(name) === objectType) { if (fn) { throw new Exception('Arg not supported with multiple helpers'); } Utils.extend(this.helpers, name); } else { this.helpers[name] = fn; } }, unregisterHelper: function(name) { delete this.helpers[name]; }, registerPartial: function(name, partial) { if (toString.call(name) === objectType) { Utils.extend(this.partials, name); } else { if (typeof partial === 'undefined') { throw new Exception('Attempting to register a partial as undefined'); } this.partials[name] = partial; } }, unregisterPartial: function(name) { delete this.partials[name]; } }; function registerDefaultHelpers(instance) { instance.registerHelper('helperMissing', function(/* [args, ]options */) { if(arguments.length === 1) { // A missing field in a {{foo}} constuct. return undefined; } else { // Someone is actually trying to call something, blow up. throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'"); } }); instance.registerHelper('blockHelperMissing', function(context, options) { var inverse = options.inverse, fn = options.fn; if(context === true) { return fn(this); } else if(context === false || context == null) { return inverse(this); } else if (isArray(context)) { if(context.length > 0) { if (options.ids) { options.ids = [options.name]; } return instance.helpers.each(context, options); } else { return inverse(this); } } else { if (options.data && options.ids) { var data = createFrame(options.data); data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name); options = {data: data}; } return fn(context, options); } }); instance.registerHelper('each', function(context, options) { if (!options) { throw new Exception('Must pass iterator to #each'); } var fn = options.fn, inverse = options.inverse; var i = 0, ret = "", data; var contextPath; if (options.data && options.ids) { contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; } if (isFunction(context)) { context = context.call(this); } if (options.data) { data = createFrame(options.data); } function execIteration(key, i, last) { if (data) { data.key = key; data.index = i; data.first = i === 0; data.last = !!last; if (contextPath) { data.contextPath = contextPath + key; } } ret = ret + fn(context[key], { data: data, blockParams: Utils.blockParams([context[key], key], [contextPath + key, null]) }); } if(context && typeof context === 'object') { if (isArray(context)) { for(var j = context.length; i<j; i++) { execIteration(i, i, i === context.length-1); } } else { var priorKey; for(var key in context) { if(context.hasOwnProperty(key)) { // We're running the iterations one step out of sync so we can detect // the last iteration without have to scan the object twice and create // an itermediate keys array. if (priorKey) { execIteration(priorKey, i-1); } priorKey = key; i++; } } if (priorKey) { execIteration(priorKey, i-1, true); } } } if(i === 0){ ret = inverse(this); } return ret; }); instance.registerHelper('if', function(conditional, options) { if (isFunction(conditional)) { conditional = conditional.call(this); } // Default behavior is to render the positive path if the value is truthy and not empty. // The `includeZero` option may be set to treat the condtional as purely not empty based on the // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. if ((!options.hash.includeZero && !conditional) || Utils.isEmpty(conditional)) { return options.inverse(this); } else { return options.fn(this); } }); instance.registerHelper('unless', function(conditional, options) { return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash}); }); instance.registerHelper('with', function(context, options) { if (isFunction(context)) { context = context.call(this); } var fn = options.fn; if (!Utils.isEmpty(context)) { if (options.data && options.ids) { var data = createFrame(options.data); data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]); options = {data:data}; } return fn(context, options); } else { return options.inverse(this); } }); instance.registerHelper('log', function(message, options) { var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; instance.log(level, message); }); instance.registerHelper('lookup', function(obj, field) { return obj && obj[field]; }); } export var logger = { methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' }, // State enum DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 1, // Can be overridden in the host environment log: function(level, message) { if (typeof console !== 'undefined' && logger.level <= level) { var method = logger.methodMap[level]; (console[method] || console.log).call(console, message); } } }; export var log = logger.log; export var createFrame = function(object) { var frame = Utils.extend({}, object); frame._parent = object; return frame; };