hogan_assets 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use @hogan_assets
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hogan_assets.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hogan_assets (1.0.0)
5
+ execjs (>= 1.2.9)
6
+ sprockets (>= 2.0.3)
7
+ tilt (>= 1.3.3)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ execjs (1.2.13)
13
+ multi_json (~> 1.0)
14
+ hike (1.2.1)
15
+ multi_json (1.0.4)
16
+ rack (1.3.5)
17
+ sprockets (2.1.2)
18
+ hike (~> 1.2)
19
+ rack (~> 1.0)
20
+ tilt (~> 1.1, != 1.3.0)
21
+ tilt (1.3.3)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ hogan_assets!
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Les Hill
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # HoganAssets
2
+
3
+ **HoganAssets** compiles your [mustache](http://mustache.github.com/) templates with [hogan.js](http://twitter.github.com/hogan.js/) on **sprockets** and the Rails asset pipeline.
4
+
5
+ **hogan.js** is a templating engine developed at [Twitter](http://twitter.com) that follows the **mustache** spec and compiles the templates to JavaScript. The first bit is *cool*, since `mustache` is *cool*. The second bit is **awesome and full of win** because we can now compile our **mustache** templates on the server using the asset pipeline/sprockets.
6
+
7
+ This gem contains **hogan.js v1.0.2**
8
+
9
+ ## Installation
10
+
11
+ ### Installation with Rails 3.1+
12
+
13
+ Add this to your `Gemfile` as part of the `assets` group
14
+
15
+ group :assets do
16
+ gem 'hogan_assets'
17
+ end
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Require `hogan.js` somewhere in your JavaScript manifest, for example in `application.js` if you are using Rails 3.1+:
24
+
25
+ //= require hogan.js
26
+
27
+ Locate your `.mustache` templates with your other JavaScript assets, usually in `app/assets/templates` or `app/assets/javascripts/templates`.
28
+ Require your templates with `require_tree`:
29
+
30
+ //= require_tree templates
31
+
32
+ Templates are named for the sub-path below `require_tree`. For example, the file `app/assets/javascripts/templates/pages/person.mustache` will be named `pages/person`.
33
+
34
+ ### Installation with sprockets
35
+
36
+ Add this line to your `Gemfile`:
37
+
38
+ gem 'hogan_assets'
39
+
40
+ And then execute:
41
+
42
+ $ bundle
43
+
44
+ Require `hogan.js` somewhere in your JavaScript.
45
+
46
+ *TODO* Templates?
47
+
48
+ ## Usage
49
+
50
+ Templates are compiled to a global JavaScript object named `HoganTemplates`. To render `pages/person`:
51
+
52
+ HoganTemplates['pages/person'].render(context, partials);
53
+
54
+ # Author
55
+
56
+ I made this because I <3 **mustache** and want to use it in Rails. Follow me on [Github](https://github.com/leshill) and [Twitter](https://twitter.com/leshill).
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/hogan_assets/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Les Hill"]
6
+ gem.email = ["leshill@gmail.com"]
7
+ gem.description = %q{Use compiled hogan.js (mustache) JavaScript templates with sprockets and the Rails asset pipeline.}
8
+ gem.summary = %q{Use compiled hogan.js (mustache) JavaScript templates with sprockets and the Rails asset pipeline.}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "hogan_assets"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = HoganAssets::VERSION
17
+
18
+ gem.add_runtime_dependency "execjs", ">= 1.2.9"
19
+ gem.add_runtime_dependency "tilt", ">= 1.3.3"
20
+ gem.add_runtime_dependency "sprockets", ">= 2.0.3"
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'hogan_assets/version'
2
+
3
+ module HoganAssets
4
+ autoload(:Hogan, 'hogan_assets/hogan')
5
+ autoload(:Tilt, 'hogan_assets/tilt')
6
+
7
+ if defined?(Rails)
8
+ require 'hogan_assets/engine'
9
+ else
10
+ require 'sprockets'
11
+ Sprockets.register_engine '.mustache', Tilt
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module HoganAssets
2
+ class Engine < ::Rails::Engine
3
+ initializer "sprockets.hogan", :after => "sprockets.environment", :group => :all do |app|
4
+ next unless app.assets
5
+ app.assets.register_engine('.mustache', Tilt)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,31 @@
1
+ # Based on https://github.com/josh/ruby-coffee-script
2
+ require 'execjs'
3
+ require 'pathname'
4
+
5
+ module HoganAssets
6
+ class Hogan
7
+ class << self
8
+ def compile(source)
9
+ context.eval("Hogan.compile(#{source.inspect}, {asString: true})")
10
+ end
11
+
12
+ private
13
+
14
+ def context
15
+ @context ||= ExecJS.compile(source)
16
+ end
17
+
18
+ def source
19
+ @source ||= path.read
20
+ end
21
+
22
+ def path
23
+ @path ||= assets_path.join('javascripts', 'hogan.js')
24
+ end
25
+
26
+ def assets_path
27
+ @assets_path ||= Pathname(__FILE__).dirname.join('..','..','vendor','assets')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'tilt'
2
+
3
+ module HoganAssets
4
+ class Tilt < Tilt::Template
5
+ self.default_mime_type = 'application/javascript'
6
+
7
+ def evaluate(scope, locals, &block)
8
+ compiled_template = Hogan.compile(data)
9
+ code = data.inspect
10
+ template_name = scope.logical_path.inspect
11
+ <<-TEMPLATE
12
+ (function() {
13
+ this.HoganTemplates || (this.HoganTemplates = {});
14
+ this.HoganTemplates[#{template_name}] = new HoganTemplate(#{code});
15
+ this.HoganTemplates[#{template_name}].r = #{compiled_template};
16
+ return HoganTemplates[#{template_name}];
17
+ }).call(this);
18
+ TEMPLATE
19
+ end
20
+
21
+ protected
22
+
23
+ def prepare; end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module HoganAssets
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ module HoganAssets
4
+ class TiltTest < Test::Unit::TestCase
5
+ def test_mime_type
6
+ assert_equal 'application/javascript', HoganAssets::Tilt.default_mime_type
7
+ end
8
+
9
+ def test_render
10
+ scope = Class.new do
11
+ def logical_path ; 'path/to/template' ; end
12
+ end.new
13
+
14
+ template = HoganAssets::Tilt.new('/myapp/app/assets/templates/path/to/template.mustache') { "This is {{mustache}}" }
15
+ assert_equal <<END_EXPECTED, template.render(scope, {})
16
+ (function() {
17
+ this.HoganTemplates || (this.HoganTemplates = {});
18
+ this.HoganTemplates["path/to/template"] = new HoganTemplate("This is {{mustache}}");
19
+ this.HoganTemplates["path/to/template"].r = function(cx,p){var c = [cx];var b = "";var _ = this;b += "This is ";b += (_.v(_.f("mustache",c,p,0)));return b;;};
20
+ return HoganTemplates["path/to/template"];
21
+ }).call(this);
22
+ END_EXPECTED
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ require 'hogan_assets/tilt'
2
+ require 'hogan_assets/hogan'
3
+
4
+ require 'test/unit'
@@ -0,0 +1,514 @@
1
+ /*
2
+ * Copyright 2011 Twitter, Inc.
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ var HoganTemplate = (function () {
17
+
18
+ function constructor(text) {
19
+ this.text = text;
20
+ }
21
+
22
+ constructor.prototype = {
23
+ // render: replaced by generated code.
24
+ r: function (context, partials) { return ''; },
25
+
26
+ // variable escaping
27
+ v: hoganEscape,
28
+
29
+ render: function render(context, partials) {
30
+ return this.r(context, partials);
31
+ },
32
+
33
+ // tries to find a partial in the curent scope and render it
34
+ rp: function(name, context, partials, indent) {
35
+ var partial = partials[name];
36
+
37
+ if (!partial) {
38
+ return '';
39
+ }
40
+
41
+ return partial.render(context, partials);
42
+ },
43
+
44
+ // render a section
45
+ rs: function(context, partials, section) {
46
+ var buf = '',
47
+ tail = context[context.length - 1];
48
+
49
+ if (!isArray(tail)) {
50
+ buf = section(context, partials);
51
+ return buf;
52
+ }
53
+
54
+ for (var i = 0; i < tail.length; i++) {
55
+ context.push(tail[i]);
56
+ buf += section(context, partials);
57
+ context.pop();
58
+ }
59
+ return buf;
60
+ },
61
+
62
+ // maybe start a section
63
+ s: function(val, ctx, partials, inverted, start, end) {
64
+ var pass;
65
+
66
+ if (isArray(val) && val.length === 0) {
67
+ return false;
68
+ }
69
+
70
+ if (!inverted && typeof val == 'function') {
71
+ val = this.ls(val, ctx, partials, start, end);
72
+ }
73
+
74
+ pass = (val === '') || !!val;
75
+
76
+ if (!inverted && pass && ctx) {
77
+ ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
78
+ }
79
+
80
+ return pass;
81
+ },
82
+
83
+ // find values with dotted names
84
+ d: function(key, ctx, partials, returnFound) {
85
+
86
+ var names = key.split('.'),
87
+ val = this.f(names[0], ctx, partials, returnFound),
88
+ cx = null;
89
+
90
+ if (key === '.' && isArray(ctx[ctx.length - 2])) {
91
+ return ctx[ctx.length - 1];
92
+ }
93
+
94
+ for (var i = 1; i < names.length; i++) {
95
+ if (val && typeof val == 'object' && names[i] in val) {
96
+ cx = val;
97
+ val = val[names[i]];
98
+ } else {
99
+ val = '';
100
+ }
101
+ }
102
+
103
+ if (returnFound && !val) {
104
+ return false;
105
+ }
106
+
107
+ if (!returnFound && typeof val == 'function') {
108
+ ctx.push(cx);
109
+ val = this.lv(val, ctx, partials);
110
+ ctx.pop();
111
+ }
112
+
113
+ return val;
114
+ },
115
+
116
+ // find values with normal names
117
+ f: function(key, ctx, partials, returnFound) {
118
+ var val = false,
119
+ v = null,
120
+ found = false;
121
+
122
+ for (var i = ctx.length - 1; i >= 0; i--) {
123
+ v = ctx[i];
124
+ if (v && typeof v == 'object' && key in v) {
125
+ val = v[key];
126
+ found = true;
127
+ break;
128
+ }
129
+ }
130
+
131
+ if (!found) {
132
+ return (returnFound) ? false : "";
133
+ }
134
+
135
+ if (!returnFound && typeof val == 'function') {
136
+ val = this.lv(val, ctx, partials);
137
+ }
138
+
139
+ return val;
140
+ },
141
+
142
+ // higher order templates
143
+ ho: function(val, cx, partials, text) {
144
+ var t = val.call(cx, text, function(t) {
145
+ return Hogan.compile(t).render(cx);
146
+ });
147
+ var s = Hogan.compile(t.toString()).render(cx, partials);
148
+ this.b = s;
149
+ return false;
150
+ },
151
+
152
+ // higher order template result buffer
153
+ b: '',
154
+
155
+ // lambda replace section
156
+ ls: function(val, ctx, partials, start, end) {
157
+ var cx = ctx[ctx.length - 1],
158
+ t = val.call(cx);
159
+
160
+ if (val.length > 0) {
161
+ return this.ho(val, cx, partials, this.text.substring(start, end));
162
+ }
163
+
164
+ if (typeof t == 'function') {
165
+ return this.ho(t, cx, partials, this.text.substring(start, end));
166
+ }
167
+ return t;
168
+ },
169
+
170
+ // lambda replace variable
171
+ lv: function(val, ctx, partials) {
172
+ var cx = ctx[ctx.length - 1];
173
+ return Hogan.compile(val.call(cx).toString()).render(cx, partials);
174
+ }
175
+ };
176
+
177
+ var rAmp = /&/g, rLt = /</g, rGt = />/g, rApos =/\'/g,
178
+ rQuot = /\"/g, hChars =/[&<>\"\']/;
179
+ function hoganEscape(str) {
180
+ var s = String(str === null ? '' : str);
181
+ return hChars.test(s) ? s.replace(rAmp,'&amp;')
182
+ .replace(rLt,'&lt;').replace(rGt,'&gt;')
183
+ .replace(rApos,'&#39;').replace(rQuot, '&quot;') : s;
184
+ }
185
+
186
+ var isArray = Array.isArray || function(a) {
187
+ return Object.prototype.toString.call(a) === '[object Array]';
188
+ };
189
+
190
+ return constructor;
191
+ })();
192
+
193
+ var Hogan = (function () {
194
+
195
+ // Setup regex assignments
196
+ // remove whitespace according to Mustache spec
197
+ var rIsWhitespace = /\S/,
198
+ rQuot = /\"/g,
199
+ rNewline = /\n/g,
200
+ rCr = /\r/g,
201
+ rSlash = /\\/g,
202
+ tagTypes = {
203
+ '#': 1, '^': 2, '/': 3, '!': 4, '>': 5,
204
+ '<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
205
+ };
206
+
207
+ function scan(text) {
208
+ var len = text.length,
209
+ IN_TEXT = 0,
210
+ IN_TAG_TYPE = 1,
211
+ IN_TAG = 2,
212
+ state = IN_TEXT,
213
+ tagType = null,
214
+ tag = null,
215
+ buf = '',
216
+ tokens = [],
217
+ seenTag = false,
218
+ i = 0,
219
+ lineStart = 0,
220
+ otag = '{{',
221
+ ctag = '}}';
222
+
223
+ function addBuf() {
224
+ if (buf.length > 0) {
225
+ tokens.push(new String(buf));
226
+ buf = '';
227
+ }
228
+ }
229
+
230
+ function lineIsWhitespace() {
231
+ var isAllWhitespace = true;
232
+ for (var j = lineStart; j < tokens.length; j++) {
233
+ isAllWhitespace =
234
+ (tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
235
+ (!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
236
+ if (!isAllWhitespace) {
237
+ return false;
238
+ }
239
+ }
240
+
241
+ return isAllWhitespace;
242
+ }
243
+
244
+ function filterLine(haveSeenTag, noNewLine) {
245
+ addBuf();
246
+ if (haveSeenTag && lineIsWhitespace()) {
247
+ for (var j = lineStart; j < tokens.length; j++) {
248
+ if (!tokens[j].tag) {
249
+ tokens.splice(j, 1);
250
+ }
251
+ }
252
+ } else if (!noNewLine) {
253
+ tokens.push({tag:'\n'});
254
+ }
255
+
256
+ seenTag = false;
257
+ lineStart = tokens.length;
258
+ }
259
+
260
+ function changeDelimiters(text, index) {
261
+ var close = '=' + ctag,
262
+ closeIndex = text.indexOf(close, index),
263
+ delimiters = trim(text.substring(text.indexOf('=', index) + 1,
264
+ closeIndex)).split(' ');
265
+ otag = delimiters[0];
266
+ ctag = delimiters[1];
267
+ return closeIndex + close.length - 1;
268
+ }
269
+
270
+ for (i = 0; i < len; i++) {
271
+ if (state == IN_TEXT) {
272
+ if (tagChange(otag, text, i)) {
273
+ --i;
274
+ addBuf();
275
+ state = IN_TAG_TYPE;
276
+ } else {
277
+ if (text.charAt(i) == '\n') {
278
+ filterLine(seenTag);
279
+ } else {
280
+ buf += text.charAt(i);
281
+ }
282
+ }
283
+ } else if (state == IN_TAG_TYPE) {
284
+ i += otag.length - 1;
285
+ tag = tagTypes[text.charAt(i + 1)];
286
+ tagType = tag ? text.charAt(i + 1) : '_v';
287
+ seenTag = i;
288
+ if (tagType == '=') {
289
+ i = changeDelimiters(text, i);
290
+ state = IN_TEXT;
291
+ } else {
292
+ if (tag) {
293
+ i++;
294
+ }
295
+ state = IN_TAG;
296
+ }
297
+ } else {
298
+ if (tagChange(ctag, text, i)) {
299
+ i += ctag.length - 1;
300
+ tokens.push({tag: tagType, n: trim(buf),
301
+ i: (tagType == '/') ? seenTag - 1 : i + 1});
302
+ buf = '';
303
+ state = IN_TEXT;
304
+ if (tagType == '{') {
305
+ i++;
306
+ }
307
+ } else {
308
+ buf += text.charAt(i);
309
+ }
310
+ }
311
+ }
312
+
313
+ filterLine(seenTag, true);
314
+
315
+ return tokens;
316
+ }
317
+
318
+ function trim(s) {
319
+ if (s.trim) {
320
+ return s.trim();
321
+ }
322
+
323
+ return s.replace(/^\s*|\s*$/g, '');
324
+ }
325
+
326
+ function tagChange(tag, text, index) {
327
+ if (text.charAt(index) != tag.charAt(0)) {
328
+ return false;
329
+ }
330
+
331
+ for (var i = 1, l = tag.length; i < l; i++) {
332
+ if (text.charAt(index + i) != tag.charAt(i)) {
333
+ return false;
334
+ }
335
+ }
336
+
337
+ return true;
338
+ }
339
+
340
+ function buildTree(tokens, kind, stack, customTags) {
341
+ var instructions = [],
342
+ opener = null,
343
+ token = null;
344
+
345
+ while (tokens.length > 0) {
346
+ token = tokens.shift();
347
+ if (token.tag == '#' || token.tag == '^' ||
348
+ isOpener(token, customTags)) {
349
+ stack.push(token);
350
+ token.nodes = buildTree(tokens, token.tag, stack, customTags);
351
+ instructions.push(token);
352
+ } else if (token.tag == '/') {
353
+ if (stack.length === 0) {
354
+ throw new Error('Closing tag without opener: /' + token.n);
355
+ }
356
+ opener = stack.pop();
357
+ if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
358
+ throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
359
+ }
360
+ opener.end = token.i;
361
+ return instructions;
362
+ } else {
363
+ instructions.push(token);
364
+ }
365
+ }
366
+
367
+ if (stack.length > 0) {
368
+ throw new Error('missing closing tag: ' + stack.pop().n);
369
+ }
370
+
371
+ return instructions;
372
+ }
373
+
374
+ function isOpener(token, tags) {
375
+ for (var i = 0, l = tags.length; i < l; i++) {
376
+ if (tags[i].o == token.n) {
377
+ token.tag = '#';
378
+ return true;
379
+ }
380
+ }
381
+ }
382
+
383
+ function isCloser(close, open, tags) {
384
+ for (var i = 0, l = tags.length; i < l; i++) {
385
+ if (tags[i].c == close && tags[i].o == open) {
386
+ return true;
387
+ }
388
+ }
389
+ }
390
+
391
+ function generate(tree, text, options) {
392
+ var code = 'var c = [cx];var b = "";var _ = this;' +
393
+ walk(tree) + 'return b;';
394
+ if (options.asString) {
395
+ return 'function(cx,p){' + code + ';}';
396
+ }
397
+
398
+ var template = new HoganTemplate(text);
399
+ template.r = new Function('cx', 'p', code);
400
+ return template;
401
+ }
402
+
403
+ function esc(s) {
404
+ return s.replace(rSlash, '\\\\')
405
+ .replace(rQuot, '\\\"')
406
+ .replace(rNewline, '\\n')
407
+ .replace(rCr, '\\r');
408
+ }
409
+
410
+ function chooseMethod(s) {
411
+ return (~s.indexOf('.')) ? 'd' : 'f';
412
+ }
413
+
414
+ function walk(tree) {
415
+ var code = '';
416
+ for (var i = 0, l = tree.length; i < l; i++) {
417
+ var tag = tree[i].tag;
418
+ if (tag == '#') {
419
+ code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
420
+ tree[i].i, tree[i].end);
421
+ } else if (tag == '^') {
422
+ code += invertedSection(tree[i].nodes, tree[i].n,
423
+ chooseMethod(tree[i].n));
424
+ } else if (tag == '<' || tag == '>') {
425
+ code += partial(tree[i].n);
426
+ } else if (tag == '{' || tag == '&') {
427
+ code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
428
+ } else if (tag == '\n') {
429
+ code += text('\n');
430
+ } else if (tag == '_v') {
431
+ code += variable(tree[i].n, chooseMethod(tree[i].n));
432
+ } else if (tag === undefined) {
433
+ code += text(tree[i]);
434
+ }
435
+ }
436
+ return code;
437
+ }
438
+
439
+ function section(nodes, id, method, start, end) {
440
+ return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
441
+ 'c,p,0,' + start + ',' + end + ')){' +
442
+ 'b += _.rs(c,p,' +
443
+ 'function(c,p){ var b = "";' +
444
+ walk(nodes) +
445
+ 'return b;});c.pop();}' +
446
+ 'else{b += _.b; _.b = ""};';
447
+ }
448
+
449
+ function invertedSection(nodes, id, method) {
450
+ return 'if (!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0)){' +
451
+ walk(nodes) +
452
+ '};';
453
+ }
454
+
455
+ function partial(id) {
456
+ return 'b += _.rp("' + esc(id) + '",c[c.length - 1],p);';
457
+ }
458
+
459
+ function tripleStache(id, method) {
460
+ return 'b += (_.' + method + '("' + esc(id) + '",c,p,0));';
461
+ }
462
+
463
+ function variable(id, method) {
464
+ return 'b += (_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
465
+ }
466
+
467
+ function text(id) {
468
+ return 'b += "' + esc(id) + '";';
469
+ }
470
+
471
+ return ({
472
+ scan: scan,
473
+
474
+ parse: function(tokens, options) {
475
+ options = options || {};
476
+ return buildTree(tokens, '', [], options.sectionTags || []);
477
+ },
478
+
479
+ cache: {},
480
+
481
+ compile: function(text, options) {
482
+ // options
483
+ //
484
+ // asString: false (default)
485
+ //
486
+ // sectionTags: [{o: '_foo', c: 'foo'}]
487
+ // An array of object with o and c fields that indicate names for custom
488
+ // section tags. The example above allows parsing of {{_foo}}{{/foo}}.
489
+ //
490
+ options = options || {};
491
+
492
+ var t = this.cache[text];
493
+ if (t) {
494
+ return t;
495
+ }
496
+ t = generate(this.parse(scan(text), options), text, options);
497
+ return this.cache[text] = t;
498
+ }
499
+ });
500
+ })();
501
+
502
+ // Export the hogan constructor for Node.js and CommonJS.
503
+ if (typeof module !== 'undefined' && module.exports) {
504
+ module.exports = Hogan;
505
+ module.exports.Template = HoganTemplate;
506
+ } else if (typeof exports !== 'undefined') {
507
+ exports.Hogan = Hogan;
508
+ exports.HoganTemplate = HoganTemplate;
509
+ }
510
+
511
+ // Expose Hogan as an AMD module.
512
+ if (typeof define === 'function' && define.amd) {
513
+ define(function () { return Hogan; });
514
+ }
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hogan_assets
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Les Hill
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-25 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: execjs
16
+ requirement: &70131289224740 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.9
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70131289224740
25
+ - !ruby/object:Gem::Dependency
26
+ name: tilt
27
+ requirement: &70131289224240 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.3
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70131289224240
36
+ - !ruby/object:Gem::Dependency
37
+ name: sprockets
38
+ requirement: &70131289223780 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.3
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70131289223780
47
+ description: Use compiled hogan.js (mustache) JavaScript templates with sprockets
48
+ and the Rails asset pipeline.
49
+ email:
50
+ - leshill@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - .rvmrc
57
+ - Gemfile
58
+ - Gemfile.lock
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - hogan_assets.gemspec
63
+ - lib/hogan_assets.rb
64
+ - lib/hogan_assets/engine.rb
65
+ - lib/hogan_assets/hogan.rb
66
+ - lib/hogan_assets/tilt.rb
67
+ - lib/hogan_assets/version.rb
68
+ - test/hogan_assets/tilt_test.rb
69
+ - test/test_helper.rb
70
+ - vendor/assets/javascripts/hogan.js
71
+ homepage: ''
72
+ licenses: []
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.10
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Use compiled hogan.js (mustache) JavaScript templates with sprockets and
95
+ the Rails asset pipeline.
96
+ test_files:
97
+ - test/hogan_assets/tilt_test.rb
98
+ - test/test_helper.rb