handlebars 0.4.0 → 0.5.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +1 -2
  3. data/README.mdown +15 -7
  4. data/handlebars.gemspec +2 -5
  5. data/lib/handlebars.rb +5 -5
  6. data/lib/handlebars/context.rb +3 -12
  7. data/lib/handlebars/version.rb +1 -1
  8. data/spec/handlebars_spec.rb +1 -1
  9. metadata +47 -58
  10. data/.gitmodules +0 -3
  11. data/vendor/handlebars/.gitignore +0 -8
  12. data/vendor/handlebars/.jshintrc +0 -52
  13. data/vendor/handlebars/.npmignore +0 -10
  14. data/vendor/handlebars/.rspec +0 -1
  15. data/vendor/handlebars/Gemfile +0 -5
  16. data/vendor/handlebars/LICENSE +0 -19
  17. data/vendor/handlebars/README.markdown +0 -317
  18. data/vendor/handlebars/Rakefile +0 -109
  19. data/vendor/handlebars/bench/benchwarmer.js +0 -149
  20. data/vendor/handlebars/bench/handlebars.js +0 -172
  21. data/vendor/handlebars/bin/handlebars +0 -193
  22. data/vendor/handlebars/dist/handlebars.js +0 -2201
  23. data/vendor/handlebars/dist/handlebars.runtime.js +0 -321
  24. data/vendor/handlebars/lib/handlebars.js +0 -14
  25. data/vendor/handlebars/lib/handlebars/base.js +0 -154
  26. data/vendor/handlebars/lib/handlebars/compiler/ast.js +0 -133
  27. data/vendor/handlebars/lib/handlebars/compiler/base.js +0 -21
  28. data/vendor/handlebars/lib/handlebars/compiler/compiler.js +0 -1267
  29. data/vendor/handlebars/lib/handlebars/compiler/index.js +0 -7
  30. data/vendor/handlebars/lib/handlebars/compiler/printer.js +0 -131
  31. data/vendor/handlebars/lib/handlebars/compiler/visitor.js +0 -13
  32. data/vendor/handlebars/lib/handlebars/runtime.js +0 -88
  33. data/vendor/handlebars/lib/handlebars/utils.js +0 -67
  34. data/vendor/handlebars/package.json +0 -35
  35. data/vendor/handlebars/spec/acceptance_spec.rb +0 -101
  36. data/vendor/handlebars/spec/parser_spec.rb +0 -433
  37. data/vendor/handlebars/spec/qunit_spec.js +0 -1370
  38. data/vendor/handlebars/spec/spec_helper.rb +0 -157
  39. data/vendor/handlebars/spec/tokenizer_spec.rb +0 -301
  40. data/vendor/handlebars/src/handlebars.l +0 -53
  41. data/vendor/handlebars/src/handlebars.yy +0 -109
  42. data/vendor/handlebars/src/parser-prefix.js +0 -1
  43. data/vendor/handlebars/src/parser-suffix.js +0 -4
