handlebars_assets 0.8.2 → 0.9.0

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