barber 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  /**