@@ -1,433 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe "Parser" do
4
- let(:handlebars) { @context["Handlebars"] }
5
-
6
- before(:all) do
7
- @compiles = true
8
- end
9
-
10
- def root(&block)
11
- ASTBuilder.build do
12
- instance_eval(&block)
13
- end
14
- end
15
-
16
- def ast_for(string)
17
- ast = handlebars.parse(string)
18
- handlebars.print(ast)
19
- end
20
-
21
- class ASTBuilder
22
- def self.build(&block)
23
- ret = new
24
- ret.evaluate(&block)
25
- ret.out
26
- end
27
-
28
- attr_reader :out
29
-
30
- def initialize
31
- @padding = 0
32
- @out = ""
33
- end
34
-
35
- def evaluate(&block)
36
- instance_eval(&block)
37
- end
38
-
39
- def pad(string)
40
- @out << (" " * @padding) + string + "\n"
41
- end
42
-
43
- def with_padding
44
- @padding += 1
45
- ret = yield
46
- @padding -= 1
47
- ret
48
- end
49
-
50
- def program
51
- pad("PROGRAM:")
52
- with_padding { yield }
53
- end
54
-
55
- def inverse
56
- pad("{{^}}")
57
- with_padding { yield }
58
- end
59
-
60
- def block
61
- pad("BLOCK:")
62
- with_padding { yield }
63
- end
64
-
65
- def inverted_block
66
- pad("INVERSE:")
67
- with_padding { yield }
68
- end
69
-
70
- def mustache(id, params = [], hash = nil)
71
- hash = " #{hash}" if hash
72
- pad("{{ #{id} [#{params.join(", ")}]#{hash} }}")
73
- end
74
-
75
- def partial(id, context = nil)
76
- content = id.dup
77
- content << " #{context}" if context
78
- pad("{{> #{content} }}")
79
- end
80
-
81
- def comment(comment)
82
- pad("{{! '#{comment}' }}")
83
- end
84
-
85
- def multiline_comment(comment)
86
- pad("{{! '\n#{comment}\n' }}")
87
- end
88
-
89
- def content(string)
90
- pad("CONTENT[ '#{string}' ]")
91
- end
92
-
93
- def string(string)
94
- string.inspect
95
- end
96
-
97
- def integer(string)
98
- "INTEGER{#{string}}"
99
- end
100
-
101
- def boolean(string)
102
- "BOOLEAN{#{string}}"
103
- end
104
-
105
- def hash(*pairs)
106
- "HASH{" + pairs.map {|k,v| "#{k}=#{v}" }.join(", ") + "}"
107
- end
108
-
109
- def id(id)
110
- "ID:#{id}"
111
- end
112
-
113
- def data(id)
114
- "@#{id}"
115
- end
116
-
117
- def partial_name(name)
118
- "PARTIAL:#{name}"
119
- end
120
-
121
- def path(*parts)
122
- "PATH:#{parts.join("/")}"
123
- end
124
- end
125
-
126
- it "parses simple mustaches" do
127
- ast_for("{{foo}}").should == root { mustache id("foo") }
128
- end
129
-
130
- it "parses simple mustaches with data" do
131
- ast_for("{{@foo}}").should == root { mustache data("foo") }
132
- end
133
-
134
- it "parses mustaches with paths" do
135
- ast_for("{{foo/bar}}").should == root { mustache path("foo", "bar") }
136
- end
137
-
138
- it "parses mustaches with this/foo" do
139
- ast_for("{{this/foo}}").should == root { mustache id("foo") }
140
- end
141
-
142
- it "parses mustaches with - in a path" do
143
- ast_for("{{foo-bar}}").should == root { mustache id("foo-bar") }
144
- end
145
-
146
- it "parses mustaches with parameters" do
147
- ast_for("{{foo bar}}").should == root { mustache id("foo"), [id("bar")] }
148
- end
149
-
150
- it "parses mustaches with hash arguments" do
151
- ast_for("{{foo bar=baz}}").should == root do
152
- mustache id("foo"), [], hash(["bar", id("baz")])
153
- end
154
-
155
- ast_for("{{foo bar=1}}").should == root do
156
- mustache id("foo"), [], hash(["bar", integer("1")])
157
- end
158
-
159
- ast_for("{{foo bar=true}}").should == root do
160
- mustache id("foo"), [], hash(["bar", boolean("true")])
161
- end
162
-
163
- ast_for("{{foo bar=false}}").should == root do
164
- mustache id("foo"), [], hash(["bar", boolean("false")])
165
- end
166
-
167
- ast_for("{{foo bar=@baz}}").should == root do
168
- mustache id("foo"), [], hash(["bar", data("baz")])
169
- end
170
-
171
- ast_for("{{foo bar=baz bat=bam}}").should == root do
172
- mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "ID:bam"])
173
- end
174
-
175
- ast_for("{{foo bar=baz bat=\"bam\"}}").should == root do
176
- mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "\"bam\""])
177
- end
178
-
179
- ast_for("{{foo bat='bam'}}").should == root do
180
- mustache id("foo"), [], hash(["bat", "\"bam\""])
181
- end
182
-
183
- ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == root do
184
- mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")])
185
- end
186
-
187
- ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == root do
188
- mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", integer("1")])
189
- end
190
-
191
- ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == root do
192
- mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("true")])
193
- end
194
-
195
- ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == root do
196
- mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("false")])
197
- end
198
- end
199
-
200
- it "parses mustaches with string parameters" do
201
- ast_for("{{foo bar \"baz\" }}").should == root { mustache id("foo"), [id("bar"), string("baz")] }
202
- end
203
-
204
- it "parses mustaches with INTEGER parameters" do
205
- ast_for("{{foo 1}}").should == root { mustache id("foo"), [integer("1")] }
206
- end
207
-
208
- it "parses mustaches with BOOLEAN parameters" do
209
- ast_for("{{foo true}}").should == root { mustache id("foo"), [boolean("true")] }
210
- ast_for("{{foo false}}").should == root { mustache id("foo"), [boolean("false")] }
211
- end
212
-
213
- it "parses mutaches with DATA parameters" do
214
- ast_for("{{foo @bar}}").should == root { mustache id("foo"), [data("bar")] }
215
- end
216
-
217
- it "parses contents followed by a mustache" do
218
- ast_for("foo bar {{baz}}").should == root do
219
- content "foo bar "
220
- mustache id("baz")
221
- end
222
- end
223
-
224
- it "parses a partial" do
225
- ast_for("{{> foo }}").should == root { partial partial_name("foo") }
226
- end
227
-
228
- it "parses a partial with context" do
229
- ast_for("{{> foo bar}}").should == root { partial partial_name("foo"), id("bar") }
230
- end
231
-
232
- it "parses a partial with a complex name" do
233
- ast_for("{{> shared/partial}}").should == root { partial partial_name("shared/partial") }
234
- end
235
-
236
- it "parses a comment" do
237
- ast_for("{{! this is a comment }}").should == root do
238
- comment " this is a comment "
239
- end
240
- end
241
-
242
- it "parses a multi-line comment" do
243
- ast_for("{{!\nthis is a multi-line comment\n}}").should == root do
244
- multiline_comment "this is a multi-line comment"
245
- end
246
- end
247
-
248
- it "parses an inverse section" do
249
- ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == root do
250
- block do
251
- mustache id("foo")
252
-
253
- program do
254
- content " bar "
255
- end
256
-
257
- inverse do
258
- content " baz "
259
- end
260
- end
261
- end
262
- end
263
-
264
- it "parses an inverse ('else'-style) section" do
265
- ast_for("{{#foo}} bar {{else}} baz {{/foo}}").should == root do
266
- block do
267
- mustache id("foo")
268
-
269
- program do
270
- content " bar "
271
- end
272
-
273
- inverse do
274
- content " baz "
275
- end
276
- end
277
- end
278
- end
279
-
280
- it "parses empty blocks" do
281
- ast_for("{{#foo}}{{/foo}}").should == root do
282
- block do
283
- mustache id("foo")
284
-
285
- program do
286
- # empty program
287
- end
288
- end
289
- end
290
- end
291
-
292
- it "parses empty blocks with empty inverse section" do
293
- ast_for("{{#foo}}{{^}}{{/foo}}").should == root do
294
- block do
295
- mustache id("foo")
296
-
297
- program do
298
- # empty program
299
- end
300
-
301
- inverse do
302
- # empty inverse
303
- end
304
- end
305
- end
306
- end
307
-
308
- it "parses empty blocks with empty inverse ('else'-style) section" do
309
- ast_for("{{#foo}}{{else}}{{/foo}}").should == root do
310
- block do
311
- mustache id("foo")
312
-
313
- program do
314
- # empty program
315
- end
316
-
317
- inverse do
318
- # empty inverse
319
- end
320
- end
321
- end
322
- end
323
-
324
- it "parses non-empty blocks with empty inverse section" do
325
- ast_for("{{#foo}} bar {{^}}{{/foo}}").should == root do
326
- block do
327
- mustache id("foo")
328
-
329
- program do
330
- content " bar "
331
- end
332
-
333
- inverse do
334
- # empty inverse
335
- end
336
- end
337
- end
338
- end
339
-
340
- it "parses non-empty blocks with empty inverse ('else'-style) section" do
341
- ast_for("{{#foo}} bar {{else}}{{/foo}}").should == root do
342
- block do
343
- mustache id("foo")
344
-
345
- program do
346
- content " bar "
347
- end
348
-
349
- inverse do
350
- # empty inverse
351
- end
352
- end
353
- end
354
- end
355
-
356
- it "parses empty blocks with non-empty inverse section" do
357
- ast_for("{{#foo}}{{^}} bar {{/foo}}").should == root do
358
- block do
359
- mustache id("foo")
360
-
361
- program do
362
- # empty program
363
- end
364
-
365
- inverse do
366
- content " bar "
367
- end
368
- end
369
- end
370
- end
371
-
372
- it "parses empty blocks with non-empty inverse ('else'-style) section" do
373
- ast_for("{{#foo}}{{else}} bar {{/foo}}").should == root do
374
- block do
375
- mustache id("foo")
376
-
377
- program do
378
- # empty program
379
- end
380
-
381
- inverse do
382
- content " bar "
383
- end
384
- end
385
- end
386
- end
387
-
388
- it "parses a standalone inverse section" do
389
- ast_for("{{^foo}}bar{{/foo}}").should == root do
390
- block do
391
- mustache id("foo")
392
-
393
- inverse do
394
- content "bar"
395
- end
396
- end
397
- end
398
- end
399
-
400
- it "raises if there's a Parse error" do
401
- lambda { ast_for("{{foo}") }.should raise_error(V8::JSError, /Parse error on line 1/)
402
- lambda { ast_for("{{foo &}}")}.should raise_error(V8::JSError, /Parse error on line 1/)
403
- lambda { ast_for("{{#goodbyes}}{{/hellos}}") }.should raise_error(V8::JSError, /goodbyes doesn't match hellos/)
404
- end
405
-
406
- it "knows how to report the correct line number in errors" do
407
- lambda { ast_for("hello\nmy\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 3/m)
408
- lambda { ast_for("hello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 5/m)
409
- end
410
-
411
- it "knows how to report the correct line number in errors when the first character is a newline" do
412
- lambda { ast_for("\n\nhello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 7/m)
413
- end
414
-
415
- context "externally compiled AST" do
416
-
417
- it "can pass through an already-compiled AST" do
418
- ast_for(@context.eval('new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]);')).should == root do
419
- content "Hello"
420
- end
421
- end
422
-
423
- it "can pass through an already-compiled AST via compile/precompile" do
424
- @context = Handlebars::Spec::FULL_CONTEXT
425
-
426
- code = 'Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))();'
427
- @context.eval(code).should == "Hello"
428
-
429
- code = @context.eval 'Handlebars.precompile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))'
430
- @context.eval("(#{code})(this)").should == "Hello"
431
- end
432
- end
433
- end
@@ -1,1370 +0,0 @@
1
- var Handlebars;
2
- if (!Handlebars) {
3
- // Setup for Node package testing
4
- Handlebars = require('../lib/handlebars');
5
-
6
- var assert = require("assert"),
7
-
8
- equal = assert.equal,
9
- equals = assert.equal,
10
- ok = assert.ok;
11
-
12
- // Note that this doesn't have the same context separation as the rspec test.
13
- // Both should be run for full acceptance of the two libary modes.
14
- var CompilerContext = {
15
- compile: function(template, options) {
16
- var templateSpec = Handlebars.precompile(template, options);
17
- return Handlebars.template(eval('(' + templateSpec + ')'));
18
- },
19
- compileWithPartial: function(template, options) {
20
- return Handlebars.compile(template, options);
21
- }
22
- };
23
- } else {
24
- var _equal = equal;
25
- equals = equal = function(a, b, msg) {
26
- // Allow exec with missing message params
27
- _equal(a, b, msg || '');
28
- };
29
- }
30
-
31
- suite("basic context");
32
-
33
- function shouldCompileTo(string, hashOrArray, expected, message) {
34
- shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
35
- }
36
-
37
- function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) {
38
- var result = compileWithPartials(string, hashOrArray, partials);
39
- equal(result, expected, "'" + expected + "' should === '" + result + "': " + message);
40
- }
41
-
42
- function compileWithPartials(string, hashOrArray, partials) {
43
- var template = CompilerContext[partials ? 'compileWithPartial' : 'compile'](string), ary;
44
- if(Object.prototype.toString.call(hashOrArray) === "[object Array]") {
45
- var helpers = hashOrArray[1];
46
-
47
- if(helpers) {
48
- for(var prop in Handlebars.helpers) {
49
- helpers[prop] = helpers[prop] || Handlebars.helpers[prop];
50
- }
51
- }
52
-
53
- ary = [];
54
- ary.push(hashOrArray[0]);
55
- ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
56
- } else {
57
- ary = [hashOrArray];
58
- }
59
-
60
- return template.apply(this, ary);
61
- }
62
-
63
- function shouldThrow(fn, exception, message) {
64
- var caught = false,
65
- exType, exMessage;
66
-
67
- if (exception instanceof Array) {
68
- exType = exception[0];
69
- exMessage = exception[1];
70
- } else if (typeof exception === 'string') {
71
- exType = Error;
72
- exMessage = exception;
73
- } else {
74
- exType = exception;
75
- }
76
-
77
- try {
78
- fn();
79
- }
80
- catch (e) {
81
- if (e instanceof exType) {
82
- if (!exMessage || e.message === exMessage) {
83
- caught = true;
84
- }
85
- }
86
- }
87
-
88
- ok(caught, message || null);
89
- }
90
-
91
- test("most basic", function() {
92
- shouldCompileTo("{{foo}}", { foo: "foo" }, "foo");
93
- });
94
-
95
- test("compiling with a basic context", function() {
96
- shouldCompileTo("Goodbye\n{{cruel}}\n{{world}}!", {cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
97
- "It works if all the required keys are provided");
98
- });
99
-
100
- test("comments", function() {
101
- shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
102
- {cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
103
- "comments are ignored");
104
- });
105
-
106
- test("boolean", function() {
107
- var string = "{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!";
108
- shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
109
- "booleans show the contents when true");
110
-
111
- shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
112
- "booleans do not show the contents when false");
113
- });
114
-
115
- test("zeros", function() {
116
- shouldCompileTo("num1: {{num1}}, num2: {{num2}}", {num1: 42, num2: 0},
117
- "num1: 42, num2: 0");
118
- shouldCompileTo("num: {{.}}", 0, "num: 0");
119
- shouldCompileTo("num: {{num1/num2}}", {num1: {num2: 0}}, "num: 0");
120
- });
121
-
122
- test("newlines", function() {
123
- shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
124
- shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
125
- });
126
-
127
- test("escaping text", function() {
128
- shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes");
129
- shouldCompileTo("Awesome\\", {}, "Awesome\\", "text is escaped so that the closing quote can't be ignored");
130
- shouldCompileTo("Awesome\\\\ foo", {}, "Awesome\\\\ foo", "text is escaped so that it doesn't mess up backslashes");
131
- shouldCompileTo("Awesome {{foo}}", {foo: '\\'}, "Awesome \\", "text is escaped so that it doesn't mess up backslashes");
132
- shouldCompileTo(' " " ', {}, ' " " ', "double quotes never produce invalid javascript");
133
- });
134
-
135
- test("escaping expressions", function() {
136
- shouldCompileTo("{{{awesome}}}", {awesome: "&\"\\<>"}, '&\"\\<>',
137
- "expressions with 3 handlebars aren't escaped");
138
-
139
- shouldCompileTo("{{&awesome}}", {awesome: "&\"\\<>"}, '&\"\\<>',
140
- "expressions with {{& handlebars aren't escaped");
141
-
142
- shouldCompileTo("{{awesome}}", {awesome: "&\"'`\\<>"}, '&amp;&quot;&#x27;&#x60;\\&lt;&gt;',
143
- "by default expressions should be escaped");
144
-
145
- shouldCompileTo("{{awesome}}", {awesome: "Escaped, <b> looks like: &lt;b&gt;"}, 'Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;gt;',
146
- "escaping should properly handle amperstands");
147
- });
148
-
149
- test("functions returning safestrings shouldn't be escaped", function() {
150
- var hash = {awesome: function() { return new Handlebars.SafeString("&\"\\<>"); }};
151
- shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
152
- "functions returning safestrings aren't escaped");
153
- });
154
-
155
- test("functions", function() {
156
- shouldCompileTo("{{awesome}}", {awesome: function() { return "Awesome"; }}, "Awesome",
157
- "functions are called and render their output");
158
- shouldCompileTo("{{awesome}}", {awesome: function() { return this.more; }, more: "More awesome"}, "More awesome",
159
- "functions are bound to the context");
160
- });
161
-
162
- test("paths with hyphens", function() {
163
- shouldCompileTo("{{foo-bar}}", {"foo-bar": "baz"}, "baz", "Paths can contain hyphens (-)");
164
- shouldCompileTo("{{foo.foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
165
- shouldCompileTo("{{foo/foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
166
- });
167
-
168
- test("nested paths", function() {
169
- shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: "beautiful"}},
170
- "Goodbye beautiful world!", "Nested paths access nested objects");
171
- });
172
-
173
- test("nested paths with empty string value", function() {
174
- shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: ""}},
175
- "Goodbye world!", "Nested paths access nested objects with empty string");
176
- });
177
-
178
- test("literal paths", function() {
179
- shouldCompileTo("Goodbye {{[@alan]/expression}} world!", {"@alan": {expression: "beautiful"}},
180
- "Goodbye beautiful world!", "Literal paths can be used");
181
- shouldCompileTo("Goodbye {{[foo bar]/expression}} world!", {"foo bar": {expression: "beautiful"}},
182
- "Goodbye beautiful world!", "Literal paths can be used");
183
- });
184
-
185
- test('literal references', function() {
186
- shouldCompileTo("Goodbye {{[foo bar]}} world!", {"foo bar": "beautiful"},
187
- "Goodbye beautiful world!", "Literal paths can be used");
188
- });
189
-
190
- test("that current context path ({{.}}) doesn't hit helpers", function() {
191
- shouldCompileTo("test: {{.}}", [null, {helper: "awesome"}], "test: ");
192
- });
193
-
194
- test("complex but empty paths", function() {
195
- shouldCompileTo("{{person/name}}", {person: {name: null}}, "");
196
- shouldCompileTo("{{person/name}}", {person: {}}, "");
197
- });
198
-
199
- test("this keyword in paths", function() {
200
- var string = "{{#goodbyes}}{{this}}{{/goodbyes}}";
201
- var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
202
- shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
203
- "This keyword in paths evaluates to current context");
204
-
205
- string = "{{#hellos}}{{this/text}}{{/hellos}}";
206
- hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
207
- shouldCompileTo(string, hash, "helloHelloHELLO", "This keyword evaluates in more complex paths");
208
- });
209
-
210
- test("this keyword nested inside path", function() {
211
- var string = "{{#hellos}}{{text/this/foo}}{{/hellos}}";
212
- shouldThrow(function() {
213
- CompilerContext.compile(string);
214
- }, Error, "Should throw exception");
215
- });
216
-
217
- test("this keyword in helpers", function() {
218
- var helpers = {foo: function(value) {
219
- return 'bar ' + value;
220
- }};
221
- var string = "{{#goodbyes}}{{foo this}}{{/goodbyes}}";
222
- var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
223
- shouldCompileTo(string, [hash, helpers], "bar goodbyebar Goodbyebar GOODBYE",
224
- "This keyword in paths evaluates to current context");
225
-
226
- string = "{{#hellos}}{{foo this/text}}{{/hellos}}";
227
- hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
228
- shouldCompileTo(string, [hash, helpers], "bar hellobar Hellobar HELLO", "This keyword evaluates in more complex paths");
229
- });
230
-
231
- test("this keyword nested inside helpers param", function() {
232
- var string = "{{#hellos}}{{foo text/this/foo}}{{/hellos}}";
233
- shouldThrow(function() {
234
- CompilerContext.compile(string);
235
- }, Error, "Should throw exception");
236
- });
237
-
238
- suite("inverted sections");
239
-
240
- test("inverted sections with unset value", function() {
241
- var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
242
- var hash = {};
243
- shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value isn't set.");
244
- });
245
-
246
- test("inverted section with false value", function() {
247
- var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
248
- var hash = {goodbyes: false};
249
- shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is false.");
250
- });
251
-
252
- test("inverted section with empty set", function() {
253
- var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
254
- var hash = {goodbyes: []};
255
- shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is empty set.");
256
- });
257
-
258
- suite("blocks");
259
-
260
- test("array", function() {
261
- var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
262
- var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
263
- shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
264
- "Arrays iterate over the contents when not empty");
265
-
266
- shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
267
- "Arrays ignore the contents when empty");
268
-
269
- });
270
-
271
- test("array with @index", function() {
272
- var string = "{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!";
273
- var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
274
-
275
- var template = CompilerContext.compile(string);
276
- var result = template(hash);
277
-
278
- equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
279
- });
280
-
281
- test("empty block", function() {
282
- var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!";
283
- var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
284
- shouldCompileTo(string, hash, "cruel world!",
285
- "Arrays iterate over the contents when not empty");
286
-
287
- shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
288
- "Arrays ignore the contents when empty");
289
- });
290
-
291
- test("nested iteration", function() {
292
-
293
- });
294
-
295
- test("block with complex lookup", function() {
296
- var string = "{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}";
297
- var hash = {name: "Alan", goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}]};
298
-
299
- shouldCompileTo(string, hash, "goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ",
300
- "Templates can access variables in contexts up the stack with relative path syntax");
301
- });
302
-
303
- test("block with complex lookup using nested context", function() {
304
- var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}";
305
-
306
- shouldThrow(function() {
307
- CompilerContext.compile(string);
308
- }, Error, "Should throw exception");
309
- });
310
-
311
- test("helper with complex lookup$", function() {
312
- var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}";
313
- var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]};
314
- var helpers = {link: function(prefix) {
315
- return "<a href='" + prefix + "/" + this.url + "'>" + this.text + "</a>";
316
- }};
317
- shouldCompileTo(string, [hash, helpers], "<a href='/root/goodbye'>Goodbye</a>");
318
- });
319
-
320
- test("helper block with complex lookup expression", function() {
321
- var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}";
322
- var hash = {name: "Alan"};
323
- var helpers = {goodbyes: function(options) {
324
- var out = "";
325
- var byes = ["Goodbye", "goodbye", "GOODBYE"];
326
- for (var i = 0,j = byes.length; i < j; i++) {
327
- out += byes[i] + " " + options.fn(this) + "! ";
328
- }
329
- return out;
330
- }};
331
- shouldCompileTo(string, [hash, helpers], "Goodbye Alan! goodbye Alan! GOODBYE Alan! ");
332
- });
333
-
334
- test("helper with complex lookup and nested template", function() {
335
- var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
336
- var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
337
- var helpers = {link: function (prefix, options) {
338
- return "<a href='" + prefix + "/" + this.url + "'>" + options.fn(this) + "</a>";
339
- }};
340
- shouldCompileToWithPartials(string, [hash, helpers], false, "<a href='/root/goodbye'>Goodbye</a>");
341
- });
342
-
343
- test("helper with complex lookup and nested template in VM+Compiler", function() {
344
- var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
345
- var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
346
- var helpers = {link: function (prefix, options) {
347
- return "<a href='" + prefix + "/" + this.url + "'>" + options.fn(this) + "</a>";
348
- }};
349
- shouldCompileToWithPartials(string, [hash, helpers], true, "<a href='/root/goodbye'>Goodbye</a>");
350
- });
351
-
352
- test("block with deep nested complex lookup", function() {
353
- var string = "{{#outer}}Goodbye {{#inner}}cruel {{../../omg}}{{/inner}}{{/outer}}";
354
- var hash = {omg: "OMG!", outer: [{ inner: [{ text: "goodbye" }] }] };
355
-
356
- shouldCompileTo(string, hash, "Goodbye cruel OMG!");
357
- });
358
-
359
- test("block helper", function() {
360
- var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
361
- var template = CompilerContext.compile(string);
362
-
363
- var result = template({world: "world"}, { helpers: {goodbyes: function(options) { return options.fn({text: "GOODBYE"}); }}});
364
- equal(result, "GOODBYE! cruel world!", "Block helper executed");
365
- });
366
-
367
- test("block helper staying in the same context", function() {
368
- var string = "{{#form}}<p>{{name}}</p>{{/form}}";
369
- var template = CompilerContext.compile(string);
370
-
371
- var result = template({name: "Yehuda"}, {helpers: {form: function(options) { return "<form>" + options.fn(this) + "</form>"; } }});
372
- equal(result, "<form><p>Yehuda</p></form>", "Block helper executed with current context");
373
- });
374
-
375
- test("block helper should have context in this", function() {
376
- var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
377
- var link = function(options) {
378
- return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
379
- };
380
- var data = { "people": [
381
- { "name": "Alan", "id": 1 },
382
- { "name": "Yehuda", "id": 2 }
383
- ]};
384
-
385
- shouldCompileTo(source, [data, {link: link}], "<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>");
386
- });
387
-
388
- test("block helper for undefined value", function() {
389
- shouldCompileTo("{{#empty}}shouldn't render{{/empty}}", {}, "");
390
- });
391
-
392
- test("block helper passing a new context", function() {
393
- var string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}";
394
- var template = CompilerContext.compile(string);
395
-
396
- var result = template({yehuda: {name: "Yehuda"}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
397
- equal(result, "<form><p>Yehuda</p></form>", "Context variable resolved");
398
- });
399
-
400
- test("block helper passing a complex path context", function() {
401
- var string = "{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}";
402
- var template = CompilerContext.compile(string);
403
-
404
- var result = template({yehuda: {name: "Yehuda", cat: {name: "Harold"}}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
405
- equal(result, "<form><p>Harold</p></form>", "Complex path variable resolved");
406
- });
407
-
408
- test("nested block helpers", function() {
409
- var string = "{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}";
410
- var template = CompilerContext.compile(string);
411
-
412
- var result = template({
413
- yehuda: {name: "Yehuda" }
414
- }, {
415
- helpers: {
416
- link: function(options) { return "<a href='" + this.name + "'>" + options.fn(this) + "</a>"; },
417
- form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }
418
- }
419
- });
420
- equal(result, "<form><p>Yehuda</p><a href='Yehuda'>Hello</a></form>", "Both blocks executed");
421
- });
422
-
423
- test("block inverted sections", function() {
424
- shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people"},
425
- "No people");
426
- });
427
-
428
- test("block inverted sections with empty arrays", function() {
429
- shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people", people: []},
430
- "No people");
431
- });
432
-
433
- test("block helper inverted sections", function() {
434
- var string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
435
- var list = function(context, options) {
436
- if (context.length > 0) {
437
- var out = "<ul>";
438
- for(var i = 0,j=context.length; i < j; i++) {
439
- out += "<li>";
440
- out += options.fn(context[i]);
441
- out += "</li>";
442
- }
443
- out += "</ul>";
444
- return out;
445
- } else {
446
- return "<p>" + options.inverse(this) + "</p>";
447
- }
448
- };
449
-
450
- var hash = {people: [{name: "Alan"}, {name: "Yehuda"}]};
451
- var empty = {people: []};
452
- var rootMessage = {
453
- people: [],
454
- message: "Nobody's here"
455
- };
456
-
457
- var messageString = "{{#list people}}Hello{{^}}{{message}}{{/list}}";
458
-
459
- // the meaning here may be kind of hard to catch, but list.not is always called,
460
- // so we should see the output of both
461
- shouldCompileTo(string, [hash, { list: list }], "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
462
- shouldCompileTo(string, [empty, { list: list }], "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
463
- shouldCompileTo(messageString, [rootMessage, { list: list }], "<p>Nobody&#x27;s here</p>", "the context of an inverse is the parent of the block");
464
- });
465
-
466
- suite("helpers hash");
467
-
468
- test("providing a helpers hash", function() {
469
- shouldCompileTo("Goodbye {{cruel}} {{world}}!", [{cruel: "cruel"}, {world: function() { return "world"; }}], "Goodbye cruel world!",
470
- "helpers hash is available");
471
-
472
- shouldCompileTo("Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!", [{iter: [{cruel: "cruel"}]}, {world: function() { return "world"; }}],
473
- "Goodbye cruel world!", "helpers hash is available inside other blocks");
474
- });
475
-
476
- test("in cases of conflict, helpers win", function() {
477
- shouldCompileTo("{{{lookup}}}", [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], "helpers",
478
- "helpers hash has precedence escaped expansion");
479
- shouldCompileTo("{{lookup}}", [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], "helpers",
480
- "helpers hash has precedence simple expansion");
481
- });
482
-
483
- test("the helpers hash is available is nested contexts", function() {
484
- shouldCompileTo("{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}",
485
- [{'outer': {'inner': {'unused':[]}}}, {'helper': function() { return 'helper'; }}], "helper",
486
- "helpers hash is available in nested contexts.");
487
- });
488
-
489
- suite("partials");
490
-
491
- test("basic partials", function() {
492
- var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
493
- var partial = "{{name}} ({{url}}) ";
494
- var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
495
- shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
496
- "Basic partials output based on current context.");
497
- });
498
-
499
- test("partials with context", function() {
500
- var string = "Dudes: {{>dude dudes}}";
501
- var partial = "{{#this}}{{name}} ({{url}}) {{/this}}";
502
- var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
503
- shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
504
- "Partials can be passed a context");
505
- });
506
-
507
- test("partial in a partial", function() {
508
- var string = "Dudes: {{#dudes}}{{>dude}}{{/dudes}}";
509
- var dude = "{{name}} {{> url}} ";
510
- var url = "<a href='{{url}}'>{{url}}</a>";
511
- var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
512
- shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, "Dudes: Yehuda <a href='http://yehuda'>http://yehuda</a> Alan <a href='http://alan'>http://alan</a> ", "Partials are rendered inside of other partials");
513
- });
514
-
515
- test("rendering undefined partial throws an exception", function() {
516
- shouldThrow(function() {
517
- var template = CompilerContext.compile("{{> whatever}}");
518
- template();
519
- }, [Handlebars.Exception, 'The partial whatever could not be found'], "Should throw exception");
520
- });
521
-
522
- test("rendering template partial in vm mode throws an exception", function() {
523
- shouldThrow(function() {
524
- var template = CompilerContext.compile("{{> whatever}}");
525
- template();
526
- }, [Handlebars.Exception, 'The partial whatever could not be found'], "Should throw exception");
527
- });
528
-
529
- test("rendering function partial in vm mode", function() {
530
- var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
531
- var partial = function(context) {
532
- return context.name + ' (' + context.url + ') ';
533
- };
534
- var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
535
- shouldCompileTo(string, [hash, {}, {dude: partial}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
536
- "Function partials output based in VM.");
537
- });
538
-
539
- test("GH-14: a partial preceding a selector", function() {
540
- var string = "Dudes: {{>dude}} {{another_dude}}";
541
- var dude = "{{name}}";
542
- var hash = {name:"Jeepers", another_dude:"Creepers"};
543
- shouldCompileToWithPartials(string, [hash, {}, {dude:dude}], true, "Dudes: Jeepers Creepers", "Regular selectors can follow a partial");
544
- });
545
-
546
- test("Partials with slash paths", function() {
547
- var string = "Dudes: {{> shared/dude}}";
548
- var dude = "{{name}}";
549
- var hash = {name:"Jeepers", another_dude:"Creepers"};
550
- shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
551
- });
552
-
553
- test("Partials with integer path", function() {
554
- var string = "Dudes: {{> 404}}";
555
- var dude = "{{name}}";
556
- var hash = {name:"Jeepers", another_dude:"Creepers"};
557
- shouldCompileToWithPartials(string, [hash, {}, {404:dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
558
- });
559
-
560
-
561
- suite("String literal parameters");
562
-
563
- test("simple literals work", function() {
564
- var string = 'Message: {{hello "world" 12 true false}}';
565
- var hash = {};
566
- var helpers = {hello: function(param, times, bool1, bool2) {
567
- if(typeof times !== 'number') { times = "NaN"; }
568
- if(typeof bool1 !== 'boolean') { bool1 = "NaB"; }
569
- if(typeof bool2 !== 'boolean') { bool2 = "NaB"; }
570
- return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2;
571
- }};
572
- shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal");
573
- });
574
-
575
- test("using a quote in the middle of a parameter raises an error", function() {
576
- shouldThrow(function() {
577
- var string = 'Message: {{hello wo"rld"}}';
578
- CompilerContext.compile(string);
579
- }, Error, "should throw exception");
580
- });
581
-
582
- test("escaping a String is possible", function(){
583
- var string = 'Message: {{{hello "\\"world\\""}}}';
584
- var hash = {};
585
- var helpers = {hello: function(param) { return "Hello " + param; }};
586
- shouldCompileTo(string, [hash, helpers], "Message: Hello \"world\"", "template with an escaped String literal");
587
- });
588
-
589
- test("it works with ' marks", function() {
590
- var string = 'Message: {{{hello "Alan\'s world"}}}';
591
- var hash = {};
592
- var helpers = {hello: function(param) { return "Hello " + param; }};
593
- shouldCompileTo(string, [hash, helpers], "Message: Hello Alan's world", "template with a ' mark");
594
- });
595
-
596
- suite("multiple parameters");
597
-
598
- test("simple multi-params work", function() {
599
- var string = 'Message: {{goodbye cruel world}}';
600
- var hash = {cruel: "cruel", world: "world"};
601
- var helpers = {goodbye: function(cruel, world) { return "Goodbye " + cruel + " " + world; }};
602
- shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "regular helpers with multiple params");
603
- });
604
-
605
- test("block multi-params work", function() {
606
- var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}';
607
- var hash = {cruel: "cruel", world: "world"};
608
- var helpers = {goodbye: function(cruel, world, options) {
609
- return options.fn({greeting: "Goodbye", adj: cruel, noun: world});
610
- }};
611
- shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "block helpers with multiple params");
612
- });
613
-
614
- suite("safestring");
615
-
616
- test("constructing a safestring from a string and checking its type", function() {
617
- var safe = new Handlebars.SafeString("testing 1, 2, 3");
618
- ok(safe instanceof Handlebars.SafeString, "SafeString is an instance of Handlebars.SafeString");
619
- equal(safe, "testing 1, 2, 3", "SafeString is equivalent to its underlying string");
620
- });
621
-
622
- suite("helperMissing");
623
-
624
- test("if a context is not found, helperMissing is used", function() {
625
- shouldThrow(function() {
626
- var template = CompilerContext.compile("{{hello}} {{link_to world}}");
627
- template({});
628
- }, [Error, "Could not find property 'link_to'"], "Should throw exception");
629
- });
630
-
631
- test("if a context is not found, custom helperMissing is used", function() {
632
- var string = "{{hello}} {{link_to world}}";
633
- var context = { hello: "Hello", world: "world" };
634
-
635
- var helpers = {
636
- helperMissing: function(helper, context) {
637
- if(helper === "link_to") {
638
- return new Handlebars.SafeString("<a>" + context + "</a>");
639
- }
640
- }
641
- };
642
-
643
- shouldCompileTo(string, [context, helpers], "Hello <a>world</a>");
644
- });
645
-
646
- suite("knownHelpers");
647
-
648
- test("Known helper should render helper", function() {
649
- var template = CompilerContext.compile("{{hello}}", {knownHelpers: {"hello" : true}});
650
-
651
- var result = template({}, {helpers: {hello: function() { return "foo"; }}});
652
- equal(result, "foo", "'foo' should === '" + result);
653
- });
654
-
655
- test("Unknown helper in knownHelpers only mode should be passed as undefined", function() {
656
- var template = CompilerContext.compile("{{typeof hello}}", {knownHelpers: {'typeof': true}, knownHelpersOnly: true});
657
-
658
- var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return "foo"; }}});
659
- equal(result, "undefined", "'undefined' should === '" + result);
660
- });
661
- test("Builtin helpers available in knownHelpers only mode", function() {
662
- var template = CompilerContext.compile("{{#unless foo}}bar{{/unless}}", {knownHelpersOnly: true});
663
-
664
- var result = template({});
665
- equal(result, "bar", "'bar' should === '" + result);
666
- });
667
- test("Field lookup works in knownHelpers only mode", function() {
668
- var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
669
-
670
- var result = template({foo: 'bar'});
671
- equal(result, "bar", "'bar' should === '" + result);
672
- });
673
- test("Conditional blocks work in knownHelpers only mode", function() {
674
- var template = CompilerContext.compile("{{#foo}}bar{{/foo}}", {knownHelpersOnly: true});
675
-
676
- var result = template({foo: 'baz'});
677
- equal(result, "bar", "'bar' should === '" + result);
678
- });
679
- test("Invert blocks work in knownHelpers only mode", function() {
680
- var template = CompilerContext.compile("{{^foo}}bar{{/foo}}", {knownHelpersOnly: true});
681
-
682
- var result = template({foo: false});
683
- equal(result, "bar", "'bar' should === '" + result);
684
- });
685
- test("Functions are bound to the context in knownHelpers only mode", function() {
686
- var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
687
- var result = template({foo: function() { return this.bar; }, bar: 'bar'});
688
- equal(result, "bar", "'bar' should === '" + result);
689
- });
690
-
691
- suite("blockHelperMissing");
692
-
693
- test("lambdas are resolved by blockHelperMissing, not handlebars proper", function() {
694
- var string = "{{#truthy}}yep{{/truthy}}";
695
- var data = { truthy: function() { return true; } };
696
- shouldCompileTo(string, data, "yep");
697
- });
698
- test("lambdas resolved by blockHelperMissing are bound to the context", function() {
699
- var string = "{{#truthy}}yep{{/truthy}}";
700
- var boundData = { truthy: function() { return this.truthiness(); }, truthiness: function() { return false; } };
701
- shouldCompileTo(string, boundData, "");
702
- });
703
-
704
- var teardown;
705
- suite("built-in helpers", {
706
- setup: function(){ teardown = null; },
707
- teardown: function(){ if (teardown) { teardown(); } }
708
- });
709
-
710
- test("with", function() {
711
- var string = "{{#with person}}{{first}} {{last}}{{/with}}";
712
- shouldCompileTo(string, {person: {first: "Alan", last: "Johnson"}}, "Alan Johnson");
713
- });
714
-
715
- test("if", function() {
716
- var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
717
- shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
718
- "if with boolean argument shows the contents when true");
719
- shouldCompileTo(string, {goodbye: "dummy", world: "world"}, "GOODBYE cruel world!",
720
- "if with string argument shows the contents");
721
- shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
722
- "if with boolean argument does not show the contents when false");
723
- shouldCompileTo(string, {world: "world"}, "cruel world!",
724
- "if with undefined does not show the contents");
725
- shouldCompileTo(string, {goodbye: ['foo'], world: "world"}, "GOODBYE cruel world!",
726
- "if with non-empty array shows the contents");
727
- shouldCompileTo(string, {goodbye: [], world: "world"}, "cruel world!",
728
- "if with empty array does not show the contents");
729
- });
730
-
731
- test("if with function argument", function() {
732
- var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
733
- shouldCompileTo(string, {goodbye: function() {return true;}, world: "world"}, "GOODBYE cruel world!",
734
- "if with function shows the contents when function returns true");
735
- shouldCompileTo(string, {goodbye: function() {return this.world;}, world: "world"}, "GOODBYE cruel world!",
736
- "if with function shows the contents when function returns string");
737
- shouldCompileTo(string, {goodbye: function() {return false;}, world: "world"}, "cruel world!",
738
- "if with function does not show the contents when returns false");
739
- shouldCompileTo(string, {goodbye: function() {return this.foo;}, world: "world"}, "cruel world!",
740
- "if with function does not show the contents when returns undefined");
741
- });
742
-
743
- test("each", function() {
744
- var string = "{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!";
745
- var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
746
- shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
747
- "each with array argument iterates over the contents when not empty");
748
- shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
749
- "each with array argument ignores the contents when empty");
750
- });
751
-
752
- test("each with an object and @key", function() {
753
- var string = "{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!";
754
- var hash = {goodbyes: {"<b>#1</b>": {text: "goodbye"}, 2: {text: "GOODBYE"}}, world: "world"};
755
-
756
- // Object property iteration order is undefined according to ECMA spec,
757
- // so we need to check both possible orders
758
- // @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop
759
- var actual = compileWithPartials(string, hash);
760
- var expected1 = "&lt;b&gt;#1&lt;/b&gt;. goodbye! 2. GOODBYE! cruel world!";
761
- var expected2 = "2. GOODBYE! &lt;b&gt;#1&lt;/b&gt;. goodbye! cruel world!";
762
-
763
- ok(actual === expected1 || actual === expected2, "each with object argument iterates over the contents when not empty");
764
- shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
765
- "each with object argument ignores the contents when empty");
766
- });
767
-
768
- test("each with @index", function() {
769
- var string = "{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!";
770
- var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
771
-
772
- var template = CompilerContext.compile(string);
773
- var result = template(hash);
774
-
775
- equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
776
- });
777
-
778
- test("data passed to helpers", function() {
779
- var string = "{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}";
780
- var hash = {letters: ['a', 'b', 'c']};
781
-
782
- var template = CompilerContext.compile(string);
783
- var result = template(hash, {
784
- data: {
785
- exclaim: '!'
786
- }
787
- });
788
- equal(result, 'a!b!c!', 'should output data');
789
- });
790
-
791
- Handlebars.registerHelper('detectDataInsideEach', function(options) {
792
- return options.data && options.data.exclaim;
793
- });
794
-
795
- test("log", function() {
796
- var string = "{{log blah}}";
797
- var hash = { blah: "whee" };
798
-
799
- var levelArg, logArg;
800
- var originalLog = Handlebars.log;
801
- Handlebars.log = function(level, arg){ levelArg = level, logArg = arg; };
802
- teardown = function(){ Handlebars.log = originalLog; };
803
-
804
- shouldCompileTo(string, hash, "", "log should not display");
805
- equals(1, levelArg, "should call log with 1");
806
- equals("whee", logArg, "should call log with 'whee'");
807
- });
808
-
809
- test("overriding property lookup", function() {
810
-
811
- });
812
-
813
-
814
- test("passing in data to a compiled function that expects data - works with helpers", function() {
815
- var template = CompilerContext.compile("{{hello}}", {data: true});
816
-
817
- var helpers = {
818
- hello: function(options) {
819
- return options.data.adjective + " " + this.noun;
820
- }
821
- };
822
-
823
- var result = template({noun: "cat"}, {helpers: helpers, data: {adjective: "happy"}});
824
- equals("happy cat", result, "Data output by helper");
825
- });
826
-
827
- test("data can be looked up via @foo", function() {
828
- var template = CompilerContext.compile("{{@hello}}");
829
- var result = template({}, { data: { hello: "hello" } });
830
- equals("hello", result, "@foo retrieves template data");
831
- });
832
-
833
- var objectCreate = Handlebars.createFrame;
834
-
835
- test("deep @foo triggers automatic top-level data", function() {
836
- var template = CompilerContext.compile('{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}');
837
-
838
- var helpers = objectCreate(Handlebars.helpers);
839
-
840
- helpers.let = function(options) {
841
- var frame = Handlebars.createFrame(options.data);
842
-
843
- for (var prop in options.hash) {
844
- frame[prop] = options.hash[prop];
845
- }
846
- return options.fn(this, { data: frame });
847
- };
848
-
849
- var result = template({ foo: true }, { helpers: helpers });
850
- equals("Hello world", result, "Automatic data was triggered");
851
- });
852
-
853
- test("parameter data can be looked up via @foo", function() {
854
- var template = CompilerContext.compile("{{hello @world}}");
855
- var helpers = {
856
- hello: function(noun) {
857
- return "Hello " + noun;
858
- }
859
- };
860
-
861
- var result = template({}, { helpers: helpers, data: { world: "world" } });
862
- equals("Hello world", result, "@foo as a parameter retrieves template data");
863
- });
864
-
865
- test("hash values can be looked up via @foo", function() {
866
- var template = CompilerContext.compile("{{hello noun=@world}}");
867
- var helpers = {
868
- hello: function(options) {
869
- return "Hello " + options.hash.noun;
870
- }
871
- };
872
-
873
- var result = template({}, { helpers: helpers, data: { world: "world" } });
874
- equals("Hello world", result, "@foo as a parameter retrieves template data");
875
- });
876
-
877
- test("data is inherited downstream", function() {
878
- var template = CompilerContext.compile("{{#let foo=bar.baz}}{{@foo}}{{/let}}", { data: true });
879
- var helpers = {
880
- let: function(options) {
881
- for (var prop in options.hash) {
882
- options.data[prop] = options.hash[prop];
883
- }
884
- return options.fn(this);
885
- }
886
- };
887
-
888
- var result = template({ bar: { baz: "hello world" } }, { helpers: helpers, data: {} });
889
- equals("hello world", result, "data variables are inherited downstream");
890
- });
891
-
892
- test("passing in data to a compiled function that expects data - works with helpers in partials", function() {
893
- var template = CompilerContext.compile("{{>my_partial}}", {data: true});
894
-
895
- var partials = {
896
- my_partial: CompilerContext.compile("{{hello}}", {data: true})
897
- };
898
-
899
- var helpers = {
900
- hello: function(options) {
901
- return options.data.adjective + " " + this.noun;
902
- }
903
- };
904
-
905
- var result = template({noun: "cat"}, {helpers: helpers, partials: partials, data: {adjective: "happy"}});
906
- equals("happy cat", result, "Data output by helper inside partial");
907
- });
908
-
909
- test("passing in data to a compiled function that expects data - works with helpers and parameters", function() {
910
- var template = CompilerContext.compile("{{hello world}}", {data: true});
911
-
912
- var helpers = {
913
- hello: function(noun, options) {
914
- return options.data.adjective + " " + noun + (this.exclaim ? "!" : "");
915
- }
916
- };
917
-
918
- var result = template({exclaim: true, world: "world"}, {helpers: helpers, data: {adjective: "happy"}});
919
- equals("happy world!", result, "Data output by helper");
920
- });
921
-
922
- test("passing in data to a compiled function that expects data - works with block helpers", function() {
923
- var template = CompilerContext.compile("{{#hello}}{{world}}{{/hello}}", {data: true});
924
-
925
- var helpers = {
926
- hello: function(options) {
927
- return options.fn(this);
928
- },
929
- world: function(options) {
930
- return options.data.adjective + " world" + (this.exclaim ? "!" : "");
931
- }
932
- };
933
-
934
- var result = template({exclaim: true}, {helpers: helpers, data: {adjective: "happy"}});
935
- equals("happy world!", result, "Data output by helper");
936
- });
937
-
938
- test("passing in data to a compiled function that expects data - works with block helpers that use ..", function() {
939
- var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
940
-
941
- var helpers = {
942
- hello: function(options) {
943
- return options.fn({exclaim: "?"});
944
- },
945
- world: function(thing, options) {
946
- return options.data.adjective + " " + thing + (this.exclaim || "");
947
- }
948
- };
949
-
950
- var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}});
951
- equals("happy world?", result, "Data output by helper");
952
- });
953
-
954
- test("passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..", function() {
955
- var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
956
-
957
- var helpers = {
958
- hello: function(options) {
959
- return options.data.accessData + " " + options.fn({exclaim: "?"});
960
- },
961
- world: function(thing, options) {
962
- return options.data.adjective + " " + thing + (this.exclaim || "");
963
- }
964
- };
965
-
966
- var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy", accessData: "#win"}});
967
- equals("#win happy world?", result, "Data output by helper");
968
- });
969
-
970
- test("you can override inherited data when invoking a helper", function() {
971
- var template = CompilerContext.compile("{{#hello}}{{world zomg}}{{/hello}}", {data: true});
972
-
973
- var helpers = {
974
- hello: function(options) {
975
- return options.fn({exclaim: "?", zomg: "world"}, { data: {adjective: "sad"} });
976
- },
977
- world: function(thing, options) {
978
- return options.data.adjective + " " + thing + (this.exclaim || "");
979
- }
980
- };
981
-
982
- var result = template({exclaim: true, zomg: "planet"}, {helpers: helpers, data: {adjective: "happy"}});
983
- equals("sad world?", result, "Overriden data output by helper");
984
- });
985
-
986
-
987
- test("you can override inherited data when invoking a helper with depth", function() {
988
- var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
989
-
990
- var helpers = {
991
- hello: function(options) {
992
- return options.fn({exclaim: "?"}, { data: {adjective: "sad"} });
993
- },
994
- world: function(thing, options) {
995
- return options.data.adjective + " " + thing + (this.exclaim || "");
996
- }
997
- };
998
-
999
- var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}});
1000
- equals("sad world?", result, "Overriden data output by helper");
1001
- });
1002
-
1003
- test("helpers take precedence over same-named context properties", function() {
1004
- var template = CompilerContext.compile("{{goodbye}} {{cruel world}}");
1005
-
1006
- var helpers = {
1007
- goodbye: function() {
1008
- return this.goodbye.toUpperCase();
1009
- },
1010
-
1011
- cruel: function(world) {
1012
- return "cruel " + world.toUpperCase();
1013
- }
1014
- };
1015
-
1016
- var context = {
1017
- goodbye: "goodbye",
1018
- world: "world"
1019
- };
1020
-
1021
- var result = template(context, {helpers: helpers});
1022
- equals(result, "GOODBYE cruel WORLD", "Helper executed");
1023
- });
1024
-
1025
- test("helpers take precedence over same-named context properties$", function() {
1026
- var template = CompilerContext.compile("{{#goodbye}} {{cruel world}}{{/goodbye}}");
1027
-
1028
- var helpers = {
1029
- goodbye: function(options) {
1030
- return this.goodbye.toUpperCase() + options.fn(this);
1031
- },
1032
-
1033
- cruel: function(world) {
1034
- return "cruel " + world.toUpperCase();
1035
- }
1036
- };
1037
-
1038
- var context = {
1039
- goodbye: "goodbye",
1040
- world: "world"
1041
- };
1042
-
1043
- var result = template(context, {helpers: helpers});
1044
- equals(result, "GOODBYE cruel WORLD", "Helper executed");
1045
- });
1046
-
1047
- test("Scoped names take precedence over helpers", function() {
1048
- var template = CompilerContext.compile("{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}");
1049
-
1050
- var helpers = {
1051
- goodbye: function() {
1052
- return this.goodbye.toUpperCase();
1053
- },
1054
-
1055
- cruel: function(world) {
1056
- return "cruel " + world.toUpperCase();
1057
- },
1058
- };
1059
-
1060
- var context = {
1061
- goodbye: "goodbye",
1062
- world: "world"
1063
- };
1064
-
1065
- var result = template(context, {helpers: helpers});
1066
- equals(result, "goodbye cruel WORLD cruel GOODBYE", "Helper not executed");
1067
- });
1068
-
1069
- test("Scoped names take precedence over block helpers", function() {
1070
- var template = CompilerContext.compile("{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}");
1071
-
1072
- var helpers = {
1073
- goodbye: function(options) {
1074
- return this.goodbye.toUpperCase() + options.fn(this);
1075
- },
1076
-
1077
- cruel: function(world) {
1078
- return "cruel " + world.toUpperCase();
1079
- },
1080
- };
1081
-
1082
- var context = {
1083
- goodbye: "goodbye",
1084
- world: "world"
1085
- };
1086
-
1087
- var result = template(context, {helpers: helpers});
1088
- equals(result, "GOODBYE cruel WORLD goodbye", "Helper executed");
1089
- });
1090
-
1091
- test("helpers can take an optional hash", function() {
1092
- var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" times=12}}');
1093
-
1094
- var helpers = {
1095
- goodbye: function(options) {
1096
- return "GOODBYE " + options.hash.cruel + " " + options.hash.world + " " + options.hash.times + " TIMES";
1097
- }
1098
- };
1099
-
1100
- var context = {};
1101
-
1102
- var result = template(context, {helpers: helpers});
1103
- equals(result, "GOODBYE CRUEL WORLD 12 TIMES", "Helper output hash");
1104
- });
1105
-
1106
- test("helpers can take an optional hash with booleans", function() {
1107
- var helpers = {
1108
- goodbye: function(options) {
1109
- if (options.hash.print === true) {
1110
- return "GOODBYE " + options.hash.cruel + " " + options.hash.world;
1111
- } else if (options.hash.print === false) {
1112
- return "NOT PRINTING";
1113
- } else {
1114
- return "THIS SHOULD NOT HAPPEN";
1115
- }
1116
- }
1117
- };
1118
-
1119
- var context = {};
1120
-
1121
- var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=true}}');
1122
- var result = template(context, {helpers: helpers});
1123
- equals(result, "GOODBYE CRUEL WORLD", "Helper output hash");
1124
-
1125
- template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=false}}');
1126
- result = template(context, {helpers: helpers});
1127
- equals(result, "NOT PRINTING", "Boolean helper parameter honored");
1128
- });
1129
-
1130
- test("block helpers can take an optional hash", function() {
1131
- var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}');
1132
-
1133
- var helpers = {
1134
- goodbye: function(options) {
1135
- return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
1136
- }
1137
- };
1138
-
1139
- var result = template({}, {helpers: helpers});
1140
- equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
1141
- });
1142
-
1143
- test("block helpers can take an optional hash with single quoted stings", function() {
1144
- var template = CompilerContext.compile("{{#goodbye cruel='CRUEL' times=12}}world{{/goodbye}}");
1145
-
1146
- var helpers = {
1147
- goodbye: function(options) {
1148
- return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
1149
- }
1150
- };
1151
-
1152
- var result = template({}, {helpers: helpers});
1153
- equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
1154
- });
1155
-
1156
- test("block helpers can take an optional hash with booleans", function() {
1157
- var helpers = {
1158
- goodbye: function(options) {
1159
- if (options.hash.print === true) {
1160
- return "GOODBYE " + options.hash.cruel + " " + options.fn(this);
1161
- } else if (options.hash.print === false) {
1162
- return "NOT PRINTING";
1163
- } else {
1164
- return "THIS SHOULD NOT HAPPEN";
1165
- }
1166
- }
1167
- };
1168
-
1169
- var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}');
1170
- var result = template({}, {helpers: helpers});
1171
- equals(result, "GOODBYE CRUEL world", "Boolean hash parameter honored");
1172
-
1173
- template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}');
1174
- result = template({}, {helpers: helpers});
1175
- equals(result, "NOT PRINTING", "Boolean hash parameter honored");
1176
- });
1177
-
1178
-
1179
- test("arguments to helpers can be retrieved from options hash in string form", function() {
1180
- var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {stringParams: true});
1181
-
1182
- var helpers = {
1183
- wycats: function(passiveVoice, noun) {
1184
- return "HELP ME MY BOSS " + passiveVoice + ' ' + noun;
1185
- }
1186
- };
1187
-
1188
- var result = template({}, {helpers: helpers});
1189
-
1190
- equals(result, "HELP ME MY BOSS is.a slave.driver", "String parameters output");
1191
- });
1192
-
1193
- test("when using block form, arguments to helpers can be retrieved from options hash in string form", function() {
1194
- var template = CompilerContext.compile('{{#wycats is.a slave.driver}}help :({{/wycats}}', {stringParams: true});
1195
-
1196
- var helpers = {
1197
- wycats: function(passiveVoice, noun, options) {
1198
- return "HELP ME MY BOSS " + passiveVoice + ' ' +
1199
- noun + ': ' + options.fn(this);
1200
- }
1201
- };
1202
-
1203
- var result = template({}, {helpers: helpers});
1204
-
1205
- equals(result, "HELP ME MY BOSS is.a slave.driver: help :(", "String parameters output");
1206
- });
1207
-
1208
- test("when inside a block in String mode, .. passes the appropriate context in the options hash", function() {
1209
- var template = CompilerContext.compile('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', {stringParams: true});
1210
-
1211
- var helpers = {
1212
- tomdale: function(desire, noun, options) {
1213
- return "STOP ME FROM READING HACKER NEWS I " +
1214
- options.contexts[0][desire] + " " + noun;
1215
- },
1216
-
1217
- "with": function(context, options) {
1218
- return options.fn(options.contexts[0][context]);
1219
- }
1220
- };
1221
-
1222
- var result = template({
1223
- dale: {},
1224
-
1225
- need: 'need-a'
1226
- }, {helpers: helpers});
1227
-
1228
- equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke", "Proper context variable output");
1229
- });
1230
-
1231
- test("in string mode, information about the types is passed along", function() {
1232
- var template = CompilerContext.compile('{{tomdale "need" dad.joke true false}}', { stringParams: true });
1233
-
1234
- var helpers = {
1235
- tomdale: function(desire, noun, trueBool, falseBool, options) {
1236
- equal(options.types[0], 'STRING', "the string type is passed");
1237
- equal(options.types[1], 'ID', "the expression type is passed");
1238
- equal(options.types[2], 'BOOLEAN', "the expression type is passed");
1239
- equal(desire, "need", "the string form is passed for strings");
1240
- equal(noun, "dad.joke", "the string form is passed for expressions");
1241
- equal(trueBool, true, "raw booleans are passed through");
1242
- equal(falseBool, false, "raw booleans are passed through");
1243
- return "Helper called";
1244
- }
1245
- };
1246
-
1247
- var result = template({}, { helpers: helpers });
1248
- equal(result, "Helper called");
1249
- });
1250
-
1251
- test("in string mode, hash parameters get type information", function() {
1252
- var template = CompilerContext.compile('{{tomdale he.says desire="need" noun=dad.joke bool=true}}', { stringParams: true });
1253
-
1254
- var helpers = {
1255
- tomdale: function(exclamation, options) {
1256
- equal(exclamation, "he.says");
1257
- equal(options.types[0], "ID");
1258
-
1259
- equal(options.hashTypes.desire, "STRING");
1260
- equal(options.hashTypes.noun, "ID");
1261
- equal(options.hashTypes.bool, "BOOLEAN");
1262
- equal(options.hash.desire, "need");
1263
- equal(options.hash.noun, "dad.joke");
1264
- equal(options.hash.bool, true);
1265
- return "Helper called";
1266
- }
1267
- };
1268
-
1269
- var result = template({}, { helpers: helpers });
1270
- equal(result, "Helper called");
1271
- });
1272
-
1273
- test("when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper", function() {
1274
- var template = CompilerContext.compile('{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', {stringParams: true});
1275
-
1276
- var helpers = {
1277
- tomdale: function(desire, noun, options) {
1278
- return "STOP ME FROM READING HACKER NEWS I " +
1279
- options.contexts[0][desire] + " " + noun + " " +
1280
- options.fn(this);
1281
- },
1282
-
1283
- "with": function(context, options) {
1284
- return options.fn(options.contexts[0][context]);
1285
- }
1286
- };
1287
-
1288
- var result = template({
1289
- dale: {},
1290
-
1291
- need: 'need-a'
1292
- }, {helpers: helpers});
1293
-
1294
- equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke wot", "Proper context variable output");
1295
- });
1296
-
1297
- suite("Regressions");
1298
-
1299
- test("GH-94: Cannot read property of undefined", function() {
1300
- var data = {"books":[{"title":"The origin of species","author":{"name":"Charles Darwin"}},{"title":"Lazarillo de Tormes"}]};
1301
- var string = "{{#books}}{{title}}{{author.name}}{{/books}}";
1302
- shouldCompileTo(string, data, "The origin of speciesCharles DarwinLazarillo de Tormes",
1303
- "Renders without an undefined property error");
1304
- });
1305
-
1306
- test("GH-150: Inverted sections print when they shouldn't", function() {
1307
- var string = "{{^set}}not set{{/set}} :: {{#set}}set{{/set}}";
1308
-
1309
- shouldCompileTo(string, {}, "not set :: ", "inverted sections run when property isn't present in context");
1310
- shouldCompileTo(string, {set: undefined}, "not set :: ", "inverted sections run when property is undefined");
1311
- shouldCompileTo(string, {set: false}, "not set :: ", "inverted sections run when property is false");
1312
- shouldCompileTo(string, {set: true}, " :: set", "inverted sections don't run when property is true");
1313
- });
1314
-
1315
- test("Mustache man page", function() {
1316
- var string = "Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}";
1317
- var data = {
1318
- "name": "Chris",
1319
- "value": 10000,
1320
- "taxed_value": 10000 - (10000 * 0.4),
1321
- "in_ca": true
1322
- };
1323
-
1324
- shouldCompileTo(string, data, "Hello Chris. You have just won $10000! Well, $6000, after taxes.", "the hello world mustache example works");
1325
- });
1326
-
1327
- test("GH-158: Using array index twice, breaks the template", function() {
1328
- var string = "{{arr.[0]}}, {{arr.[1]}}";
1329
- var data = { "arr": [1,2] };
1330
-
1331
- shouldCompileTo(string, data, "1, 2", "it works as expected");
1332
- });
1333
-
1334
- test("bug reported by @fat where lambdas weren't being properly resolved", function() {
1335
- var string = "<strong>This is a slightly more complicated {{thing}}.</strong>.\n{{! Just ignore this business. }}\nCheck this out:\n{{#hasThings}}\n<ul>\n{{#things}}\n<li class={{className}}>{{word}}</li>\n{{/things}}</ul>.\n{{/hasThings}}\n{{^hasThings}}\n\n<small>Nothing to check out...</small>\n{{/hasThings}}";
1336
- var data = {
1337
- thing: function() {
1338
- return "blah";
1339
- },
1340
- things: [
1341
- {className: "one", word: "@fat"},
1342
- {className: "two", word: "@dhg"},
1343
- {className: "three", word:"@sayrer"}
1344
- ],
1345
- hasThings: function() {
1346
- return true;
1347
- }
1348
- };
1349
-
1350
- var output = "<strong>This is a slightly more complicated blah.</strong>.\n\nCheck this out:\n\n<ul>\n\n<li class=one>@fat</li>\n\n<li class=two>@dhg</li>\n\n<li class=three>@sayrer</li>\n</ul>.\n\n";
1351
- shouldCompileTo(string, data, output);
1352
- });
1353
-
1354
- test("Passing falsy values to Handlebars.compile throws an error", function() {
1355
- shouldThrow(function() {
1356
- CompilerContext.compile(null);
1357
- }, "You must pass a string or Handlebars AST to Handlebars.compile. You passed null");
1358
- });
1359
-
1360
- test('GH-408: Multiple loops fail', function() {
1361
- var context = [
1362
- { name: "John Doe", location: { city: "Chicago" } },
1363
- { name: "Jane Doe", location: { city: "New York"} }
1364
- ];
1365
-
1366
- var template = CompilerContext.compile('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}');
1367
-
1368
- var result = template(context);
1369
- equals(result, "John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe", 'It should output multiple times');
1370
- });