diff --git a/src/handlebars/Gruntfile.js b/src/handlebars/Gruntfile.js index 4daab57dca3..a75fd35b9c5 100644 --- a/src/handlebars/Gruntfile.js +++ b/src/handlebars/Gruntfile.js @@ -2,58 +2,200 @@ module.exports = function (grunt) { var path = require('path'), - HANDLEBARS_LIB = path.join(process.cwd(), '../../../', 'handlebars.js/lib'), - PREPENDED_COMMENT = '/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */\n', - REMOVE_EXPORTS_REGEX = /^\/\/ BEGIN\(BROWSER\)$\s*([\s\S]*)^\/\/ END\(BROWSER\)$/m, - - BASE_FILES = [ - 'handlebars/base.js', - 'handlebars/utils.js', - 'handlebars/runtime.js' - ], - - COMPILER_FILES = [ - 'handlebars/compiler/parser.js', - 'handlebars/compiler/base.js', - 'handlebars/compiler/ast.js', - 'handlebars/compiler/compiler.js' - ]; + HANDLEBARS_LIB = path.join(process.cwd(), '../../../', 'handlebars.js'), + HANDLEBARS_BUILD_PATH = 'dist/cjs', + PREPENDED_COMMENT = '/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */\n', + REMOVE_EXPORTS_REGEX = /^\/\/ BEGIN\(BROWSER\)$\s*([\s\S]*)^\/\/ END\(BROWSER\)$/m, + REMOVE_REQUIRES_REGEX = /^.+=\s*require\([^\)]+\)[^;,]*[;,]\s*/gm, + MODULE_TEMPLATE = '{{comment}}' + + '{{before}}' + + '(function ({{argumentNames}}) {\n' + + '{{module}}\n' + + '}({{arguments}}));\n' + + '{{after}}', + + ROOT_FILES = { + 'handlebars.js': { + arguments: { + exports: 'Handlebars', + Handlebars: 'Handlebars.Runtime', + AST: 'Handlebars.AST', + Parser: 'Handlebars.Parser', + parse: 'Handlebars.parse', + Compiler: 'Handlebars.Compiler', + compile: 'Handlebars.compile', + precompile: 'Handlebars.precompile', + JavaScriptCompiler: 'Handlebars.JavaScriptCompiler' + }, + 'after': 'Y.Handlebars = Handlebars[\'default\'];' + }, + 'handlebars.runtime.js': { + before: 'Handlebars.Runtime = {};', + arguments: { + exports: 'Handlebars.Runtime', + base: 'Handlebars', + SafeString: 'Handlebars.SafeString', + Exception: 'Handlebars.Exception', + Utils: 'Handlebars.Utils', + runtime: 'Handlebars.VM' + }, + after: 'Handlebars.Runtime = Handlebars.Runtime[\'default\'];' + } + }, + + BASE_FILES = { + 'handlebars/base.js': { + arguments: { + exports: 'Handlebars', + Utils: 'Handlebars.Utils', + Exception: 'Handlebars.Exception' + } + }, + 'handlebars/utils.js': { + before: 'Handlebars.Utils = {};', + arguments: { + exports: 'Handlebars.Utils', + SafeString: 'Handlebars.SafeString' + } + }, + 'handlebars/runtime.js': { + before: 'Handlebars.VM = {};', + arguments: { + exports: 'Handlebars.VM', + Utils: 'Handlebars.Utils', + Exception: 'Handlebars.Exception', + COMPILER_REVISION: 'Handlebars.COMPILER_REVISION', + REVISION_CHANGES: 'Handlebars.REVISION_CHANGES' + }, + after: 'Handlebars.template = Handlebars.VM.template;' + }, + 'handlebars/exception.js': { + before: 'Handlebars.Exception = {};', + arguments: { + exports: 'Handlebars.Exception' + }, + after: 'Handlebars.Exception = Handlebars.Exception[\'default\'];' + }, + 'handlebars/safe-string.js': { + before: 'Handlebars.SafeString = {};', + arguments: { + exports: 'Handlebars.SafeString' + }, + after: 'Handlebars.SafeString = Handlebars.SafeString[\'default\'];' + } + }, + + COMPILER_FILES = { + 'handlebars/compiler/helpers.js': { + before: 'Handlebars.Helpers = {};', + arguments: { + exports: 'Handlebars.Helpers', + Exception: 'Handlebars.Exception' + } + }, + 'handlebars/compiler/parser.js': { + before: 'Handlebars.Parser = {};', + arguments: { + exports: 'Handlebars.Parser', + yy: 'Handlebars.Helpers' + }, + after: 'Handlebars.Parser = Handlebars.Parser[\'default\'];' + }, + 'handlebars/compiler/base.js': { + arguments: { + exports: 'Handlebars', + parser: 'Handlebars.Parser', + AST: 'Handlebars.AST', + extend: 'Handlebars.Utils.extend', + Helpers: 'Handlebars.Helpers' + } + }, + 'handlebars/compiler/ast.js': { + before: 'Handlebars.AST = {};', + arguments: { + exports: 'Handlebars.AST', + Exception: 'Handlebars.Exception' + }, + after: 'Handlebars.AST = Handlebars.AST[\'default\'];' + }, + 'handlebars/compiler/compiler.js': { + arguments: { + exports: 'Handlebars', + isArray: 'Handlebars.Utils.isArray', + Exception: 'Handlebars.Exception' + } + }, + 'handlebars/compiler/javascript-compiler.js': { + before: 'Handlebars.JavaScriptCompiler = {};', + arguments: { + exports: 'Handlebars.JavaScriptCompiler', + COMPILER_REVISION: 'Handlebars.COMPILER_REVISION', + REVISION_CHANGES: 'Handlebars.REVISION_CHANGES', + log: 'Handlebars.log', + Exception: 'Handlebars.Exception' + }, + after: 'Handlebars.JavaScriptCompiler = Handlebars.JavaScriptCompiler[\'default\'];' + } + }; - function importFiles(files, prefix) { - prefix || (prefix = ''); + function removeExports(contents) { + var match = contents.match(REMOVE_EXPORTS_REGEX); + return match ? match[1] : contents; + } - files.forEach(function (file) { - var src = path.join(HANDLEBARS_LIB, file), - dest = path.join('js', prefix + path.basename(file)); + function removeRequires(contents) { + return contents.replace(REMOVE_REQUIRES_REGEX, ''); + } - if (!grunt.file.exists(src)) { - grunt.fail.fatal('Did you build Handlebars.js yet?'); + function render(template, templateValues) { + var stachs = template.match(/\{\{([^{}]+)\}\}/g) || []; + + stachs.forEach(function (stach) { + var key = stach.substring(2, stach.length - 2).trim(), + value = templateValues[key] !== undefined ? templateValues[key] : ''; + stach = stach.replace(/\{/g, '\\{').replace(/\}/g, '\\}'); + if (value instanceof Array) { + value = value.join(', '); } + template = template.replace(new RegExp(stach, 'g'), value); + }); - grunt.log.writeln('Importing: '.green + file.cyan + ' to ' + dest.cyan); - grunt.file.write(dest, processFile(src)); + return template; + } + + function processFile(file, options) { + var module = grunt.file.read(file), + templateValues = {}; + + templateValues.comment = PREPENDED_COMMENT; + templateValues.before = options.before && (options.before + '\n'); + templateValues.module = removeRequires(removeExports(module)).trim(); + templateValues.argumentNames = Object.keys(options.arguments || {}); + templateValues.arguments = []; + templateValues.argumentNames.forEach(function (argumentName) { + var argument = options.arguments[argumentName]; + templateValues.arguments.push(argument); }); + templateValues.after = options.after && (options.after + '\n'); + + return render(MODULE_TEMPLATE, templateValues); } - function processFile(file) { - var contents = grunt.file.read(file), - processed; + function importFiles(files, prefix) { + var fileName; + prefix || (prefix = ''); - // Processes the raw file by adding a the "this is a build file" - // comment, and remove the module exports from the contents of the file. - // This also trims the file and makes sure it ends with a newline char. - processed = [ - PREPENDED_COMMENT, - removeExports(contents).trim(), - '' - ].join('\n'); + for (fileName in files) { + var src = path.join(HANDLEBARS_LIB, HANDLEBARS_BUILD_PATH, fileName), + dest = path.join('js', prefix + path.basename(fileName)); - return processed; - } + if (!grunt.file.exists(src)) { + grunt.fail.fatal('Did you build Handlebars.js yet? Make sure to run grunt inside handlebars.js.'); + } - function removeExports(contents) { - var match = contents.match(REMOVE_EXPORTS_REGEX); - return match ? match[1] : contents; + grunt.log.writeln('Importing: '.green + fileName.cyan + ' to ' + dest.cyan); + grunt.file.write(dest, processFile(src, files[fileName])); + }; } // -- Tasks ---------------------------------------------------------------- @@ -80,6 +222,10 @@ module.exports = function (grunt) { }); }); + grunt.registerTask('import-root', 'Import Handlebars root source files.', function () { + importFiles(ROOT_FILES); + }); + grunt.registerTask('import-base', 'Import Handlebars base source files.', function () { importFiles(BASE_FILES, 'handlebars-'); }); @@ -89,6 +235,7 @@ module.exports = function (grunt) { }); grunt.registerTask('import', [ + 'import-root', 'import-base', 'import-compiler' ]); diff --git a/src/handlebars/build.json b/src/handlebars/build.json index c1fd4b22fd3..f1d38ace9be 100644 --- a/src/handlebars/build.json +++ b/src/handlebars/build.json @@ -11,8 +11,10 @@ "jsfiles": [ "yui-handlebars-copyright.js", "yui-handlebars-base-before.js", - "handlebars-base.js", + "handlebars-safe-string.js", "handlebars-utils.js", + "handlebars-exception.js", + "handlebars-base.js", "handlebars-runtime.js", "yui-handlebars-base-after.js" ] @@ -22,11 +24,15 @@ "jsfiles": [ "yui-handlebars-copyright.js", "yui-handlebars-compiler-before.js", + "handlebars-compiler-helpers.js", "handlebars-compiler-parser.js", - "handlebars-compiler-base.js", "handlebars-compiler-ast.js", "handlebars-compiler-compiler.js", - "yui-handlebars-compiler-after.js" + "handlebars-compiler-javascript-compiler.js", + "handlebars-compiler-base.js", + "yui-handlebars-compiler-after.js", + "handlebars.runtime.js", + "handlebars.js" ] } } diff --git a/src/handlebars/js/handlebars-base.js b/src/handlebars/js/handlebars-base.js index c58f20ef841..3b615d6cbf2 100644 --- a/src/handlebars/js/handlebars-base.js +++ b/src/handlebars/js/handlebars-base.js @@ -1,155 +1,231 @@ /* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ - -Handlebars.VERSION = "1.0.0"; -Handlebars.COMPILER_REVISION = 4; - -Handlebars.REVISION_CHANGES = { +(function (exports, Utils, Exception) { +"use strict"; +var VERSION = "2.0.0"; +exports.VERSION = VERSION;var COMPILER_REVISION = 6; +exports.COMPILER_REVISION = COMPILER_REVISION; +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.0.0' + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1' }; +exports.REVISION_CHANGES = REVISION_CHANGES; +var isArray = Utils.isArray, + isFunction = Utils.isFunction, + toString = Utils.toString, + objectType = '[object Object]'; -Handlebars.helpers = {}; -Handlebars.partials = {}; +function HandlebarsEnvironment(helpers, partials) { + this.helpers = helpers || {}; + this.partials = partials || {}; -var toString = Object.prototype.toString, - functionType = '[object Function]', - objectType = '[object Object]'; + registerDefaultHelpers(this); +} -Handlebars.registerHelper = function(name, fn, inverse) { - if (toString.call(name) === objectType) { - if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); } - Handlebars.Utils.extend(this.helpers, name); - } else { - if (inverse) { fn.not = inverse; } - this.helpers[name] = fn; - } -}; +exports.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, -Handlebars.registerPartial = function(name, str) { - if (toString.call(name) === objectType) { - Handlebars.Utils.extend(this.partials, name); - } else { - this.partials[name] = str; - } -}; + logger: logger, + log: log, -Handlebars.registerHelper('helperMissing', function(arg) { - if(arguments.length === 2) { - return undefined; - } else { - throw new Error("Missing helper: '" + arg + "'"); + 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 { + this.partials[name] = partial; + } + }, + unregisterPartial: function(name) { + delete this.partials[name]; } -}); +}; -Handlebars.registerHelper('blockHelperMissing', function(context, options) { - var inverse = options.inverse || function() {}, fn = options.fn; +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 + "'"); + } + }); - var type = toString.call(context); + instance.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse, + fn = options.fn; - if(type === functionType) { context = context.call(this); } + 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]; + } - if(context === true) { - return fn(this); - } else if(context === false || context == null) { - return inverse(this); - } else if(type === "[object Array]") { - if(context.length > 0) { - return Handlebars.helpers.each(context, options); + return instance.helpers.each(context, options); + } else { + return inverse(this); + } } else { - return inverse(this); + 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); } - } else { - return fn(context); - } -}); + }); -Handlebars.K = function() {}; + instance.registerHelper('each', function(context, options) { + if (!options) { + throw new Exception('Must pass iterator to #each'); + } -Handlebars.createFrame = Object.create || function(object) { - Handlebars.K.prototype = object; - var obj = new Handlebars.K(); - Handlebars.K.prototype = null; - return obj; -}; + var fn = options.fn, inverse = options.inverse; + var i = 0, ret = "", data; -Handlebars.logger = { - DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, + var contextPath; + if (options.data && options.ids) { + contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } - methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, + if (isFunction(context)) { context = context.call(this); } - // can be overridden in the host environment - log: function(level, obj) { - if (Handlebars.logger.level <= level) { - var method = Handlebars.logger.methodMap[level]; - if (typeof console !== 'undefined' && console[method]) { - console[method].call(console, obj); - } + if (options.data) { + data = createFrame(options.data); } - } -}; -Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; + if(context && typeof context === 'object') { + if (isArray(context)) { + for(var j = context.length; i 0) { throw new Handlebars.Exception("Invalid path: " + original); } - else if (part === "..") { depth++; } - else { this.isScoped = true; } + if (rawParams instanceof AST.SexprNode) { + this.sexpr = rawParams; + } else { + // Support old AST API + this.sexpr = new AST.SexprNode(rawParams, hash); } - else { dig.push(part); } - } - - this.original = original; - this.parts = dig; - this.string = dig.join('.'); - this.depth = depth; - // an ID is simple if it only has one part, and that part is not - // `..` or `this`. - this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; - - this.stringModeValue = this.string; -}; + // Support old AST API that stored this info in MustacheNode + this.id = this.sexpr.id; + this.params = this.sexpr.params; + this.hash = this.sexpr.hash; + this.eligibleHelper = this.sexpr.eligibleHelper; + this.isHelper = this.sexpr.isHelper; + }, + + SexprNode: function(rawParams, hash, locInfo) { + LocationInfo.call(this, locInfo); + + this.type = "sexpr"; + this.hash = hash; + + var id = this.id = rawParams[0]; + var params = this.params = rawParams.slice(1); + + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + this.isHelper = !!(params.length || hash); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + this.eligibleHelper = this.isHelper || id.isSimple; + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. + }, + + PartialNode: function(partialName, context, hash, strip, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "partial"; + this.partialName = partialName; + this.context = context; + this.hash = hash; + this.strip = strip; + + this.strip.inlineStandalone = true; + }, + + BlockNode: function(mustache, program, inverse, strip, locInfo) { + LocationInfo.call(this, locInfo); + + this.type = 'block'; + this.mustache = mustache; + this.program = program; + this.inverse = inverse; + this.strip = strip; + + if (inverse && !program) { + this.isInverse = true; + } + }, -Handlebars.AST.PartialNameNode = function(name) { - this.type = "PARTIAL_NAME"; - this.name = name.original; -}; + RawBlockNode: function(mustache, content, close, locInfo) { + LocationInfo.call(this, locInfo); -Handlebars.AST.DataNode = function(id) { - this.type = "DATA"; - this.id = id; -}; + if (mustache.sexpr.id.original !== close) { + throw new Exception(mustache.sexpr.id.original + " doesn't match " + close, this); + } -Handlebars.AST.StringNode = function(string) { - this.type = "STRING"; - this.original = - this.string = - this.stringModeValue = string; -}; + content = new AST.ContentNode(content, locInfo); + + this.type = 'block'; + this.mustache = mustache; + this.program = new AST.ProgramNode([content], {}, locInfo); + }, + + ContentNode: function(string, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "content"; + this.original = this.string = string; + }, + + HashNode: function(pairs, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "hash"; + this.pairs = pairs; + }, + + IdNode: function(parts, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "ID"; + + var original = "", + dig = [], + depth = 0, + depthString = ''; + + for(var i=0,l=parts.length; i 0) { + throw new Exception("Invalid path: " + original, this); + } else if (part === "..") { + depth++; + depthString += '../'; + } else { + this.isScoped = true; + } + } else { + dig.push(part); + } + } -Handlebars.AST.IntegerNode = function(integer) { - this.type = "INTEGER"; - this.original = - this.integer = integer; - this.stringModeValue = Number(integer); + this.original = original; + this.parts = dig; + this.string = dig.join('.'); + this.depth = depth; + this.idName = depthString + this.string; + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; + + this.stringModeValue = this.string; + }, + + PartialNameNode: function(name, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "PARTIAL_NAME"; + this.name = name.original; + }, + + DataNode: function(id, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "DATA"; + this.id = id; + this.stringModeValue = id.stringModeValue; + this.idName = '@' + id.stringModeValue; + }, + + StringNode: function(string, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "STRING"; + this.original = + this.string = + this.stringModeValue = string; + }, + + NumberNode: function(number, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "NUMBER"; + this.original = + this.number = number; + this.stringModeValue = Number(number); + }, + + BooleanNode: function(bool, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "BOOLEAN"; + this.bool = bool; + this.stringModeValue = bool === "true"; + }, + + CommentNode: function(comment, locInfo) { + LocationInfo.call(this, locInfo); + this.type = "comment"; + this.comment = comment; + + this.strip = { + inlineStandalone: true + }; + } }; -Handlebars.AST.BooleanNode = function(bool) { - this.type = "BOOLEAN"; - this.bool = bool; - this.stringModeValue = bool === "true"; -}; -Handlebars.AST.CommentNode = function(comment) { - this.type = "comment"; - this.comment = comment; -}; +// Must be exported as an object rather than the root of the module as the jison lexer +// most modify the object to operate properly. +exports["default"] = AST; +}(Handlebars.AST, Handlebars.Exception)); +Handlebars.AST = Handlebars.AST['default']; diff --git a/src/handlebars/js/handlebars-compiler-base.js b/src/handlebars/js/handlebars-compiler-base.js index d3943bcbb9d..945183ad870 100644 --- a/src/handlebars/js/handlebars-compiler-base.js +++ b/src/handlebars/js/handlebars-compiler-base.js @@ -1,12 +1,19 @@ /* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +(function (exports, parser, AST, extend, Helpers) { +"use strict"; +exports.parser = parser; -Handlebars.Parser = handlebars; - -Handlebars.parse = function(input) { +var yy = {}; +extend(yy, Helpers, AST); +function parse(input) { // Just return if an already-compile AST was passed in. - if(input.constructor === Handlebars.AST.ProgramNode) { return input; } + if (input.constructor === AST.ProgramNode) { return input; } + + parser.yy = yy; + + return parser.parse(input); +} - Handlebars.Parser.yy = Handlebars.AST; - return Handlebars.Parser.parse(input); -}; +exports.parse = parse; +}(Handlebars, Handlebars.Parser, Handlebars.AST, Handlebars.Utils.extend, Handlebars.Helpers)); diff --git a/src/handlebars/js/handlebars-compiler-compiler.js b/src/handlebars/js/handlebars-compiler-compiler.js index 3ce410e1c8d..67307e4b127 100644 --- a/src/handlebars/js/handlebars-compiler-compiler.js +++ b/src/handlebars/js/handlebars-compiler-compiler.js @@ -1,10 +1,11 @@ /* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +(function (exports, isArray, Exception) { +"use strict"; +var slice = [].slice; -/*jshint eqnull:true*/ -var Compiler = Handlebars.Compiler = function() {}; -var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {}; +function Compiler() {} -// the foundHelper register will disambiguate helper lookup from finding a +exports.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a // function in a context. This is necessary for mustache compatibility, which // requires that context functions in blocks are evaluated by blockHelperMissing, // and then proceed as if the resulting value was provided to blockHelperMissing. @@ -12,29 +13,6 @@ var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {}; Compiler.prototype = { compiler: Compiler, - disassemble: function() { - var opcodes = this.opcodes, opcode, out = [], params, param; - - for (var i=0, l=opcodes.length; i 0) { - this.source[1] = this.source[1] + ", " + locals.join(", "); - } - - // Generate minimizer alias mappings - if (!this.isChild) { - for (var alias in this.context.aliases) { - if (this.context.aliases.hasOwnProperty(alias)) { - this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; - } - } - } - - if (this.source[1]) { - this.source[1] = "var " + this.source[1].substring(2) + ";"; - } - - // Merge children - if (!this.isChild) { - this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; - } - - if (!this.environment.isSimple) { - this.source.push("return buffer;"); - } - - var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; - - for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } - return this.topStackName(); - }, - topStackName: function() { - return "stack" + this.stackSlot; - }, - flushInline: function() { - var inlineStack = this.inlineStack; - if (inlineStack.length) { - this.inlineStack = []; - for (var i = 0, len = inlineStack.length; i < len; i++) { - var entry = inlineStack[i]; - if (entry instanceof Literal) { - this.compileStack.push(entry); - } else { - this.pushStack(entry); - } - } - } - }, - isInline: function() { - return this.inlineStack.length; - }, - - popStack: function(wrapped) { - var inline = this.isInline(), - item = (inline ? this.inlineStack : this.compileStack).pop(); - - if (!wrapped && (item instanceof Literal)) { - return item.value; - } else { - if (!inline) { - this.stackSlot--; - } - return item; - } - }, - - topStack: function(wrapped) { - var stack = (this.isInline() ? this.inlineStack : this.compileStack), - item = stack[stack.length - 1]; - - if (!wrapped && (item instanceof Literal)) { - return item.value; - } else { - return item; - } - }, - - quotedString: function(str) { - return '"' + str - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 - .replace(/\u2029/g, '\\u2029') + '"'; - }, - - setupHelper: function(paramSize, name, missingParams) { - var params = []; - this.setupParams(paramSize, params, missingParams); - var foundHelper = this.nameLookup('helpers', name, 'helper'); - - return { - params: params, - name: foundHelper, - callParams: ["depth0"].concat(params).join(", "), - helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") - }; - }, - - // the params and contexts arguments are passed in arrays - // to fill in - setupParams: function(paramSize, params, useRegister) { - var options = [], contexts = [], types = [], param, inverse, program; - - options.push("hash:" + this.popStack()); - - inverse = this.popStack(); - program = this.popStack(); - - // Avoid setting fn and inverse if neither are set. This allows - // helpers to do a check for `if (options.fn)` - if (program || inverse) { - if (!program) { - this.context.aliases.self = "this"; - program = "self.noop"; - } - - if (!inverse) { - this.context.aliases.self = "this"; - inverse = "self.noop"; - } - - options.push("inverse:" + inverse); - options.push("fn:" + program); - } - - for(var i=0; i 0) { + varDeclarations += ", " + locals.join(", "); + } + + // Generate minimizer alias mappings + for (var alias in this.aliases) { + if (this.aliases.hasOwnProperty(alias)) { + varDeclarations += ', ' + alias + '=' + this.aliases[alias]; + } + } + + var params = ["depth0", "helpers", "partials", "data"]; + + if (this.useDepths) { + params.push('depths'); + } + + // Perform a second pass over the output to merge content when possible + var source = this.mergeSource(varDeclarations); + + if (asObject) { + params.push(source); + + return Function.apply(this, params); + } else { + return 'function(' + params.join(',') + ') {\n ' + source + '}'; + } + }, + mergeSource: function(varDeclarations) { + var source = '', + buffer, + appendOnly = !this.forceBuffer, + appendFirst; + + for (var i = 0, len = this.source.length; i < len; i++) { + var line = this.source[i]; + if (line.appendToBuffer) { + if (buffer) { + buffer = buffer + '\n + ' + line.content; + } else { + buffer = line.content; + } + } else { + if (buffer) { + if (!source) { + appendFirst = true; + source = buffer + ';\n '; + } else { + source += 'buffer += ' + buffer + ';\n '; + } + buffer = undefined; + } + source += line + '\n '; + + if (!this.environment.isSimple) { + appendOnly = false; + } + } + } + + if (appendOnly) { + if (buffer || !source) { + source += 'return ' + (buffer || '""') + ';\n'; + } + } else { + varDeclarations += ", buffer = " + (appendFirst ? '' : this.initializeBuffer()); + if (buffer) { + source += 'return buffer + ' + buffer + ';\n'; + } else { + source += 'return buffer;\n'; + } + } + + if (varDeclarations) { + source = 'var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n ') + source; + } + + return source; + }, + + // [blockValue] + // + // On stack, before: hash, inverse, program, value + // On stack, after: return value of blockHelperMissing + // + // The purpose of this opcode is to take a block of the form + // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and + // replace it on the stack with the result of properly + // invoking blockHelperMissing. + blockValue: function(name) { + this.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; + + var params = [this.contextName(0)]; + this.setupParams(name, 0, params); + + var blockName = this.popStack(); + params.splice(1, 0, blockName); + + this.push('blockHelperMissing.call(' + params.join(', ') + ')'); + }, + + // [ambiguousBlockValue] + // + // On stack, before: hash, inverse, program, value + // Compiler value, before: lastHelper=value of last found helper, if any + // On stack, after, if no lastHelper: same as [blockValue] + // On stack, after, if lastHelper: value + ambiguousBlockValue: function() { + this.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; + + // We're being a bit cheeky and reusing the options value from the prior exec + var params = [this.contextName(0)]; + this.setupParams('', 0, params, true); + + this.flushInline(); + + var current = this.topStack(); + params.splice(1, 0, current); + + this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }"); + }, + + // [appendContent] + // + // On stack, before: ... + // On stack, after: ... + // + // Appends the string value of `content` to the current buffer + appendContent: function(content) { + if (this.pendingContent) { + content = this.pendingContent + content; + } + + this.pendingContent = content; + }, + + // [append] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Coerces `value` to a String and appends it to the current buffer. + // + // If `value` is truthy, or 0, it is coerced into a string and appended + // Otherwise, the empty string is appended + append: function() { + // Force anything that is inlined onto the stack so we don't have duplication + // when we examine local + this.flushInline(); + var local = this.popStack(); + this.pushSource('if (' + local + ' != null) { ' + this.appendToBuffer(local) + ' }'); + if (this.environment.isSimple) { + this.pushSource("else { " + this.appendToBuffer("''") + " }"); + } + }, + + // [appendEscaped] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Escape `value` and append it to the buffer + appendEscaped: function() { + this.aliases.escapeExpression = 'this.escapeExpression'; + + this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")")); + }, + + // [getContext] + // + // On stack, before: ... + // On stack, after: ... + // Compiler value, after: lastContext=depth + // + // Set the value of the `lastContext` compiler value to the depth + getContext: function(depth) { + this.lastContext = depth; + }, + + // [pushContext] + // + // On stack, before: ... + // On stack, after: currentContext, ... + // + // Pushes the value of the current context onto the stack. + pushContext: function() { + this.pushStackLiteral(this.contextName(this.lastContext)); + }, + + // [lookupOnContext] + // + // On stack, before: ... + // On stack, after: currentContext[name], ... + // + // Looks up the value of `name` on the current context and pushes + // it onto the stack. + lookupOnContext: function(parts, falsy, scoped) { + /*jshint -W083 */ + var i = 0, + len = parts.length; + + if (!scoped && this.options.compat && !this.lastContext) { + // The depthed query is expected to handle the undefined logic for the root level that + // is implemented below, so we evaluate that directly in compat mode + this.push(this.depthedLookup(parts[i++])); + } else { + this.pushContext(); + } + + for (; i < len; i++) { + this.replaceStack(function(current) { + var lookup = this.nameLookup(current, parts[i], 'context'); + // We want to ensure that zero and false are handled properly if the context (falsy flag) + // needs to have the special handling for these values. + if (!falsy) { + return ' != null ? ' + lookup + ' : ' + current; + } else { + // Otherwise we can use generic falsy handling + return ' && ' + lookup; + } + }); + } + }, + + // [lookupData] + // + // On stack, before: ... + // On stack, after: data, ... + // + // Push the data lookup operator + lookupData: function(depth, parts) { + /*jshint -W083 */ + if (!depth) { + this.pushStackLiteral('data'); + } else { + this.pushStackLiteral('this.data(data, ' + depth + ')'); + } + + var len = parts.length; + for (var i = 0; i < len; i++) { + this.replaceStack(function(current) { + return ' && ' + this.nameLookup(current, parts[i], 'data'); + }); + } + }, + + // [resolvePossibleLambda] + // + // On stack, before: value, ... + // On stack, after: resolved value, ... + // + // If the `value` is a lambda, replace it on the stack by + // the return value of the lambda + resolvePossibleLambda: function() { + this.aliases.lambda = 'this.lambda'; + + this.push('lambda(' + this.popStack() + ', ' + this.contextName(0) + ')'); + }, + + // [pushStringParam] + // + // On stack, before: ... + // On stack, after: string, currentContext, ... + // + // This opcode is designed for use in string mode, which + // provides the string value of a parameter along with its + // depth rather than resolving it immediately. + pushStringParam: function(string, type) { + this.pushContext(); + this.pushString(type); + + // If it's a subexpression, the string result + // will be pushed after this opcode. + if (type !== 'sexpr') { + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } + } + }, + + emptyHash: function() { + this.pushStackLiteral('{}'); + + if (this.trackIds) { + this.push('{}'); // hashIds + } + if (this.stringParams) { + this.push('{}'); // hashContexts + this.push('{}'); // hashTypes + } + }, + pushHash: function() { + if (this.hash) { + this.hashes.push(this.hash); + } + this.hash = {values: [], types: [], contexts: [], ids: []}; + }, + popHash: function() { + var hash = this.hash; + this.hash = this.hashes.pop(); + + if (this.trackIds) { + this.push('{' + hash.ids.join(',') + '}'); + } + if (this.stringParams) { + this.push('{' + hash.contexts.join(',') + '}'); + this.push('{' + hash.types.join(',') + '}'); + } + + this.push('{\n ' + hash.values.join(',\n ') + '\n }'); + }, + + // [pushString] + // + // On stack, before: ... + // On stack, after: quotedString(string), ... + // + // Push a quoted version of `string` onto the stack + pushString: function(string) { + this.pushStackLiteral(this.quotedString(string)); + }, + + // [push] + // + // On stack, before: ... + // On stack, after: expr, ... + // + // Push an expression onto the stack + push: function(expr) { + this.inlineStack.push(expr); + return expr; + }, + + // [pushLiteral] + // + // On stack, before: ... + // On stack, after: value, ... + // + // Pushes a value onto the stack. This operation prevents + // the compiler from creating a temporary variable to hold + // it. + pushLiteral: function(value) { + this.pushStackLiteral(value); + }, + + // [pushProgram] + // + // On stack, before: ... + // On stack, after: program(guid), ... + // + // Push a program expression onto the stack. This takes + // a compile-time guid and converts it into a runtime-accessible + // expression. + pushProgram: function(guid) { + if (guid != null) { + this.pushStackLiteral(this.programExpression(guid)); + } else { + this.pushStackLiteral(null); + } + }, + + // [invokeHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // Pops off the helper's parameters, invokes the helper, + // and pushes the helper's return value onto the stack. + // + // If the helper is not found, `helperMissing` is called. + invokeHelper: function(paramSize, name, isSimple) { + this.aliases.helperMissing = 'helpers.helperMissing'; + + var nonHelper = this.popStack(); + var helper = this.setupHelper(paramSize, name); + + var lookup = (isSimple ? helper.name + ' || ' : '') + nonHelper + ' || helperMissing'; + this.push('((' + lookup + ').call(' + helper.callParams + '))'); + }, + + // [invokeKnownHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // This operation is used when the helper is known to exist, + // so a `helperMissing` fallback is not required. + invokeKnownHelper: function(paramSize, name) { + var helper = this.setupHelper(paramSize, name); + this.push(helper.name + ".call(" + helper.callParams + ")"); + }, + + // [invokeAmbiguous] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of disambiguation + // + // This operation is used when an expression like `{{foo}}` + // is provided, but we don't know at compile-time whether it + // is a helper or a path. + // + // This operation emits more code than the other options, + // and can be avoided by passing the `knownHelpers` and + // `knownHelpersOnly` flags at compile-time. + invokeAmbiguous: function(name, helperCall) { + this.aliases.functionType = '"function"'; + this.aliases.helperMissing = 'helpers.helperMissing'; + this.useRegister('helper'); + + var nonHelper = this.popStack(); + + this.emptyHash(); + var helper = this.setupHelper(0, name, helperCall); + + var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + + this.push( + '((helper = (helper = ' + helperName + ' || ' + nonHelper + ') != null ? helper : helperMissing' + + (helper.paramsInit ? '),(' + helper.paramsInit : '') + '),' + + '(typeof helper === functionType ? helper.call(' + helper.callParams + ') : helper))'); + }, + + // [invokePartial] + // + // On stack, before: context, ... + // On stack after: result of partial invocation + // + // This operation pops off a context, invokes a partial with that context, + // and pushes the result of the invocation back. + invokePartial: function(name, indent) { + var params = [this.nameLookup('partials', name, 'partial'), "'" + indent + "'", "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"]; + + if (this.options.data) { + params.push("data"); + } else if (this.options.compat) { + params.push('undefined'); + } + if (this.options.compat) { + params.push('depths'); + } + + this.push("this.invokePartial(" + params.join(", ") + ")"); + }, + + // [assignToHash] + // + // On stack, before: value, ..., hash, ... + // On stack, after: ..., hash, ... + // + // Pops a value off the stack and assigns it to the current hash + assignToHash: function(key) { + var value = this.popStack(), + context, + type, + id; + + if (this.trackIds) { + id = this.popStack(); + } + if (this.stringParams) { + type = this.popStack(); + context = this.popStack(); + } + + var hash = this.hash; + if (context) { + hash.contexts.push("'" + key + "': " + context); + } + if (type) { + hash.types.push("'" + key + "': " + type); + } + if (id) { + hash.ids.push("'" + key + "': " + id); + } + hash.values.push("'" + key + "': (" + value + ")"); + }, + + pushId: function(type, name) { + if (type === 'ID' || type === 'DATA') { + this.pushString(name); + } else if (type === 'sexpr') { + this.pushStackLiteral('true'); + } else { + this.pushStackLiteral('null'); + } + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function(environment, options) { + var children = environment.children, child, compiler; + + for(var i=0, l=children.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } + return this.topStackName(); + }, + topStackName: function() { + return "stack" + this.stackSlot; + }, + flushInline: function() { + var inlineStack = this.inlineStack; + if (inlineStack.length) { + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + this.pushStack(entry); + } + } + } + }, + isInline: function() { + return this.inlineStack.length; + }, + + popStack: function(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + if (!inline) { + /* istanbul ignore next */ + if (!this.stackSlot) { + throw new Exception('Invalid stack pop'); + } + this.stackSlot--; + } + return item; + } + }, + + topStack: function() { + var stack = (this.isInline() ? this.inlineStack : this.compileStack), + item = stack[stack.length - 1]; + + if (item instanceof Literal) { + return item.value; + } else { + return item; + } + }, + + contextName: function(context) { + if (this.useDepths && context) { + return 'depths[' + context + ']'; + } else { + return 'depth' + context; + } + }, + + quotedString: function(str) { + return '"' + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + objectLiteral: function(obj) { + var pairs = []; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + pairs.push(this.quotedString(key) + ':' + obj[key]); + } + } + + return '{' + pairs.join(',') + '}'; + }, + + setupHelper: function(paramSize, name, blockHelper) { + var params = [], + paramsInit = this.setupParams(name, paramSize, params, blockHelper); + var foundHelper = this.nameLookup('helpers', name, 'helper'); + + return { + params: params, + paramsInit: paramsInit, + name: foundHelper, + callParams: [this.contextName(0)].concat(params).join(", ") + }; + }, + + setupOptions: function(helper, paramSize, params) { + var options = {}, contexts = [], types = [], ids = [], param, inverse, program; + + options.name = this.quotedString(helper); + options.hash = this.popStack(); + + if (this.trackIds) { + options.hashIds = this.popStack(); + } + if (this.stringParams) { + options.hashTypes = this.popStack(); + options.hashContexts = this.popStack(); + } + + inverse = this.popStack(); + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + if (!program) { + program = 'this.noop'; + } + + if (!inverse) { + inverse = 'this.noop'; + } + + options.fn = program; + options.inverse = inverse; + } + + // The parameters go on to the stack in order (making sure that they are evaluated in order) + // so we need to pop them off the stack in reverse order + var i = paramSize; + while (i--) { + param = this.popStack(); + params[i] = param; + + if (this.trackIds) { + ids[i] = this.popStack(); + } + if (this.stringParams) { + types[i] = this.popStack(); + contexts[i] = this.popStack(); + } + } + + if (this.trackIds) { + options.ids = "[" + ids.join(",") + "]"; + } + if (this.stringParams) { + options.types = "[" + types.join(",") + "]"; + options.contexts = "[" + contexts.join(",") + "]"; + } + + if (this.options.data) { + options.data = "data"; + } + + return options; + }, + + // the params and contexts arguments are passed in arrays + // to fill in + setupParams: function(helperName, paramSize, params, useRegister) { + var options = this.objectLiteral(this.setupOptions(helperName, paramSize, params)); + + if (useRegister) { + this.useRegister('options'); + params.push('options'); + return 'options=' + options; + } else { + params.push(options); + return ''; + } + } +}; + +var reservedWords = ( + "break else new var" + + " case finally return void" + + " catch for switch while" + + " continue function this with" + + " default if throw" + + " delete in try" + + " do instanceof typeof" + + " abstract enum int short" + + " boolean export interface static" + + " byte extends long super" + + " char final native synchronized" + + " class float package throws" + + " const goto private transient" + + " debugger implements protected volatile" + + " double import public let yield" +).split(" "); + +var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + +for(var i=0, l=reservedWords.length; i)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; -lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"INITIAL":{"rules":[0,1,2,31],"inclusive":true}}; +lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; +lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,38],"inclusive":true}}; return lexer;})() parser.lexer = lexer; function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; return new Parser; -})(); +})();exports["default"] = handlebars; +/* jshint ignore:end */ +}(Handlebars.Parser, Handlebars.Helpers)); +Handlebars.Parser = Handlebars.Parser['default']; diff --git a/src/handlebars/js/handlebars-exception.js b/src/handlebars/js/handlebars-exception.js new file mode 100644 index 00000000000..72e25698870 --- /dev/null +++ b/src/handlebars/js/handlebars-exception.js @@ -0,0 +1,33 @@ +/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +Handlebars.Exception = {}; +(function (exports) { +"use strict"; + +var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + +function Exception(message, node) { + var line; + if (node && node.firstLine) { + line = node.firstLine; + + message += ' - ' + line + ':' + node.firstColumn; + } + + var tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + if (line) { + this.lineNumber = line; + this.column = node.firstColumn; + } +} + +Exception.prototype = new Error(); + +exports["default"] = Exception; +}(Handlebars.Exception)); +Handlebars.Exception = Handlebars.Exception['default']; diff --git a/src/handlebars/js/handlebars-runtime.js b/src/handlebars/js/handlebars-runtime.js index bddfbe3892e..6ee4429e03a 100644 --- a/src/handlebars/js/handlebars-runtime.js +++ b/src/handlebars/js/handlebars-runtime.js @@ -1,98 +1,192 @@ /* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +Handlebars.VM = {}; +(function (exports, Utils, Exception, COMPILER_REVISION, REVISION_CHANGES) { +"use strict"; +function checkRevision(compilerInfo) { + var compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = COMPILER_REVISION; -Handlebars.VM = { - template: function(templateSpec) { - // Just add water - var container = { - escapeExpression: Handlebars.Utils.escapeExpression, - invokePartial: Handlebars.VM.invokePartial, - programs: [], - program: function(i, fn, data) { - var programWrapper = this.programs[i]; - if(data) { - programWrapper = Handlebars.VM.program(i, fn, data); - } else if (!programWrapper) { - programWrapper = this.programs[i] = Handlebars.VM.program(i, fn); - } - return programWrapper; - }, - merge: function(param, common) { - var ret = param || common; - - if (param && common) { - ret = {}; - Handlebars.Utils.extend(ret, common); - Handlebars.Utils.extend(ret, param); + if (compilerRevision !== currentRevision) { + if (compilerRevision < currentRevision) { + var 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]+")."); + } + } +} + +exports.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial + +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); + } + + // 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); + + var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data, depths) { + if (hash) { + context = Utils.extend({}, context, hash); + } + + var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data, depths); + + if (result == null && env.compile) { + var options = { helpers: helpers, partials: partials, data: data, depths: depths }; + partials[name] = env.compile(partial, { data: data !== undefined, compat: templateSpec.compat }, env); + result = partials[name](context, options); + } + if (result != null) { + if (indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = indent + lines[i]; } - return ret; - }, - programWithDepth: Handlebars.VM.programWithDepth, - noop: Handlebars.VM.noop, - compilerInfo: null - }; - - return function(context, options) { - options = options || {}; - var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data); - - var compilerInfo = container.compilerInfo || [], - compilerRevision = compilerInfo[0] || 1, - currentRevision = Handlebars.COMPILER_REVISION; - - if (compilerRevision !== currentRevision) { - if (compilerRevision < currentRevision) { - var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision], - compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision]; - throw "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 "Template was precompiled with a newer version of Handlebars than the current runtime. "+ - "Please update your runtime to a newer version ("+compilerInfo[1]+")."; + result = lines.join('\n'); + } + return result; + } else { + throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); + } + }; + + // Just add water + var container = { + lookup: function(depths, name) { + var len = depths.length; + for (var 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; + }, - return result; - }; - }, - - programWithDepth: function(i, fn, data /*, $depth */) { - var args = Array.prototype.slice.call(arguments, 3); - - var program = function(context, options) { - options = options || {}; - - return fn.apply(this, [context, options.data || data].concat(args)); - }; - program.program = i; - program.depth = args.length; - return program; - }, - program: function(i, fn, data) { - var program = function(context, options) { - options = options || {}; - - return fn(context, options.data || data); - }; - program.program = i; - program.depth = 0; - return program; - }, - noop: function() { return ""; }, - invokePartial: function(partial, name, context, helpers, partials, data) { - var options = { helpers: helpers, partials: partials, data: data }; - - if(partial === undefined) { - throw new Handlebars.Exception("The partial " + name + " could not be found"); - } else if(partial instanceof Function) { - return partial(context, options); - } else if (!Handlebars.compile) { - throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function(i) { + return templateSpec[i]; + }, + + programs: [], + program: function(i, data, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths) { + programWrapper = program(this, i, fn, data, depths); + } else if (!programWrapper) { + programWrapper = this.programs[i] = program(this, i, fn); + } + return programWrapper; + }, + + data: function(data, depth) { + while (data && depth--) { + data = data._parent; + } + return data; + }, + merge: function(param, common) { + var ret = param || common; + + if (param && common && (param !== common)) { + ret = Utils.extend({}, common, param); + } + + return ret; + }, + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler + }; + + var ret = function(context, options) { + options = options || {}; + var data = options.data; + + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + var depths; + if (templateSpec.useDepths) { + depths = options.depths ? [context].concat(options.depths) : [context]; + } + + return templateSpec.main.call(container, context, container.helpers, container.partials, data, depths); + }; + 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); + } } else { - partials[name] = Handlebars.compile(partial, {data: data !== undefined}); - return partials[name](context, options); + container.helpers = options.helpers; + container.partials = options.partials; + } + }; + + ret._child = function(i, data, depths) { + if (templateSpec.useDepths && !depths) { + throw new Exception('must pass parent depths'); } + + return program(container, i, templateSpec[i], data, depths); + }; + return ret; +} + +exports.template = template;function program(container, i, fn, data, depths) { + var prog = function(context, options) { + options = options || {}; + + return fn.call(container, context, container.helpers, container.partials, options.data || data, depths && [context].concat(depths)); + }; + prog.program = i; + prog.depth = depths ? depths.length : 0; + return prog; +} + +exports.program = program;function invokePartial(partial, name, context, helpers, partials, data, depths) { + var options = { partial: true, helpers: helpers, partials: partials, data: data, depths: depths }; + + if(partial === undefined) { + throw new Exception("The partial " + name + " could not be found"); + } else if(partial instanceof Function) { + return partial(context, options); } -}; +} +exports.invokePartial = invokePartial;function noop() { return ""; } + +exports.noop = noop;function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? createFrame(data) : {}; + data.root = context; + } + return data; +} +}(Handlebars.VM, Handlebars.Utils, Handlebars.Exception, Handlebars.COMPILER_REVISION, Handlebars.REVISION_CHANGES)); Handlebars.template = Handlebars.VM.template; diff --git a/src/handlebars/js/handlebars-safe-string.js b/src/handlebars/js/handlebars-safe-string.js new file mode 100644 index 00000000000..ce60763b5ab --- /dev/null +++ b/src/handlebars/js/handlebars-safe-string.js @@ -0,0 +1,16 @@ +/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +Handlebars.SafeString = {}; +(function (exports) { +"use strict"; +// Build out our basic SafeString type +function SafeString(string) { + this.string = string; +} + +SafeString.prototype.toString = function() { + return "" + this.string; +}; + +exports["default"] = SafeString; +}(Handlebars.SafeString)); +Handlebars.SafeString = Handlebars.SafeString['default']; diff --git a/src/handlebars/js/handlebars-utils.js b/src/handlebars/js/handlebars-utils.js index 56aaa3265c0..f558ef2f3e3 100644 --- a/src/handlebars/js/handlebars-utils.js +++ b/src/handlebars/js/handlebars-utils.js @@ -1,25 +1,8 @@ /* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ - -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - -Handlebars.Exception = function(message) { - var tmp = Error.prototype.constructor.apply(this, arguments); - - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } -}; -Handlebars.Exception.prototype = new Error(); - -// Build out our basic SafeString type -Handlebars.SafeString = function(string) { - this.string = string; -}; -Handlebars.SafeString.prototype.toString = function() { - return this.string.toString(); -}; - +Handlebars.Utils = {}; +(function (exports, SafeString) { +"use strict"; +/*jshint -W004 */ var escape = { "&": "&", "<": "<", @@ -32,43 +15,76 @@ var escape = { var badChars = /[&<>"'`]/g; var possible = /[&<>"'`]/; -var escapeChar = function(chr) { - return escape[chr] || "&"; -}; +function escapeChar(chr) { + return escape[chr]; +} -Handlebars.Utils = { - extend: function(obj, value) { - for(var key in value) { - if(value.hasOwnProperty(key)) { - obj[key] = value[key]; +function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; } } - }, + } - escapeExpression: function(string) { - // don't escape SafeStrings, since they're already safe - if (string instanceof Handlebars.SafeString) { - return string.toString(); - } else if (string == null || string === false) { - return ""; - } + return obj; +} - // Force a string conversion as this will be done by the append regardless and - // the regex test will do this transparently behind the scenes, causing issues if - // an object's to string has escaped characters in it. - string = string.toString(); +exports.extend = extend;var toString = Object.prototype.toString; +exports.toString = toString; +// Sourced from lodash +// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt +var isFunction = function(value) { + return typeof value === 'function'; +}; +// fallback for older versions of Chrome and Safari +/* istanbul ignore next */ +if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; +} +var isFunction; +exports.isFunction = isFunction; +/* istanbul ignore next */ +var isArray = Array.isArray || function(value) { + return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; +}; +exports.isArray = isArray; - if(!possible.test(string)) { return string; } - return string.replace(badChars, escapeChar); - }, +function escapeExpression(string) { + // don't escape SafeStrings, since they're already safe + if (string instanceof SafeString) { + return string.toString(); + } else if (string == null) { + return ""; + } else if (!string) { + return string + ''; + } - isEmpty: function(value) { - if (!value && value !== 0) { - return true; - } else if(toString.call(value) === "[object Array]" && value.length === 0) { - return true; - } else { - return false; - } + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = "" + string; + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); +} + +exports.escapeExpression = escapeExpression;function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; } -}; +} + +exports.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; +} + +exports.appendContextPath = appendContextPath; +}(Handlebars.Utils, Handlebars.SafeString)); diff --git a/src/handlebars/js/handlebars.js b/src/handlebars/js/handlebars.js new file mode 100644 index 00000000000..c33498b3c2a --- /dev/null +++ b/src/handlebars/js/handlebars.js @@ -0,0 +1,33 @@ +/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +(function (exports, Handlebars, AST, Parser, parse, Compiler, compile, precompile, JavaScriptCompiler) { +"use strict"; +/*globals Handlebars: true */ +// Compiler imports +var _create = Handlebars.create; +var create = function() { + var hb = _create(); + + hb.compile = function(input, options) { + return compile(input, options, hb); + }; + hb.precompile = function (input, options) { + return precompile(input, options, hb); + }; + + hb.AST = AST; + hb.Compiler = Compiler; + hb.JavaScriptCompiler = JavaScriptCompiler; + hb.Parser = Parser; + hb.parse = parse; + + return hb; +}; + +Handlebars = create(); +Handlebars.create = create; + +Handlebars['default'] = Handlebars; + +exports["default"] = Handlebars; +}(Handlebars, Handlebars.Runtime, Handlebars.AST, Handlebars.Parser, Handlebars.parse, Handlebars.Compiler, Handlebars.compile, Handlebars.precompile, Handlebars.JavaScriptCompiler)); +Y.Handlebars = Handlebars['default']; diff --git a/src/handlebars/js/handlebars.runtime.js b/src/handlebars/js/handlebars.runtime.js new file mode 100644 index 00000000000..3a3ea6a7340 --- /dev/null +++ b/src/handlebars/js/handlebars.runtime.js @@ -0,0 +1,33 @@ +/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */ +Handlebars.Runtime = {}; +(function (exports, base, SafeString, Exception, Utils, runtime) { +"use strict"; +/*globals Handlebars: true */ +// Each of these augment the Handlebars object. No need to setup here. +// (This is done to easily share code between commonjs and browse envs) +// For compatibility and usage outside of module systems, make the Handlebars object a namespace +var create = function() { + var hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = SafeString; + hb.Exception = Exception; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function(spec) { + return runtime.template(spec, hb); + }; + + return hb; +}; + +var Handlebars = create(); +Handlebars.create = create; + +Handlebars['default'] = Handlebars; + +exports["default"] = Handlebars; +}(Handlebars.Runtime, Handlebars, Handlebars.SafeString, Handlebars.Exception, Handlebars.Utils, Handlebars.VM)); +Handlebars.Runtime = Handlebars.Runtime['default']; diff --git a/src/handlebars/js/yui-handlebars-compiler-after.js b/src/handlebars/js/yui-handlebars-compiler-after.js index 82dd8d438c9..f73f92a4d49 100644 --- a/src/handlebars/js/yui-handlebars-compiler-after.js +++ b/src/handlebars/js/yui-handlebars-compiler-after.js @@ -51,7 +51,7 @@ single step. @return {String} Rendered template string. */ Handlebars.render = function (string, context, options) { - return Handlebars.compile(string)(context, options); + return this.compile(string)(context, options); }; // The rest of this file is just API docs for methods defined in Handlebars diff --git a/src/handlebars/tests/unit/assets/handlebars-test.js b/src/handlebars/tests/unit/assets/handlebars-test.js index 28677ee1532..028bc2a8cce 100644 --- a/src/handlebars/tests/unit/assets/handlebars-test.js +++ b/src/handlebars/tests/unit/assets/handlebars-test.js @@ -54,8 +54,8 @@ suite.add(new Y.Test.Case({ ); }, - '`false` should be considered empty': function () { - Assert.areSame('', H.render('{{value}}', {value: false})); + '`false` should render as the string `false`': function () { + Assert.areSame('false', H.render('{{value}}', {value: false})); }, '`null` should be considered empty': function () {