handlebars_assets 0.8.2 → 0.9.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.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## On master
2
2
 
3
+ ## 0.9.0 (2013-07-25)
4
+
5
+ * Update to [this commit](https://github.com/wycats/handlebars.js/commit/a3376e24b1a25f72cf86d1d999bd2ea93fa4dc39) of `handlebars.js`
6
+ * The hack that converted partial names to underscored paths (`shared/_time` -> `_shared_time`) is no longer necessary and has been removed. You should change all the partial references in your app when going to v0.9.x.
7
+
3
8
  ## 0.8.2
4
9
 
5
10
  * Update to [this commit](https://github.com/wycats/handlebars.js/commit/5e5f0dce9c352f490f1f1e58fd7d0f76dd006cac) of `handlebars.js`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- handlebars_assets (0.8.2)
4
+ handlebars_assets (0.9.0)
5
5
  execjs (>= 1.2.9)
6
6
  sprockets (>= 2.0.3)
7
7
  tilt
data/README.md CHANGED
@@ -6,11 +6,15 @@ Yea, I think so too. That is why I wrote **handlebars_assets**. Give your Handle
6
6
 
7
7
  Using `sprockets` with Sinatra or another framework? **handlebars_assets** works outside of Rails too (as of v0.2.0)
8
8
 
9
+ # BREAKING CHANGE IN v0.9.0
10
+
11
+ My pull request to allow `/` in partials was pulled into Handlebars. The hack that converted partial names to underscored paths (`shared/_time` -> `_shared_time`) is no longer necessary and has been removed. You should change all the partial references in your app when going to v0.9.x.
12
+
9
13
  ## handlebars.js
10
14
 
11
15
  **Please read**
12
16
 
13
- Because of a serious regression introduced in RC1, `handlebars_assets` is packaged with an edge build of `handlebars.js` (this [commit](https://github.com/wycats/handlebars.js/commit/5e5f0dce9c352f490f1f1e58fd7d0f76dd006cac). See the section on using another version if that does not work for you.
17
+ `handlebars_assets` is packaged with an edge build of `handlebars.js` (this [commit](https://github.com/wycats/handlebars.js/commit/a3376e24b1a25f72cf86d1d999bd2ea93fa4dc39). See the section on using another version if that does not work for you.
14
18
 
15
19
  ## Installation with Rails 3.1+
16
20
 
@@ -132,18 +136,7 @@ The Haml will be pre-processed so that the Handlebars template is basically this
132
136
 
133
137
  If you begin the name of the template with an underscore, it will be recognized as a partial. You can invoke partials inside a template using the Handlebars partial syntax:
134
138
 
135
- Invoke a {{> partial }}
136
-
137
- **Important!** Handlebars does not understand nested partials. To support them, partials are named based on their path using `_` instead of `/` (skid => slash). So given:
138
-
139
- templates/
140
- _form.hbs
141
- contacts/
142
- _form.hbs
143
- todos/
144
- _form.hbs
145
-
146
- You will get three partials named `_form`, `_contacts_form`, and `_todos_form`; note that the partials begin with `_`.
139
+ Invoke a {{> path/to/_partial }}
147
140
 
148
141
  ## Using another version of `handlebars.js`
149
142
 
@@ -78,25 +78,17 @@ module HandlebarsAssets
78
78
  end
79
79
 
80
80
  def name
81
- is_partial? ? partial_name : template_name
81
+ template_name
82
82
  end
83
83
 
84
84
  private
85
85
 
86
86
  attr_accessor :full_path, :template_path
87
87
 
88
- def forced_underscore_name
89
- '_' + relative_path
90
- end
91
-
92
88
  def relative_path
93
89
  template_path.gsub(/^#{HandlebarsAssets::Config.path_prefix}\/(.*)$/i, "\\1")
94
90
  end
95
91
 
96
- def partial_name
97
- forced_underscore_name.gsub(/\//, '_').gsub(/__/, '_').dump
98
- end
99
-
100
92
  def template_name
101
93
  relative_path.dump
102
94
  end
@@ -1,3 +1,3 @@
1
1
  module HandlebarsAssets
2
- VERSION = "0.8.2"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -63,7 +63,7 @@ module HandlebarsAssets
63
63
 
64
64
  template2 = HandlebarsAssets::TiltHandlebars.new(scope2.pathname.to_s) { source }
65
65
 
66
- assert_equal hbs_compiled_partial('_some_thing_test_underscore', source), template2.render(scope2, {})
66
+ assert_equal hbs_compiled_partial('some/thing/_test_underscore', source), template2.render(scope2, {})
67
67
  end
68
68
 
69
69
  def test_without_known_helpers_opt
@@ -1,3 +1,27 @@
1
+ /*
2
+
3
+ Copyright (C) 2011 by Yehuda Katz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ */
24
+
1
25
  // lib/handlebars/base.js
2
26
 
3
27
  /*jshint eqnull:true*/
@@ -5,7 +29,7 @@ this.Handlebars = {};
5
29
 
6
30
  (function(Handlebars) {
7
31
 
8
- Handlebars.VERSION = "1.0.rc.1";
32
+ Handlebars.VERSION = "1.0.rc.2";
9
33
 
10
34
  Handlebars.helpers = {};
11
35
  Handlebars.partials = {};
@@ -702,8 +726,11 @@ Handlebars.print = function(ast) {
702
726
  for(var i=0,l=parts.length; i<l; i++) {
703
727
  var part = parts[i];
704
728
 
705
- if(part === "..") { depth++; }
706
- else if(part === "." || part === "this") { this.isScoped = true; }
729
+ if (part === ".." || part === "." || part === "this") {
730
+ if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
731
+ else if (part === "..") { depth++; }
732
+ else { this.isScoped = true; }
733
+ }
707
734
  else { dig.push(part); }
708
735
  }
709
736
 
@@ -714,6 +741,8 @@ Handlebars.print = function(ast) {
714
741
  // an ID is simple if it only has one part, and that part is not
715
742
  // `..` or `this`.
716
743
  this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
744
+
745
+ this.stringModeValue = this.string;
717
746
  };
718
747
 
719
748
  Handlebars.AST.PartialNameNode = function(name) {
@@ -729,16 +758,19 @@ Handlebars.print = function(ast) {
729
758
  Handlebars.AST.StringNode = function(string) {
730
759
  this.type = "STRING";
731
760
  this.string = string;
761
+ this.stringModeValue = string;
732
762
  };
733
763
 
734
764
  Handlebars.AST.IntegerNode = function(integer) {
735
765
  this.type = "INTEGER";
736
766
  this.integer = integer;
767
+ this.stringModeValue = Number(integer);
737
768
  };
738
769
 
739
770
  Handlebars.AST.BooleanNode = function(bool) {
740
771
  this.type = "BOOLEAN";
741
772
  this.bool = bool;
773
+ this.stringModeValue = bool === "true";
742
774
  };
743
775
 
744
776
  Handlebars.AST.CommentNode = function(comment) {
@@ -848,6 +880,26 @@ Handlebars.JavaScriptCompiler = function() {};
848
880
 
849
881
  return out.join("\n");
850
882
  },
883
+ equals: function(other) {
884
+ var len = this.opcodes.length;
885
+ if (other.opcodes.length !== len) {
886
+ return false;
887
+ }
888
+
889
+ for (var i = 0; i < len; i++) {
890
+ var opcode = this.opcodes[i],
891
+ otherOpcode = other.opcodes[i];
892
+ if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
893
+ return false;
894
+ }
895
+ for (var j = 0; j < opcode.args.length; j++) {
896
+ if (opcode.args[j] !== otherOpcode.args[j]) {
897
+ return false;
898
+ }
899
+ }
900
+ }
901
+ return true;
902
+ },
851
903
 
852
904
  guid: 0,
853
905
 
@@ -939,7 +991,7 @@ Handlebars.JavaScriptCompiler = function() {};
939
991
  // evaluate it by executing `blockHelperMissing`
940
992
  this.opcode('pushProgram', program);
941
993
  this.opcode('pushProgram', inverse);
942
- this.opcode('pushLiteral', '{}');
994
+ this.opcode('emptyHash');
943
995
  this.opcode('blockValue');
944
996
  } else {
945
997
  this.ambiguousMustache(mustache, program, inverse);
@@ -948,7 +1000,7 @@ Handlebars.JavaScriptCompiler = function() {};
948
1000
  // evaluate it by executing `blockHelperMissing`
949
1001
  this.opcode('pushProgram', program);
950
1002
  this.opcode('pushProgram', inverse);
951
- this.opcode('pushLiteral', '{}');
1003
+ this.opcode('emptyHash');
952
1004
  this.opcode('ambiguousBlockValue');
953
1005
  }
954
1006
 
@@ -958,15 +1010,21 @@ Handlebars.JavaScriptCompiler = function() {};
958
1010
  hash: function(hash) {
959
1011
  var pairs = hash.pairs, pair, val;
960
1012
 
961
- this.opcode('push', '{}');
1013
+ this.opcode('pushHash');
962
1014
 
963
1015
  for(var i=0, l=pairs.length; i<l; i++) {
964
1016
  pair = pairs[i];
965
1017
  val = pair[1];
966
1018
 
967
- this.accept(val);
1019
+ if (this.options.stringParams) {
1020
+ this.opcode('pushStringParam', val.stringModeValue, val.type);
1021
+ } else {
1022
+ this.accept(val);
1023
+ }
1024
+
968
1025
  this.opcode('assignToHash', pair[0]);
969
1026
  }
1027
+ this.opcode('popHash');
970
1028
  },
971
1029
 
972
1030
  partial: function(partial) {
@@ -1007,17 +1065,19 @@ Handlebars.JavaScriptCompiler = function() {};
1007
1065
  },
1008
1066
 
1009
1067
  ambiguousMustache: function(mustache, program, inverse) {
1010
- var id = mustache.id, name = id.parts[0];
1068
+ var id = mustache.id,
1069
+ name = id.parts[0],
1070
+ isBlock = program != null || inverse != null;
1011
1071
 
1012
1072
  this.opcode('getContext', id.depth);
1013
1073
 
1014
1074
  this.opcode('pushProgram', program);
1015
1075
  this.opcode('pushProgram', inverse);
1016
1076
 
1017
- this.opcode('invokeAmbiguous', name);
1077
+ this.opcode('invokeAmbiguous', name, isBlock);
1018
1078
  },
1019
1079
 
1020
- simpleMustache: function(mustache, program, inverse) {
1080
+ simpleMustache: function(mustache) {
1021
1081
  var id = mustache.id;
1022
1082
 
1023
1083
  if (id.type === 'DATA') {
@@ -1134,7 +1194,7 @@ Handlebars.JavaScriptCompiler = function() {};
1134
1194
  }
1135
1195
 
1136
1196
  this.opcode('getContext', param.depth || 0);
1137
- this.opcode('pushStringParam', param.string);
1197
+ this.opcode('pushStringParam', param.stringModeValue, param.type);
1138
1198
  } else {
1139
1199
  this[param.type](param);
1140
1200
  }
@@ -1148,7 +1208,7 @@ Handlebars.JavaScriptCompiler = function() {};
1148
1208
  if(mustache.hash) {
1149
1209
  this.hash(mustache.hash);
1150
1210
  } else {
1151
- this.opcode('pushLiteral', '{}');
1211
+ this.opcode('emptyHash');
1152
1212
  }
1153
1213
 
1154
1214
  return params;
@@ -1165,7 +1225,7 @@ Handlebars.JavaScriptCompiler = function() {};
1165
1225
  if(mustache.hash) {
1166
1226
  this.hash(mustache.hash);
1167
1227
  } else {
1168
- this.opcode('pushLiteral', '{}');
1228
+ this.opcode('emptyHash');
1169
1229
  }
1170
1230
 
1171
1231
  return params;
@@ -1179,7 +1239,7 @@ Handlebars.JavaScriptCompiler = function() {};
1179
1239
  JavaScriptCompiler.prototype = {
1180
1240
  // PUBLIC API: You can override these methods in a subclass to provide
1181
1241
  // alternative compiled forms for name lookup and buffering semantics
1182
- nameLookup: function(parent, name, type) {
1242
+ nameLookup: function(parent, name /* , type*/) {
1183
1243
  if (/^[0-9]+$/.test(name)) {
1184
1244
  return parent + "[" + name + "]";
1185
1245
  } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
@@ -1194,7 +1254,11 @@ Handlebars.JavaScriptCompiler = function() {};
1194
1254
  if (this.environment.isSimple) {
1195
1255
  return "return " + string + ";";
1196
1256
  } else {
1197
- return "buffer += " + string + ";";
1257
+ return {
1258
+ appendToBuffer: true,
1259
+ content: string,
1260
+ toString: function() { return "buffer += " + string + ";"; }
1261
+ };
1198
1262
  }
1199
1263
  },
1200
1264
 
@@ -1215,6 +1279,7 @@ Handlebars.JavaScriptCompiler = function() {};
1215
1279
  this.isChild = !!context;
1216
1280
  this.context = context || {
1217
1281
  programs: [],
1282
+ environments: [],
1218
1283
  aliases: { }
1219
1284
  };
1220
1285
 
@@ -1224,6 +1289,7 @@ Handlebars.JavaScriptCompiler = function() {};
1224
1289
  this.stackVars = [];
1225
1290
  this.registers = { list: [] };
1226
1291
  this.compileStack = [];
1292
+ this.inlineStack = [];
1227
1293
 
1228
1294
  this.compileChildren(environment, options);
1229
1295
 
@@ -1245,11 +1311,11 @@ Handlebars.JavaScriptCompiler = function() {};
1245
1311
  },
1246
1312
 
1247
1313
  nextOpcode: function() {
1248
- var opcodes = this.environment.opcodes, opcode = opcodes[this.i + 1];
1314
+ var opcodes = this.environment.opcodes;
1249
1315
  return opcodes[this.i + 1];
1250
1316
  },
1251
1317
 
1252
- eat: function(opcode) {
1318
+ eat: function() {
1253
1319
  this.i = this.i + 1;
1254
1320
  },
1255
1321
 
@@ -1287,7 +1353,6 @@ Handlebars.JavaScriptCompiler = function() {};
1287
1353
 
1288
1354
  // Generate minimizer alias mappings
1289
1355
  if (!this.isChild) {
1290
- var aliases = [];
1291
1356
  for (var alias in this.context.aliases) {
1292
1357
  this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1293
1358
  }
@@ -1312,16 +1377,42 @@ Handlebars.JavaScriptCompiler = function() {};
1312
1377
  params.push("depth" + this.environment.depths.list[i]);
1313
1378
  }
1314
1379
 
1380
+ // Perform a second pass over the output to merge content when possible
1381
+ var source = this.mergeSource();
1382
+
1315
1383
  if (asObject) {
1316
- params.push(this.source.join("\n "));
1384
+ params.push(source);
1317
1385
 
1318
1386
  return Function.apply(this, params);
1319
1387
  } else {
1320
- var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
1388
+ var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
1321
1389
  Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
1322
1390
  return functionSource;
1323
1391
  }
1324
1392
  },
1393
+ mergeSource: function() {
1394
+ // WARN: We are not handling the case where buffer is still populated as the source should
1395
+ // not have buffer append operations as their final action.
1396
+ var source = '',
1397
+ buffer;
1398
+ for (var i = 0, len = this.source.length; i < len; i++) {
1399
+ var line = this.source[i];
1400
+ if (line.appendToBuffer) {
1401
+ if (buffer) {
1402
+ buffer = buffer + '\n + ' + line.content;
1403
+ } else {
1404
+ buffer = line.content;
1405
+ }
1406
+ } else {
1407
+ if (buffer) {
1408
+ source += 'buffer += ' + buffer + ';\n ';
1409
+ buffer = undefined;
1410
+ }
1411
+ source += line + '\n ';
1412
+ }
1413
+ }
1414
+ return source;
1415
+ },
1325
1416
 
1326
1417
  // [blockValue]
1327
1418
  //
@@ -1359,6 +1450,9 @@ Handlebars.JavaScriptCompiler = function() {};
1359
1450
  var current = this.topStack();
1360
1451
  params.splice(1, 0, current);
1361
1452
 
1453
+ // Use the options value generated from the invocation
1454
+ params[params.length-1] = 'options';
1455
+
1362
1456
  this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
1363
1457
  },
1364
1458
 
@@ -1382,6 +1476,9 @@ Handlebars.JavaScriptCompiler = function() {};
1382
1476
  // If `value` is truthy, or 0, it is coerced into a string and appended
1383
1477
  // Otherwise, the empty string is appended
1384
1478
  append: function() {
1479
+ // Force anything that is inlined onto the stack so we don't have duplication
1480
+ // when we examine local
1481
+ this.flushInline();
1385
1482
  var local = this.popStack();
1386
1483
  this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
1387
1484
  if (this.environment.isSimple) {
@@ -1396,15 +1493,9 @@ Handlebars.JavaScriptCompiler = function() {};
1396
1493
  //
1397
1494
  // Escape `value` and append it to the buffer
1398
1495
  appendEscaped: function() {
1399
- var opcode = this.nextOpcode(), extra = "";
1400
1496
  this.context.aliases.escapeExpression = 'this.escapeExpression';
1401
1497
 
1402
- if(opcode && opcode.opcode === 'appendContent') {
1403
- extra = " + " + this.quotedString(opcode.args[0]);
1404
- this.eat(opcode);
1405
- }
1406
-
1407
- this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
1498
+ this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
1408
1499
  },
1409
1500
 
1410
1501
  // [getContext]
@@ -1428,7 +1519,7 @@ Handlebars.JavaScriptCompiler = function() {};
1428
1519
  // Looks up the value of `name` on the current context and pushes
1429
1520
  // it onto the stack.
1430
1521
  lookupOnContext: function(name) {
1431
- this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
1522
+ this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
1432
1523
  },
1433
1524
 
1434
1525
  // [pushContext]
@@ -1476,7 +1567,7 @@ Handlebars.JavaScriptCompiler = function() {};
1476
1567
  //
1477
1568
  // Push the result of looking up `id` on the current data
1478
1569
  lookupData: function(id) {
1479
- this.pushStack(this.nameLookup('data', id, 'data'));
1570
+ this.push(this.nameLookup('data', id, 'data'));
1480
1571
  },
1481
1572
 
1482
1573
  // [pushStringParam]
@@ -1487,9 +1578,36 @@ Handlebars.JavaScriptCompiler = function() {};
1487
1578
  // This opcode is designed for use in string mode, which
1488
1579
  // provides the string value of a parameter along with its
1489
1580
  // depth rather than resolving it immediately.
1490
- pushStringParam: function(string) {
1581
+ pushStringParam: function(string, type) {
1491
1582
  this.pushStackLiteral('depth' + this.lastContext);
1492
- this.pushString(string);
1583
+
1584
+ this.pushString(type);
1585
+
1586
+ if (typeof string === 'string') {
1587
+ this.pushString(string);
1588
+ } else {
1589
+ this.pushStackLiteral(string);
1590
+ }
1591
+ },
1592
+
1593
+ emptyHash: function() {
1594
+ this.pushStackLiteral('{}');
1595
+
1596
+ if (this.options.stringParams) {
1597
+ this.register('hashTypes', '{}');
1598
+ }
1599
+ },
1600
+ pushHash: function() {
1601
+ this.hash = {values: [], types: []};
1602
+ },
1603
+ popHash: function() {
1604
+ var hash = this.hash;
1605
+ this.hash = undefined;
1606
+
1607
+ if (this.options.stringParams) {
1608
+ this.register('hashTypes', '{' + hash.types.join(',') + '}');
1609
+ }
1610
+ this.push('{\n ' + hash.values.join(',\n ') + '\n }');
1493
1611
  },
1494
1612
 
1495
1613
  // [pushString]
@@ -1509,7 +1627,8 @@ Handlebars.JavaScriptCompiler = function() {};
1509
1627
  //
1510
1628
  // Push an expression onto the stack
1511
1629
  push: function(expr) {
1512
- this.pushStack(expr);
1630
+ this.inlineStack.push(expr);
1631
+ return expr;
1513
1632
  },
1514
1633
 
1515
1634
  // [pushLiteral]
@@ -1552,12 +1671,14 @@ Handlebars.JavaScriptCompiler = function() {};
1552
1671
  invokeHelper: function(paramSize, name) {
1553
1672
  this.context.aliases.helperMissing = 'helpers.helperMissing';
1554
1673
 
1555
- var helper = this.lastHelper = this.setupHelper(paramSize, name);
1556
- this.register('foundHelper', helper.name);
1674
+ var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
1557
1675
 
1558
- this.pushStack("foundHelper ? foundHelper.call(" +
1559
- helper.callParams + ") " + ": helperMissing.call(" +
1560
- helper.helperMissingParams + ")");
1676
+ this.push(helper.name);
1677
+ this.replaceStack(function(name) {
1678
+ return name + ' ? ' + name + '.call(' +
1679
+ helper.callParams + ") " + ": helperMissing.call(" +
1680
+ helper.helperMissingParams + ")";
1681
+ });
1561
1682
  },
1562
1683
 
1563
1684
  // [invokeKnownHelper]
@@ -1569,7 +1690,7 @@ Handlebars.JavaScriptCompiler = function() {};
1569
1690
  // so a `helperMissing` fallback is not required.
1570
1691
  invokeKnownHelper: function(paramSize, name) {
1571
1692
  var helper = this.setupHelper(paramSize, name);
1572
- this.pushStack(helper.name + ".call(" + helper.callParams + ")");
1693
+ this.push(helper.name + ".call(" + helper.callParams + ")");
1573
1694
  },
1574
1695
 
1575
1696
  // [invokeAmbiguous]
@@ -1584,19 +1705,18 @@ Handlebars.JavaScriptCompiler = function() {};
1584
1705
  // This operation emits more code than the other options,
1585
1706
  // and can be avoided by passing the `knownHelpers` and
1586
1707
  // `knownHelpersOnly` flags at compile-time.
1587
- invokeAmbiguous: function(name) {
1708
+ invokeAmbiguous: function(name, helperCall) {
1588
1709
  this.context.aliases.functionType = '"function"';
1589
1710
 
1590
- this.pushStackLiteral('{}');
1591
- var helper = this.setupHelper(0, name);
1711
+ this.pushStackLiteral('{}'); // Hash value
1712
+ var helper = this.setupHelper(0, name, helperCall);
1592
1713
 
1593
1714
  var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
1594
- this.register('foundHelper', helperName);
1595
1715
 
1596
1716
  var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
1597
1717
  var nextStack = this.nextStack();
1598
1718
 
1599
- this.source.push('if (foundHelper) { ' + nextStack + ' = foundHelper.call(' + helper.callParams + '); }');
1719
+ this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
1600
1720
  this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
1601
1721
  },
1602
1722
 
@@ -1615,7 +1735,7 @@ Handlebars.JavaScriptCompiler = function() {};
1615
1735
  }
1616
1736
 
1617
1737
  this.context.aliases.self = "this";
1618
- this.pushStack("self.invokePartial(" + params.join(", ") + ")");
1738
+ this.push("self.invokePartial(" + params.join(", ") + ")");
1619
1739
  },
1620
1740
 
1621
1741
  // [assignToHash]
@@ -1626,10 +1746,19 @@ Handlebars.JavaScriptCompiler = function() {};
1626
1746
  // Pops a value and hash off the stack, assigns `hash[key] = value`
1627
1747
  // and pushes the hash back onto the stack.
1628
1748
  assignToHash: function(key) {
1629
- var value = this.popStack();
1630
- var hash = this.topStack();
1749
+ var value = this.popStack(),
1750
+ type;
1631
1751
 
1632
- this.source.push(hash + "['" + key + "'] = " + value + ";");
1752
+ if (this.options.stringParams) {
1753
+ type = this.popStack();
1754
+ this.popStack();
1755
+ }
1756
+
1757
+ var hash = this.hash;
1758
+ if (type) {
1759
+ hash.types.push("'" + key + "': " + type);
1760
+ }
1761
+ hash.values.push("'" + key + "': (" + value + ")");
1633
1762
  },
1634
1763
 
1635
1764
  // HELPERS
@@ -1643,11 +1772,27 @@ Handlebars.JavaScriptCompiler = function() {};
1643
1772
  child = children[i];
1644
1773
  compiler = new this.compiler();
1645
1774
 
1646
- this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1647
- var index = this.context.programs.length;
1648
- child.index = index;
1649
- child.name = 'program' + index;
1650
- this.context.programs[index] = compiler.compile(child, options, this.context);
1775
+ var index = this.matchExistingProgram(child);
1776
+
1777
+ if (index == null) {
1778
+ this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
1779
+ index = this.context.programs.length;
1780
+ child.index = index;
1781
+ child.name = 'program' + index;
1782
+ this.context.programs[index] = compiler.compile(child, options, this.context);
1783
+ this.context.environments[index] = child;
1784
+ } else {
1785
+ child.index = index;
1786
+ child.name = 'program' + index;
1787
+ }
1788
+ }
1789
+ },
1790
+ matchExistingProgram: function(child) {
1791
+ for (var i = 0, len = this.context.environments.length; i < len; i++) {
1792
+ var environment = this.context.environments[i];
1793
+ if (environment && environment.equals(child)) {
1794
+ return i;
1795
+ }
1651
1796
  }
1652
1797
  },
1653
1798
 
@@ -1691,57 +1836,111 @@ Handlebars.JavaScriptCompiler = function() {};
1691
1836
  },
1692
1837
 
1693
1838
  pushStackLiteral: function(item) {
1694
- this.compileStack.push(new Literal(item));
1695
- return item;
1839
+ return this.push(new Literal(item));
1696
1840
  },
1697
1841
 
1698
1842
  pushStack: function(item) {
1843
+ this.flushInline();
1844
+
1699
1845
  var stack = this.incrStack();
1700
- this.source.push(stack + " = " + item + ";");
1846
+ if (item) {
1847
+ this.source.push(stack + " = " + item + ";");
1848
+ }
1701
1849
  this.compileStack.push(stack);
1702
1850
  return stack;
1703
1851
  },
1704
1852
 
1705
1853
  replaceStack: function(callback) {
1706
- var stack = this.topStack(),
1707
- item = callback.call(this, stack);
1854
+ var prefix = '',
1855
+ inline = this.isInline(),
1856
+ stack;
1857
+
1858
+ // If we are currently inline then we want to merge the inline statement into the
1859
+ // replacement statement via ','
1860
+ if (inline) {
1861
+ var top = this.popStack(true);
1862
+
1863
+ if (top instanceof Literal) {
1864
+ // Literals do not need to be inlined
1865
+ stack = top.value;
1866
+ } else {
1867
+ // Get or create the current stack name for use by the inline
1868
+ var name = this.stackSlot ? this.topStackName() : this.incrStack();
1708
1869
 
1709
- // Prevent modification of the context depth variable. Through replaceStack
1710
- if (/^depth/.test(stack)) {
1711
- stack = this.nextStack();
1870
+ prefix = '(' + this.push(name) + ' = ' + top + '),';
1871
+ stack = this.topStack();
1872
+ }
1873
+ } else {
1874
+ stack = this.topStack();
1712
1875
  }
1713
1876
 
1714
- this.source.push(stack + " = " + item + ";");
1877
+ var item = callback.call(this, stack);
1878
+
1879
+ if (inline) {
1880
+ if (this.inlineStack.length || this.compileStack.length) {
1881
+ this.popStack();
1882
+ }
1883
+ this.push('(' + prefix + item + ')');
1884
+ } else {
1885
+ // Prevent modification of the context depth variable. Through replaceStack
1886
+ if (!/^stack/.test(stack)) {
1887
+ stack = this.nextStack();
1888
+ }
1889
+
1890
+ this.source.push(stack + " = (" + prefix + item + ");");
1891
+ }
1715
1892
  return stack;
1716
1893
  },
1717
1894
 
1718
- nextStack: function(skipCompileStack) {
1719
- var name = this.incrStack();
1720
- this.compileStack.push(name);
1721
- return name;
1895
+ nextStack: function() {
1896
+ return this.pushStack();
1722
1897
  },
1723
1898
 
1724
1899
  incrStack: function() {
1725
1900
  this.stackSlot++;
1726
1901
  if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1902
+ return this.topStackName();
1903
+ },
1904
+ topStackName: function() {
1727
1905
  return "stack" + this.stackSlot;
1728
1906
  },
1907
+ flushInline: function() {
1908
+ var inlineStack = this.inlineStack;
1909
+ if (inlineStack.length) {
1910
+ this.inlineStack = [];
1911
+ for (var i = 0, len = inlineStack.length; i < len; i++) {
1912
+ var entry = inlineStack[i];
1913
+ if (entry instanceof Literal) {
1914
+ this.compileStack.push(entry);
1915
+ } else {
1916
+ this.pushStack(entry);
1917
+ }
1918
+ }
1919
+ }
1920
+ },
1921
+ isInline: function() {
1922
+ return this.inlineStack.length;
1923
+ },
1729
1924
 
1730
- popStack: function() {
1731
- var item = this.compileStack.pop();
1925
+ popStack: function(wrapped) {
1926
+ var inline = this.isInline(),
1927
+ item = (inline ? this.inlineStack : this.compileStack).pop();
1732
1928
 
1733
- if (item instanceof Literal) {
1929
+ if (!wrapped && (item instanceof Literal)) {
1734
1930
  return item.value;
1735
1931
  } else {
1736
- this.stackSlot--;
1932
+ if (!inline) {
1933
+ this.stackSlot--;
1934
+ }
1737
1935
  return item;
1738
1936
  }
1739
1937
  },
1740
1938
 
1741
- topStack: function() {
1742
- var item = this.compileStack[this.compileStack.length - 1];
1939
+ topStack: function(wrapped) {
1940
+ var stack = (this.isInline() ? this.inlineStack : this.compileStack),
1941
+ item = stack[stack.length - 1];
1743
1942
 
1744
- if (item instanceof Literal) {
1943
+ if (!wrapped && (item instanceof Literal)) {
1745
1944
  return item.value;
1746
1945
  } else {
1747
1946
  return item;
@@ -1756,23 +1955,23 @@ Handlebars.JavaScriptCompiler = function() {};
1756
1955
  .replace(/\r/g, '\\r') + '"';
1757
1956
  },
1758
1957
 
1759
- setupHelper: function(paramSize, name) {
1958
+ setupHelper: function(paramSize, name, missingParams) {
1760
1959
  var params = [];
1761
- this.setupParams(paramSize, params);
1960
+ this.setupParams(paramSize, params, missingParams);
1762
1961
  var foundHelper = this.nameLookup('helpers', name, 'helper');
1763
1962
 
1764
1963
  return {
1765
1964
  params: params,
1766
1965
  name: foundHelper,
1767
1966
  callParams: ["depth0"].concat(params).join(", "),
1768
- helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
1967
+ helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
1769
1968
  };
1770
1969
  },
1771
1970
 
1772
1971
  // the params and contexts arguments are passed in arrays
1773
1972
  // to fill in
1774
- setupParams: function(paramSize, params) {
1775
- var options = [], contexts = [], param, inverse, program;
1973
+ setupParams: function(paramSize, params, useRegister) {
1974
+ var options = [], contexts = [], types = [], param, inverse, program;
1776
1975
 
1777
1976
  options.push("hash:" + this.popStack());
1778
1977
 
@@ -1801,19 +2000,28 @@ Handlebars.JavaScriptCompiler = function() {};
1801
2000
  params.push(param);
1802
2001
 
1803
2002
  if(this.options.stringParams) {
2003
+ types.push(this.popStack());
1804
2004
  contexts.push(this.popStack());
1805
2005
  }
1806
2006
  }
1807
2007
 
1808
2008
  if (this.options.stringParams) {
1809
2009
  options.push("contexts:[" + contexts.join(",") + "]");
2010
+ options.push("types:[" + types.join(",") + "]");
2011
+ options.push("hashTypes:hashTypes");
1810
2012
  }
1811
2013
 
1812
2014
  if(this.options.data) {
1813
2015
  options.push("data:data");
1814
2016
  }
1815
2017
 
1816
- params.push("{" + options.join(",") + "}");
2018
+ options = "{" + options.join(",") + "}";
2019
+ if (useRegister) {
2020
+ this.register('options', options);
2021
+ params.push('options');
2022
+ } else {
2023
+ params.push(options);
2024
+ }
1817
2025
  return params.join(", ");
1818
2026
  }
1819
2027
  };
@@ -1,3 +1,27 @@
1
+ /*
2
+
3
+ Copyright (C) 2011 by Yehuda Katz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ */
24
+
1
25
  // lib/handlebars/base.js
2
26
 
3
27
  /*jshint eqnull:true*/
@@ -5,7 +29,7 @@ this.Handlebars = {};
5
29
 
6
30
  (function(Handlebars) {
7
31
 
8
- Handlebars.VERSION = "1.0.rc.1";
32
+ Handlebars.VERSION = "1.0.rc.2";
9
33
 
10
34
  Handlebars.helpers = {};
11
35
  Handlebars.partials = {};
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: handlebars_assets
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-16 00:00:00.000000000 Z
12
+ date: 2013-01-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: execjs