barber 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,34 @@
1
1
  (function() {
2
2
  var Ember = { assert: function() {} };
3
- // Version: v1.0.0-pre.4-12-ge3fb7f6
4
- // Last commit: e3fb7f6 (2013-01-22 10:37:17 -0800)
3
+ // Version: v1.0.0-rc.1
4
+ // Last commit: 8b061b4 (2013-02-15 12:10:22 -0800)
5
5
 
6
6
 
7
7
  (function() {
8
+ /*
9
+
10
+ Copyright (C) 2011 by Yehuda Katz
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in
20
+ all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ THE SOFTWARE.
29
+
30
+ */
31
+
8
32
  // lib/handlebars/base.js
9
33
 
10
34
  /*jshint eqnull:true*/
@@ -12,7 +36,13 @@ this.Handlebars = {};
12
36
 
13
37
  (function(Handlebars) {
14
38
 
15
- Handlebars.VERSION = "1.0.rc.2";
39
+ Handlebars.VERSION = "1.0.0-rc.3";
40
+ Handlebars.COMPILER_REVISION = 2;
41
+
42
+ Handlebars.REVISION_CHANGES = {
43
+ 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
44
+ 2: '>= 1.0.0-rc.3'
45
+ };
16
46
 
17
47
  Handlebars.helpers = {};
18
48
  Handlebars.partials = {};
@@ -625,9 +655,13 @@ return new Parser;
625
655
  // lib/handlebars/compiler/base.js
626
656
  Handlebars.Parser = handlebars;
627
657
 
628
- Handlebars.parse = function(string) {
658
+ Handlebars.parse = function(input) {
659
+
660
+ // Just return if an already-compile AST was passed in.
661
+ if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
662
+
629
663
  Handlebars.Parser.yy = Handlebars.AST;
630
- return Handlebars.Parser.parse(string);
664
+ return Handlebars.Parser.parse(input);
631
665
  };
632
666
 
633
667
  Handlebars.print = function(ast) {
@@ -709,8 +743,11 @@ Handlebars.print = function(ast) {
709
743
  for(var i=0,l=parts.length; i<l; i++) {
710
744
  var part = parts[i];
711
745
 
712
- if(part === "..") { depth++; }
713
- else if(part === "." || part === "this") { this.isScoped = true; }
746
+ if (part === ".." || part === "." || part === "this") {
747
+ if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
748
+ else if (part === "..") { depth++; }
749
+ else { this.isScoped = true; }
750
+ }
714
751
  else { dig.push(part); }
715
752
  }
716
753
 
@@ -860,6 +897,26 @@ Handlebars.JavaScriptCompiler = function() {};
860
897
 
861
898
  return out.join("\n");
862
899
  },
900
+ equals: function(other) {
901
+ var len = this.opcodes.length;
902
+ if (other.opcodes.length !== len) {
903
+ return false;
904
+ }
905
+
906
+ for (var i = 0; i < len; i++) {
907
+ var opcode = this.opcodes[i],
908
+ otherOpcode = other.opcodes[i];
909
+ if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
910
+ return false;
911
+ }
912
+ for (var j = 0; j < opcode.args.length; j++) {
913
+ if (opcode.args[j] !== otherOpcode.args[j]) {
914
+ return false;
915
+ }
916
+ }
917
+ }
918
+ return true;
919
+ },
863
920
 
864
921
  guid: 0,
865
922
 
@@ -951,7 +1008,7 @@ Handlebars.JavaScriptCompiler = function() {};
951
1008
  // evaluate it by executing `blockHelperMissing`
952
1009
  this.opcode('pushProgram', program);
953
1010
  this.opcode('pushProgram', inverse);
954
- this.opcode('pushHash');
1011
+ this.opcode('emptyHash');
955
1012
  this.opcode('blockValue');
956
1013
  } else {
957
1014
  this.ambiguousMustache(mustache, program, inverse);
@@ -960,7 +1017,7 @@ Handlebars.JavaScriptCompiler = function() {};
960
1017
  // evaluate it by executing `blockHelperMissing`
961
1018
  this.opcode('pushProgram', program);
962
1019
  this.opcode('pushProgram', inverse);
963
- this.opcode('pushHash');
1020
+ this.opcode('emptyHash');
964
1021
  this.opcode('ambiguousBlockValue');
965
1022
  }
966
1023
 
@@ -984,6 +1041,7 @@ Handlebars.JavaScriptCompiler = function() {};
984
1041
 
985
1042
  this.opcode('assignToHash', pair[0]);
986
1043
  }
1044
+ this.opcode('popHash');
987
1045
  },
988
1046
 
989
1047
  partial: function(partial) {
@@ -1024,17 +1082,19 @@ Handlebars.JavaScriptCompiler = function() {};
1024
1082
  },
1025
1083
 
1026
1084
  ambiguousMustache: function(mustache, program, inverse) {
1027
- var id = mustache.id, name = id.parts[0];
1085
+ var id = mustache.id,
1086
+ name = id.parts[0],
1087
+ isBlock = program != null || inverse != null;
1028
1088
 
1029
1089
  this.opcode('getContext', id.depth);
1030
1090
 
1031
1091
  this.opcode('pushProgram', program);
1032
1092
  this.opcode('pushProgram', inverse);
1033
1093
 
1034
- this.opcode('invokeAmbiguous', name);
1094
+ this.opcode('invokeAmbiguous', name, isBlock);
1035
1095
  },
1036
1096
 
1037
- simpleMustache: function(mustache, program, inverse) {
1097
+ simpleMustache: function(mustache) {
1038
1098
  var id = mustache.id;
1039
1099
 
1040
1100
  if (id.type === 'DATA') {
@@ -1165,7 +1225,7 @@ Handlebars.JavaScriptCompiler = function() {};
1165
1225
  if(mustache.hash) {
1166
1226
  this.hash(mustache.hash);
1167
1227
  } else {
1168
- this.opcode('pushHash');
1228
+ this.opcode('emptyHash');
1169
1229
  }
1170
1230
 
1171
1231
  return params;
@@ -1182,7 +1242,7 @@ Handlebars.JavaScriptCompiler = function() {};
1182
1242
  if(mustache.hash) {
1183
1243
  this.hash(mustache.hash);
1184
1244
  } else {
1185
- this.opcode('pushHash');
1245
+ this.opcode('emptyHash');
1186
1246
  }
1187
1247
 
1188
1248
  return params;
@@ -1196,7 +1256,7 @@ Handlebars.JavaScriptCompiler = function() {};
1196
1256
  JavaScriptCompiler.prototype = {
1197
1257
  // PUBLIC API: You can override these methods in a subclass to provide
1198
1258
  // alternative compiled forms for name lookup and buffering semantics
1199
- nameLookup: function(parent, name, type) {
1259
+ nameLookup: function(parent, name /* , type*/) {
1200
1260
  if (/^[0-9]+$/.test(name)) {
1201
1261
  return parent + "[" + name + "]";
1202
1262
  } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
@@ -1211,7 +1271,11 @@ Handlebars.JavaScriptCompiler = function() {};
1211
1271
  if (this.environment.isSimple) {
1212
1272
  return "return " + string + ";";
1213
1273
  } else {
1214
- return "buffer += " + string + ";";
1274
+ return {
1275
+ appendToBuffer: true,
1276
+ content: string,
1277
+ toString: function() { return "buffer += " + string + ";"; }
1278
+ };
1215
1279
  }
1216
1280
  },
1217
1281
 
@@ -1232,6 +1296,7 @@ Handlebars.JavaScriptCompiler = function() {};
1232
1296
  this.isChild = !!context;
1233
1297
  this.context = context || {
1234
1298
  programs: [],
1299
+ environments: [],
1235
1300
  aliases: { }
1236
1301
  };
1237
1302
 
@@ -1241,6 +1306,7 @@ Handlebars.JavaScriptCompiler = function() {};
1241
1306
  this.stackVars = [];
1242
1307
  this.registers = { list: [] };
1243
1308
  this.compileStack = [];
1309
+ this.inlineStack = [];
1244
1310
 
1245
1311
  this.compileChildren(environment, options);
1246
1312
 
@@ -1262,11 +1328,11 @@ Handlebars.JavaScriptCompiler = function() {};
1262
1328
  },
1263
1329
 
1264
1330
  nextOpcode: function() {
1265
- var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
1331
+ var opcodes = this.environment.opcodes;
1266
1332
  return opcodes[this.i + 1];
1267
1333
  },
1268
1334
 
1269
- eat: function(opcode) {
1335
+ eat: function() {
1270
1336
  this.i = this.i + 1;
1271
1337
  },
1272
1338
 
@@ -1304,7 +1370,6 @@ Handlebars.JavaScriptCompiler = function() {};
1304
1370
 
1305
1371
  // Generate minimizer alias mappings
1306
1372
  if (!this.isChild) {
1307
- var aliases = [];
1308
1373
  for (var alias in this.context.aliases) {
1309
1374
  this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1310
1375
  }
@@ -1329,16 +1394,48 @@ Handlebars.JavaScriptCompiler = function() {};
1329
1394
  params.push("depth" + this.environment.depths.list[i]);
1330
1395
  }
1331
1396
 
1397
+ // Perform a second pass over the output to merge content when possible
1398
+ var source = this.mergeSource();
1399
+
1400
+ if (!this.isChild) {
1401
+ var revision = Handlebars.COMPILER_REVISION,
1402
+ versions = Handlebars.REVISION_CHANGES[revision];
1403
+ source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
1404
+ }
1405
+
1332
1406
  if (asObject) {
1333
- params.push(this.source.join("\n "));
1407
+ params.push(source);
1334
1408
 
1335
1409
  return Function.apply(this, params);
1336
1410
  } else {
1337
- var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
1411
+ var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
1338
1412
  Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
1339
1413
  return functionSource;
1340
1414
  }
1341
1415
  },
1416
+ mergeSource: function() {
1417
+ // WARN: We are not handling the case where buffer is still populated as the source should
1418
+ // not have buffer append operations as their final action.
1419
+ var source = '',
1420
+ buffer;
1421
+ for (var i = 0, len = this.source.length; i < len; i++) {
1422
+ var line = this.source[i];
1423
+ if (line.appendToBuffer) {
1424
+ if (buffer) {
1425
+ buffer = buffer + '\n + ' + line.content;
1426
+ } else {
1427
+ buffer = line.content;
1428
+ }
1429
+ } else {
1430
+ if (buffer) {
1431
+ source += 'buffer += ' + buffer + ';\n ';
1432
+ buffer = undefined;
1433
+ }
1434
+ source += line + '\n ';
1435
+ }
1436
+ }
1437
+ return source;
1438
+ },
1342
1439
 
1343
1440
  // [blockValue]
1344
1441
  //
@@ -1376,6 +1473,9 @@ Handlebars.JavaScriptCompiler = function() {};
1376
1473
  var current = this.topStack();
1377
1474
  params.splice(1, 0, current);
1378
1475
 
1476
+ // Use the options value generated from the invocation
1477
+ params[params.length-1] = 'options';
1478
+
1379
1479
  this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1380
1480
  },
1381
1481
 
@@ -1399,6 +1499,9 @@ Handlebars.JavaScriptCompiler = function() {};
1399
1499
  // If `value` is truthy, or 0, it is coerced into a string and appended
1400
1500
  // Otherwise, the empty string is appended
1401
1501
  append: function() {
1502
+ // Force anything that is inlined onto the stack so we don't have duplication
1503
+ // when we examine local
1504
+ this.flushInline();
1402
1505
  var local = this.popStack();
1403
1506
  this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
1404
1507
  if (this.environment.isSimple) {
@@ -1413,15 +1516,9 @@ Handlebars.JavaScriptCompiler = function() {};
1413
1516
  //
1414
1517
  // Escape `value` and append it to the buffer
1415
1518
  appendEscaped: function() {
1416
- var opcode = this.nextOpcode(), extra = "";
1417
1519
  this.context.aliases.escapeExpression = 'this.escapeExpression';
1418
1520
 
1419
- if(opcode && opcode.opcode === 'appendContent') {
1420
- extra = " + " + this.quotedString(opcode.args[0]);
1421
- this.eat(opcode);
1422
- }
1423
-
1424
- this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
1521
+ this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
1425
1522
  },
1426
1523
 
1427
1524
  // [getContext]
@@ -1445,7 +1542,7 @@ Handlebars.JavaScriptCompiler = function() {};
1445
1542
  // Looks up the value of `name` on the current context and pushes
1446
1543
  // it onto the stack.
1447
1544
  lookupOnContext: function(name) {
1448
- this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
1545
+ this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
1449
1546
  },
1450
1547
 
1451
1548
  // [pushContext]
@@ -1493,7 +1590,7 @@ Handlebars.JavaScriptCompiler = function() {};
1493
1590
  //
1494
1591
  // Push the result of looking up `id` on the current data
1495
1592
  lookupData: function(id) {
1496
- this.pushStack(this.nameLookup('data', id, 'data'));
1593
+ this.push(this.nameLookup('data', id, 'data'));
1497
1594
  },
1498
1595
 
1499
1596
  // [pushStringParam]
@@ -1516,13 +1613,25 @@ Handlebars.JavaScriptCompiler = function() {};
1516
1613
  }
1517
1614
  },
1518
1615
 
1519
- pushHash: function() {
1520
- this.push('{}');
1616
+ emptyHash: function() {
1617
+ this.pushStackLiteral('{}');
1521
1618
 
1522
1619
  if (this.options.stringParams) {
1523
1620
  this.register('hashTypes', '{}');
1524
1621
  }
1525
1622
  },
1623
+ pushHash: function() {
1624
+ this.hash = {values: [], types: []};
1625
+ },
1626
+ popHash: function() {
1627
+ var hash = this.hash;
1628
+ this.hash = undefined;
1629
+
1630
+ if (this.options.stringParams) {
1631
+ this.register('hashTypes', '{' + hash.types.join(',') + '}');
1632
+ }
1633
+ this.push('{\n ' + hash.values.join(',\n ') + '\n }');
1634
+ },
1526
1635
 
1527
1636
  // [pushString]
1528
1637
  //
@@ -1541,7 +1650,8 @@ Handlebars.JavaScriptCompiler = function() {};
1541
1650
  //
1542
1651
  // Push an expression onto the stack
1543
1652
  push: function(expr) {
1544
- this.pushStack(expr);
1653
+ this.inlineStack.push(expr);
1654
+ return expr;
1545
1655
  },
1546
1656
 
1547
1657
  // [pushLiteral]
@@ -1584,12 +1694,14 @@ Handlebars.JavaScriptCompiler = function() {};
1584
1694
  invokeHelper: function(paramSize, name) {
1585
1695
  this.context.aliases.helperMissing = 'helpers.helperMissing';
1586
1696
 
1587
- var helper = this.lastHelper = this.setupHelper(paramSize, name);
1588
- this.register('foundHelper', helper.name);
1697
+ var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
1589
1698
 
1590
- this.pushStack("foundHelper ? foundHelper.call(" +
1591
- helper.callParams + ") " + ": helperMissing.call(" +
1592
- helper.helperMissingParams + ")");
1699
+ this.push(helper.name);
1700
+ this.replaceStack(function(name) {
1701
+ return name + ' ? ' + name + '.call(' +
1702
+ helper.callParams + ") " + ": helperMissing.call(" +
1703
+ helper.helperMissingParams + ")";
1704
+ });
1593
1705
  },
1594
1706
 
1595
1707
  // [invokeKnownHelper]
@@ -1601,7 +1713,7 @@ Handlebars.JavaScriptCompiler = function() {};
1601
1713
  // so a `helperMissing` fallback is not required.
1602
1714
  invokeKnownHelper: function(paramSize, name) {
1603
1715
  var helper = this.setupHelper(paramSize, name);
1604
- this.pushStack(helper.name + ".call(" + helper.callParams + ")");
1716
+ this.push(helper.name + ".call(" + helper.callParams + ")");
1605
1717
  },
1606
1718
 
1607
1719
  // [invokeAmbiguous]
@@ -1616,19 +1728,18 @@ Handlebars.JavaScriptCompiler = function() {};
1616
1728
  // This operation emits more code than the other options,
1617
1729
  // and can be avoided by passing the `knownHelpers` and
1618
1730
  // `knownHelpersOnly` flags at compile-time.
1619
- invokeAmbiguous: function(name) {
1731
+ invokeAmbiguous: function(name, helperCall) {
1620
1732
  this.context.aliases.functionType = '"function"';
1621
1733
 
1622
- this.pushStackLiteral('{}');
1623
- var helper = this.setupHelper(0, name);
1734
+ this.pushStackLiteral('{}'); // Hash value
1735
+ var helper = this.setupHelper(0, name, helperCall);
1624
1736
 
1625
1737
  var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1626
- this.register('foundHelper', helperName);
1627
1738
 
1628
1739
  var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1629
1740
  var nextStack = this.nextStack();
1630
1741
 
1631
- this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
1742
+ this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
1632
1743
  this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
1633
1744
  },
1634
1745
 
@@ -1647,7 +1758,7 @@ Handlebars.JavaScriptCompiler = function() {};
1647
1758
  }
1648
1759
 
1649
1760
  this.context.aliases.self = "this";
1650
- this.pushStack("self.invokePartial(" + params.join(", ") + ")");
1761
+ this.push("self.invokePartial(" + params.join(", ") + ")");
1651
1762
  },
1652
1763
 
1653
1764
  // [assignToHash]
@@ -1658,17 +1769,19 @@ Handlebars.JavaScriptCompiler = function() {};
1658
1769
  // Pops a value and hash off the stack, assigns `hash[key] = value`
1659
1770
  // and pushes the hash back onto the stack.
1660
1771
  assignToHash: function(key) {
1661
- var value = this.popStack();
1772
+ var value = this.popStack(),
1773
+ type;
1662
1774
 
1663
1775
  if (this.options.stringParams) {
1664
- var type = this.popStack();
1776
+ type = this.popStack();
1665
1777
  this.popStack();
1666
- this.source.push("hashTypes['" + key + "'] = " + type + ";");
1667
1778
  }
1668
1779
 
1669
- var hash = this.topStack();
1670
-
1671
- this.source.push(hash + "['" + key + "'] = " + value + ";");
1780
+ var hash = this.hash;
1781
+ if (type) {
1782
+ hash.types.push("'" + key + "': " + type);
1783
+ }
1784
+ hash.values.push("'" + key + "': (" + value + ")");
1672
1785
  },
1673
1786
 
1674
1787
  // HELPERS
@@ -1682,11 +1795,27 @@ Handlebars.JavaScriptCompiler = function() {};
1682
1795
  child = children[i];
1683
1796
  compiler = new this.compiler();
1684
1797
 
1685
- this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1686
- var index = this.context.programs.length;
1687
- child.index = index;
1688
- child.name = 'program' + index;
1689
- this.context.programs[index] = compiler.compile(child, options, this.context);
1798
+ var index = this.matchExistingProgram(child);
1799
+
1800
+ if (index == null) {
1801
+ this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1802
+ index = this.context.programs.length;
1803
+ child.index = index;
1804
+ child.name = 'program' + index;
1805
+ this.context.programs[index] = compiler.compile(child, options, this.context);
1806
+ this.context.environments[index] = child;
1807
+ } else {
1808
+ child.index = index;
1809
+ child.name = 'program' + index;
1810
+ }
1811
+ }
1812
+ },
1813
+ matchExistingProgram: function(child) {
1814
+ for (var i = 0, len = this.context.environments.length; i < len; i++) {
1815
+ var environment = this.context.environments[i];
1816
+ if (environment && environment.equals(child)) {
1817
+ return i;
1818
+ }
1690
1819
  }
1691
1820
  },
1692
1821
 
@@ -1730,57 +1859,111 @@ Handlebars.JavaScriptCompiler = function() {};
1730
1859
  },
1731
1860
 
1732
1861
  pushStackLiteral: function(item) {
1733
- this.compileStack.push(new Literal(item));
1734
- return item;
1862
+ return this.push(new Literal(item));
1735
1863
  },
1736
1864
 
1737
1865
  pushStack: function(item) {
1866
+ this.flushInline();
1867
+
1738
1868
  var stack = this.incrStack();
1739
- this.source.push(stack + " = " + item + ";");
1869
+ if (item) {
1870
+ this.source.push(stack + " = " + item + ";");
1871
+ }
1740
1872
  this.compileStack.push(stack);
1741
1873
  return stack;
1742
1874
  },
1743
1875
 
1744
1876
  replaceStack: function(callback) {
1745
- var stack = this.topStack(),
1746
- item = callback.call(this, stack);
1877
+ var prefix = '',
1878
+ inline = this.isInline(),
1879
+ stack;
1880
+
1881
+ // If we are currently inline then we want to merge the inline statement into the
1882
+ // replacement statement via ','
1883
+ if (inline) {
1884
+ var top = this.popStack(true);
1885
+
1886
+ if (top instanceof Literal) {
1887
+ // Literals do not need to be inlined
1888
+ stack = top.value;
1889
+ } else {
1890
+ // Get or create the current stack name for use by the inline
1891
+ var name = this.stackSlot ? this.topStackName() : this.incrStack();
1747
1892
 
1748
- // Prevent modification of the context depth variable. Through replaceStack
1749
- if (/^depth/.test(stack)) {
1750
- stack = this.nextStack();
1893
+ prefix = '(' + this.push(name) + ' = ' + top + '),';
1894
+ stack = this.topStack();
1895
+ }
1896
+ } else {
1897
+ stack = this.topStack();
1751
1898
  }
1752
1899
 
1753
- this.source.push(stack + " = " + item + ";");
1900
+ var item = callback.call(this, stack);
1901
+
1902
+ if (inline) {
1903
+ if (this.inlineStack.length || this.compileStack.length) {
1904
+ this.popStack();
1905
+ }
1906
+ this.push('(' + prefix + item + ')');
1907
+ } else {
1908
+ // Prevent modification of the context depth variable. Through replaceStack
1909
+ if (!/^stack/.test(stack)) {
1910
+ stack = this.nextStack();
1911
+ }
1912
+
1913
+ this.source.push(stack + " = (" + prefix + item + ");");
1914
+ }
1754
1915
  return stack;
1755
1916
  },
1756
1917
 
1757
- nextStack: function(skipCompileStack) {
1758
- var name = this.incrStack();
1759
- this.compileStack.push(name);
1760
- return name;
1918
+ nextStack: function() {
1919
+ return this.pushStack();
1761
1920
  },
1762
1921
 
1763
1922
  incrStack: function() {
1764
1923
  this.stackSlot++;
1765
1924
  if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1925
+ return this.topStackName();
1926
+ },
1927
+ topStackName: function() {
1766
1928
  return "stack" + this.stackSlot;
1767
1929
  },
1930
+ flushInline: function() {
1931
+ var inlineStack = this.inlineStack;
1932
+ if (inlineStack.length) {
1933
+ this.inlineStack = [];
1934
+ for (var i = 0, len = inlineStack.length; i < len; i++) {
1935
+ var entry = inlineStack[i];
1936
+ if (entry instanceof Literal) {
1937
+ this.compileStack.push(entry);
1938
+ } else {
1939
+ this.pushStack(entry);
1940
+ }
1941
+ }
1942
+ }
1943
+ },
1944
+ isInline: function() {
1945
+ return this.inlineStack.length;
1946
+ },
1768
1947
 
1769
- popStack: function() {
1770
- var item = this.compileStack.pop();
1948
+ popStack: function(wrapped) {
1949
+ var inline = this.isInline(),
1950
+ item = (inline ? this.inlineStack : this.compileStack).pop();
1771
1951
 
1772
- if (item instanceof Literal) {
1952
+ if (!wrapped && (item instanceof Literal)) {
1773
1953
  return item.value;
1774
1954
  } else {
1775
- this.stackSlot--;
1955
+ if (!inline) {
1956
+ this.stackSlot--;
1957
+ }
1776
1958
  return item;
1777
1959
  }
1778
1960
  },
1779
1961
 
1780
- topStack: function() {
1781
- var item = this.compileStack[this.compileStack.length - 1];
1962
+ topStack: function(wrapped) {
1963
+ var stack = (this.isInline() ? this.inlineStack : this.compileStack),
1964
+ item = stack[stack.length - 1];
1782
1965
 
1783
- if (item instanceof Literal) {
1966
+ if (!wrapped && (item instanceof Literal)) {
1784
1967
  return item.value;
1785
1968
  } else {
1786
1969
  return item;
@@ -1795,22 +1978,22 @@ Handlebars.JavaScriptCompiler = function() {};
1795
1978
  .replace(/\r/g, '\\r') + '"';
1796
1979
  },
1797
1980
 
1798
- setupHelper: function(paramSize, name) {
1981
+ setupHelper: function(paramSize, name, missingParams) {
1799
1982
  var params = [];
1800
- this.setupParams(paramSize, params);
1983
+ this.setupParams(paramSize, params, missingParams);
1801
1984
  var foundHelper = this.nameLookup('helpers', name, 'helper');
1802
1985
 
1803
1986
  return {
1804
1987
  params: params,
1805
1988
  name: foundHelper,
1806
1989
  callParams: ["depth0"].concat(params).join(", "),
1807
- helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
1990
+ helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
1808
1991
  };
1809
1992
  },
1810
1993
 
1811
1994
  // the params and contexts arguments are passed in arrays
1812
1995
  // to fill in
1813
- setupParams: function(paramSize, params) {
1996
+ setupParams: function(paramSize, params, useRegister) {
1814
1997
  var options = [], contexts = [], types = [], param, inverse, program;
1815
1998
 
1816
1999
  options.push("hash:" + this.popStack());
@@ -1855,7 +2038,13 @@ Handlebars.JavaScriptCompiler = function() {};
1855
2038
  options.push("data:data");
1856
2039
  }
1857
2040
 
1858
- params.push("{" + options.join(",") + "}");
2041
+ options = "{" + options.join(",") + "}";
2042
+ if (useRegister) {
2043
+ this.register('options', options);
2044
+ params.push('options');
2045
+ } else {
2046
+ params.push(options);
2047
+ }
1859
2048
  return params.join(", ");
1860
2049
  }
1861
2050
  };
@@ -1893,23 +2082,23 @@ Handlebars.JavaScriptCompiler = function() {};
1893
2082
 
1894
2083
  })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
1895
2084
 
1896
- Handlebars.precompile = function(string, options) {
1897
- if (typeof string !== 'string') {
1898
- throw new Handlebars.Exception("You must pass a string to Handlebars.compile. You passed " + string);
2085
+ Handlebars.precompile = function(input, options) {
2086
+ if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
2087
+ throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
1899
2088
  }
1900
2089
 
1901
2090
  options = options || {};
1902
2091
  if (!('data' in options)) {
1903
2092
  options.data = true;
1904
2093
  }
1905
- var ast = Handlebars.parse(string);
2094
+ var ast = Handlebars.parse(input);
1906
2095
  var environment = new Handlebars.Compiler().compile(ast, options);
1907
2096
  return new Handlebars.JavaScriptCompiler().compile(environment, options);
1908
2097
  };
1909
2098
 
1910
- Handlebars.compile = function(string, options) {
1911
- if (typeof string !== 'string') {
1912
- throw new Handlebars.Exception("You must pass a string to Handlebars.compile. You passed " + string);
2099
+ Handlebars.compile = function(input, options) {
2100
+ if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
2101
+ throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
1913
2102
  }
1914
2103
 
1915
2104
  options = options || {};
@@ -1918,7 +2107,7 @@ Handlebars.compile = function(string, options) {
1918
2107
  }
1919
2108
  var compiled;
1920
2109
  function compile() {
1921
- var ast = Handlebars.parse(string);
2110
+ var ast = Handlebars.parse(input);
1922
2111
  var environment = new Handlebars.Compiler().compile(ast, options);
1923
2112
  var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
1924
2113
  return Handlebars.template(templateSpec);
@@ -1953,12 +2142,32 @@ Handlebars.VM = {
1953
2142
  }
1954
2143
  },
1955
2144
  programWithDepth: Handlebars.VM.programWithDepth,
1956
- noop: Handlebars.VM.noop
2145
+ noop: Handlebars.VM.noop,
2146
+ compilerInfo: null
1957
2147
  };
1958
2148
 
1959
2149
  return function(context, options) {
1960
2150
  options = options || {};
1961
- return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
2151
+ var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
2152
+
2153
+ var compilerInfo = container.compilerInfo || [],
2154
+ compilerRevision = compilerInfo[0] || 1,
2155
+ currentRevision = Handlebars.COMPILER_REVISION;
2156
+
2157
+ if (compilerRevision !== currentRevision) {
2158
+ if (compilerRevision < currentRevision) {
2159
+ var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
2160
+ compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
2161
+ throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
2162
+ "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
2163
+ } else {
2164
+ // Use the embedded version info since the runtime doesn't know about this revision yet
2165
+ throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
2166
+ "Please update your runtime to a newer version ("+compilerInfo[1]+").";
2167
+ }
2168
+ }
2169
+
2170
+ return result;
1962
2171
  };
1963
2172
  },
1964
2173
 
@@ -2014,7 +2223,7 @@ var objectCreate = Object.create || function(parent) {
2014
2223
  };
2015
2224
 
2016
2225
  var Handlebars = this.Handlebars || Ember.imports.Handlebars;
2017
- Ember.assert("Ember Handlebars requires Handlebars 1.0.rc.2 or greater", Handlebars && Handlebars.VERSION.match(/^1\.0\.rc\.[23456789]+/));
2226
+ Ember.assert("Ember Handlebars requires Handlebars 1.0.0-rc.3 or greater", Handlebars && Handlebars.VERSION.match(/^1\.0\.[0-9](\.rc\.[23456789]+)?/));
2018
2227
 
2019
2228
  /**
2020
2229
  Prepares the Handlebars templating library for use inside Ember's view
@@ -2091,6 +2300,8 @@ Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string)
2091
2300
  return "data.buffer.push("+string+");";
2092
2301
  };
2093
2302
 
2303
+ var prefix = "ember" + (+new Date()), incr = 1;
2304
+
2094
2305
  /**
2095
2306
  @private
2096
2307
 
@@ -2103,8 +2314,11 @@ Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string)
2103
2314
  @param mustache
2104
2315
  */
2105
2316
  Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
2106
- if (mustache.params.length || mustache.hash) {
2107
- return Handlebars.Compiler.prototype.mustache.call(this, mustache);
2317
+ if (mustache.isHelper && mustache.id.string === 'control') {
2318
+ mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
2319
+ mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]);
2320
+ } else if (mustache.params.length || mustache.hash) {
2321
+ // no changes required
2108
2322
  } else {
2109
2323
  var id = new Handlebars.AST.IdNode(['_triageMustache']);
2110
2324
 
@@ -2116,8 +2330,9 @@ Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
2116
2330
  mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
2117
2331
  }
2118
2332
  mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
2119
- return Handlebars.Compiler.prototype.mustache.call(this, mustache);
2120
2333
  }
2334
+
2335
+ return Handlebars.Compiler.prototype.mustache.call(this, mustache);
2121
2336
  };
2122
2337
 
2123
2338
  /**