| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- import * as Utils from './utils';
- import Exception from './exception';
- import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from './base';
- export function checkRevision(compilerInfo) {
- const compilerRevision = compilerInfo && compilerInfo[0] || 1,
- currentRevision = COMPILER_REVISION;
- if (compilerRevision !== currentRevision) {
- if (compilerRevision < currentRevision) {
- const runtimeVersions = REVISION_CHANGES[currentRevision],
- compilerVersions = REVISION_CHANGES[compilerRevision];
- throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' +
- 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
- } else {
- // Use the embedded version info since the runtime doesn't know about this revision yet
- throw new Exception('Template was precompiled with a newer version of Handlebars than the current runtime. ' +
- 'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
- }
- }
- }
- export function template(templateSpec, env) {
- /* istanbul ignore next */
- if (!env) {
- throw new Exception('No environment passed to template');
- }
- if (!templateSpec || !templateSpec.main) {
- throw new Exception('Unknown template object: ' + typeof templateSpec);
- }
- templateSpec.main.decorator = templateSpec.main_d;
- // Note: Using env.VM references rather than local var references throughout this section to allow
- // for external users to override these as psuedo-supported APIs.
- env.VM.checkRevision(templateSpec.compiler);
- function invokePartialWrapper(partial, context, options) {
- if (options.hash) {
- context = Utils.extend({}, context, options.hash);
- if (options.ids) {
- options.ids[0] = true;
- }
- }
- partial = env.VM.resolvePartial.call(this, partial, context, options);
- let result = env.VM.invokePartial.call(this, partial, context, options);
- if (result == null && env.compile) {
- options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
- result = options.partials[options.name](context, options);
- }
- if (result != null) {
- if (options.indent) {
- let lines = result.split('\n');
- for (let i = 0, l = lines.length; i < l; i++) {
- if (!lines[i] && i + 1 === l) {
- break;
- }
- lines[i] = options.indent + lines[i];
- }
- result = lines.join('\n');
- }
- return result;
- } else {
- throw new Exception('The partial ' + options.name + ' could not be compiled when running in runtime-only mode');
- }
- }
- // Just add water
- let container = {
- strict: function(obj, name) {
- if (!(name in obj)) {
- throw new Exception('"' + name + '" not defined in ' + obj);
- }
- return obj[name];
- },
- lookup: function(depths, name) {
- const len = depths.length;
- for (let i = 0; i < len; i++) {
- if (depths[i] && depths[i][name] != null) {
- return depths[i][name];
- }
- }
- },
- lambda: function(current, context) {
- return typeof current === 'function' ? current.call(context) : current;
- },
- escapeExpression: Utils.escapeExpression,
- invokePartial: invokePartialWrapper,
- fn: function(i) {
- let ret = templateSpec[i];
- ret.decorator = templateSpec[i + '_d'];
- return ret;
- },
- programs: [],
- program: function(i, data, declaredBlockParams, blockParams, depths) {
- let programWrapper = this.programs[i],
- fn = this.fn(i);
- if (data || depths || blockParams || declaredBlockParams) {
- programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths);
- } else if (!programWrapper) {
- programWrapper = this.programs[i] = wrapProgram(this, i, fn);
- }
- return programWrapper;
- },
- data: function(value, depth) {
- while (value && depth--) {
- value = value._parent;
- }
- return value;
- },
- merge: function(param, common) {
- let obj = param || common;
- if (param && common && (param !== common)) {
- obj = Utils.extend({}, common, param);
- }
- return obj;
- },
- // An empty object to use as replacement for null-contexts
- nullContext: Object.seal({}),
- noop: env.VM.noop,
- compilerInfo: templateSpec.compiler
- };
- function ret(context, options = {}) {
- let data = options.data;
- ret._setup(options);
- if (!options.partial && templateSpec.useData) {
- data = initData(context, data);
- }
- let depths,
- blockParams = templateSpec.useBlockParams ? [] : undefined;
- if (templateSpec.useDepths) {
- if (options.depths) {
- depths = context != options.depths[0] ? [context].concat(options.depths) : options.depths;
- } else {
- depths = [context];
- }
- }
- function main(context/*, options*/) {
- return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths);
- }
- main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams);
- return main(context, options);
- }
- ret.isTop = true;
- ret._setup = function(options) {
- if (!options.partial) {
- container.helpers = container.merge(options.helpers, env.helpers);
- if (templateSpec.usePartial) {
- container.partials = container.merge(options.partials, env.partials);
- }
- if (templateSpec.usePartial || templateSpec.useDecorators) {
- container.decorators = container.merge(options.decorators, env.decorators);
- }
- } else {
- container.helpers = options.helpers;
- container.partials = options.partials;
- container.decorators = options.decorators;
- }
- };
- ret._child = function(i, data, blockParams, depths) {
- if (templateSpec.useBlockParams && !blockParams) {
- throw new Exception('must pass block params');
- }
- if (templateSpec.useDepths && !depths) {
- throw new Exception('must pass parent depths');
- }
- return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths);
- };
- return ret;
- }
- export function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) {
- function prog(context, options = {}) {
- let currentDepths = depths;
- if (depths && context != depths[0] && !(context === container.nullContext && depths[0] === null)) {
- currentDepths = [context].concat(depths);
- }
- return fn(container,
- context,
- container.helpers, container.partials,
- options.data || data,
- blockParams && [options.blockParams].concat(blockParams),
- currentDepths);
- }
- prog = executeDecorators(fn, prog, container, depths, data, blockParams);
- prog.program = i;
- prog.depth = depths ? depths.length : 0;
- prog.blockParams = declaredBlockParams || 0;
- return prog;
- }
- export function resolvePartial(partial, context, options) {
- if (!partial) {
- if (options.name === '@partial-block') {
- partial = options.data['partial-block'];
- } else {
- partial = options.partials[options.name];
- }
- } else if (!partial.call && !options.name) {
- // This is a dynamic partial that returned a string
- options.name = partial;
- partial = options.partials[partial];
- }
- return partial;
- }
- export function invokePartial(partial, context, options) {
- // Use the current closure context to save the partial-block if this partial
- const currentPartialBlock = options.data && options.data['partial-block'];
- options.partial = true;
- if (options.ids) {
- options.data.contextPath = options.ids[0] || options.data.contextPath;
- }
- let partialBlock;
- if (options.fn && options.fn !== noop) {
- options.data = createFrame(options.data);
- // Wrapper function to get access to currentPartialBlock from the closure
- let fn = options.fn;
- partialBlock = options.data['partial-block'] = function partialBlockWrapper(context, options = {}) {
- // Restore the partial-block from the closure for the execution of the block
- // i.e. the part inside the block of the partial call.
- options.data = createFrame(options.data);
- options.data['partial-block'] = currentPartialBlock;
- return fn(context, options);
- };
- if (fn.partials) {
- options.partials = Utils.extend({}, options.partials, fn.partials);
- }
- }
- if (partial === undefined && partialBlock) {
- partial = partialBlock;
- }
- if (partial === undefined) {
- throw new Exception('The partial ' + options.name + ' could not be found');
- } else if (partial instanceof Function) {
- return partial(context, options);
- }
- }
- export function noop() { return ''; }
- function initData(context, data) {
- if (!data || !('root' in data)) {
- data = data ? createFrame(data) : {};
- data.root = context;
- }
- return data;
- }
- function executeDecorators(fn, prog, container, depths, data, blockParams) {
- if (fn.decorator) {
- let props = {};
- prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths);
- Utils.extend(prog, props);
- }
- return prog;
- }
|