dist/handlebars.js in handlebars-source-1.0.0.rc3 vs dist/handlebars.js in handlebars-source-1.0.0.rc4
- old
+ new
@@ -27,40 +27,52 @@
(function(Handlebars, undefined) {
;
// lib/handlebars/base.js
-Handlebars.VERSION = "1.0.0-rc.3";
-Handlebars.COMPILER_REVISION = 2;
+Handlebars.VERSION = "1.0.0-rc.4";
+Handlebars.COMPILER_REVISION = 3;
Handlebars.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'
+ 2: '== 1.0.0-rc.3',
+ 3: '>= 1.0.0-rc.4'
};
Handlebars.helpers = {};
Handlebars.partials = {};
+var toString = Object.prototype.toString,
+ functionType = '[object Function]',
+ objectType = '[object Object]';
+
Handlebars.registerHelper = function(name, fn, inverse) {
- if(inverse) { fn.not = inverse; }
- this.helpers[name] = fn;
+ 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;
+ }
};
Handlebars.registerPartial = function(name, str) {
- this.partials[name] = str;
+ if (toString.call(name) === objectType) {
+ Handlebars.Utils.extend(this.partials, name);
+ } else {
+ this.partials[name] = str;
+ }
};
Handlebars.registerHelper('helperMissing', function(arg) {
if(arguments.length === 2) {
return undefined;
} else {
throw new Error("Could not find property '" + arg + "'");
}
});
-var toString = Object.prototype.toString, functionType = "[object Function]";
-
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var type = toString.call(context);
@@ -154,11 +166,11 @@
Handlebars.registerHelper('unless', function(context, options) {
return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn});
});
Handlebars.registerHelper('with', function(context, options) {
- return options.fn(context);
+ if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
});
Handlebars.registerHelper('log', function(context, options) {
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
Handlebars.log(level, context);
@@ -550,91 +562,93 @@
this.begin(condition);
}});
lexer.options = {};
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
-var YYSTATE=YY_START
+var YYSTATE=YY_START;
switch($avoiding_name_collisions) {
-case 0:
+case 0: yy_.yytext = "\\"; return 14;
+break;
+case 1:
if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
if(yy_.yytext) return 14;
break;
-case 1: return 14;
+case 2: return 14;
break;
-case 2:
+case 3:
if(yy_.yytext.slice(-1) !== "\\") this.popState();
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
return 14;
break;
-case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
+case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
break;
-case 4: this.begin("par"); return 24;
+case 5: this.begin("par"); return 24;
break;
-case 5: return 16;
+case 6: return 16;
break;
-case 6: return 20;
+case 7: return 20;
break;
-case 7: return 19;
-break;
case 8: return 19;
break;
-case 9: return 23;
+case 9: return 19;
break;
case 10: return 23;
break;
-case 11: this.popState(); this.begin('com');
+case 11: return 23;
break;
-case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
+case 12: this.popState(); this.begin('com');
break;
-case 13: return 22;
+case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
break;
-case 14: return 36;
+case 14: return 22;
break;
-case 15: return 35;
+case 15: return 36;
break;
case 16: return 35;
break;
-case 17: return 39;
+case 17: return 35;
break;
-case 18: /*ignore whitespace*/
+case 18: return 39;
break;
-case 19: this.popState(); return 18;
+case 19: /*ignore whitespace*/
break;
case 20: this.popState(); return 18;
break;
-case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30;
+case 21: this.popState(); return 18;
break;
-case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30;
+case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30;
break;
-case 23: yy_.yytext = yy_.yytext.substr(1); return 28;
+case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30;
break;
-case 24: return 32;
+case 24: yy_.yytext = yy_.yytext.substr(1); return 28;
break;
case 25: return 32;
break;
-case 26: return 31;
+case 26: return 32;
break;
-case 27: return 35;
+case 27: return 31;
break;
-case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35;
+case 28: return 35;
break;
-case 29: return 'INVALID';
+case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35;
break;
-case 30: /*ignore whitespace*/
+case 30: return 'INVALID';
break;
-case 31: this.popState(); return 37;
+case 31: /*ignore whitespace*/
break;
-case 32: return 5;
+case 32: this.popState(); return 37;
break;
+case 33: return 5;
+break;
}
};
-lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/];
-lexer.conditions = {"mu":{"rules":[4,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,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"par":{"rules":[30,31],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
-return lexer;})()
+lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$:\-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$\-\/]+)/,/^(?:$)/];
+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,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}};
+return lexer;})();
parser.lexer = lexer;
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
return new Parser;
})();;
// lib/handlebars/compiler/base.js
@@ -813,18 +827,31 @@
var escapeChar = function(chr) {
return escape[chr] || "&";
};
Handlebars.Utils = {
+ extend: function(obj, value) {
+ for(var key in value) {
+ if(value.hasOwnProperty(key)) {
+ obj[key] = value[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 "";
}
+ // 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();
+
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
@@ -1021,10 +1048,14 @@
for(var i=0, l=pairs.length; i<l; i++) {
pair = pairs[i];
val = pair[1];
if (this.options.stringParams) {
+ if(val.depth) {
+ this.addDepth(val.depth);
+ }
+ this.opcode('getContext', val.depth || 0);
this.opcode('pushStringParam', val.stringModeValue, val.type);
} else {
this.accept(val);
}
@@ -1104,11 +1135,11 @@
var params = this.setupFullMustacheParams(mustache, program, inverse),
name = mustache.id.parts[0];
if (this.options.knownHelpers[name]) {
this.opcode('invokeKnownHelper', params.length, name);
- } else if (this.knownHelpersOnly) {
+ } else if (this.options.knownHelpersOnly) {
throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
} else {
this.opcode('invokeHelper', params.length, name);
}
},
@@ -1605,20 +1636,22 @@
emptyHash: function() {
this.pushStackLiteral('{}');
if (this.options.stringParams) {
this.register('hashTypes', '{}');
+ this.register('hashContexts', '{}');
}
},
pushHash: function() {
- this.hash = {values: [], types: []};
+ this.hash = {values: [], types: [], contexts: []};
},
popHash: function() {
var hash = this.hash;
this.hash = undefined;
if (this.options.stringParams) {
+ this.register('hashContexts', '{' + hash.contexts.join(',') + '}');
this.register('hashTypes', '{' + hash.types.join(',') + '}');
}
this.push('{\n ' + hash.values.join(',\n ') + '\n }');
},
@@ -1757,18 +1790,22 @@
//
// Pops a value and hash off the stack, assigns `hash[key] = value`
// and pushes the hash back onto the stack.
assignToHash: function(key) {
var value = this.popStack(),
+ context,
type;
if (this.options.stringParams) {
type = this.popStack();
- this.popStack();
+ context = this.popStack();
}
var hash = this.hash;
+ if (context) {
+ hash.contexts.push("'" + key + "': " + context);
+ }
if (type) {
hash.types.push("'" + key + "': " + type);
}
hash.values.push("'" + key + "': (" + value + ")");
},
@@ -1825,16 +1862,11 @@
if(depth === 1) { programParams.push("depth0"); }
else { programParams.push("depth" + (depth - 1)); }
}
- if(depths.length === 0) {
- return "self.program(" + programParams.join(", ") + ")";
- } else {
- programParams.shift();
- return "self.programWithDepth(" + programParams.join(", ") + ")";
- }
+ return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")";
},
register: function(name, val) {
this.useRegister(name);
this.source.push(name + " = " + val + ";");
@@ -1962,11 +1994,13 @@
quotedString: function(str) {
return '"' + str
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r') + '"';
+ .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);
@@ -2018,10 +2052,11 @@
}
if (this.options.stringParams) {
options.push("contexts:[" + contexts.join(",") + "]");
options.push("types:[" + types.join(",") + "]");
+ options.push("hashContexts:hashContexts");
options.push("hashTypes:hashTypes");
}
if(this.options.data) {
options.push("data:data");
@@ -2068,11 +2103,11 @@
}
return false;
};
Handlebars.precompile = function(input, options) {
- if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
+ if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
}
options = options || {};
if (!('data' in options)) {
@@ -2082,11 +2117,11 @@
var environment = new Compiler().compile(ast, options);
return new JavaScriptCompiler().compile(environment, options);
};
Handlebars.compile = function(input, options) {
- if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
+ if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
}
options = options || {};
if (!('data' in options)) {
@@ -2120,17 +2155,15 @@
invokePartial: Handlebars.VM.invokePartial,
programs: [],
program: function(i, fn, data) {
var programWrapper = this.programs[i];
if(data) {
- return Handlebars.VM.program(fn, data);
- } else if(programWrapper) {
- return programWrapper;
- } else {
- programWrapper = this.programs[i] = Handlebars.VM.program(fn);
- return programWrapper;
+ programWrapper = Handlebars.VM.program(i, fn, data);
+ } else if (!programWrapper) {
+ programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
}
+ return programWrapper;
},
programWithDepth: Handlebars.VM.programWithDepth,
noop: Handlebars.VM.noop,
compilerInfo: null
};
@@ -2158,24 +2191,30 @@
return result;
};
},
- programWithDepth: function(fn, data, $depth) {
- var args = Array.prototype.slice.call(arguments, 2);
+ programWithDepth: function(i, fn, data /*, $depth */) {
+ var args = Array.prototype.slice.call(arguments, 3);
- return function(context, options) {
+ 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(fn, data) {
- return function(context, options) {
+ 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 };