shared_mustache 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +48 -0
- data/Rakefile +13 -0
- data/jenkins.sh +6 -0
- data/lib/shared_mustache/config.rb +15 -0
- data/lib/shared_mustache/engine.rb +4 -0
- data/lib/shared_mustache/hogan.rb +22 -0
- data/lib/shared_mustache/railtie.rb +12 -0
- data/lib/shared_mustache/version.rb +3 -0
- data/lib/shared_mustache/view_helpers.rb +21 -0
- data/lib/shared_mustache.rb +27 -0
- data/lib/tasks/shared_mustache.rake +20 -0
- data/shared_mustache.gemspec +24 -0
- data/vendor/assets/javascripts/hogan-2.0.0.js +576 -0
- data/vendor/assets/javascripts/shared_mustache/mustache-loader.js +156 -0
- data/vendor/assets/javascripts/shared_mustache/template-2.0.0.js +243 -0
- data/vendor/assets/javascripts/shared_mustache.js +1 -0
- data/vendor/assets/javascripts/templates.js +0 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 HM Government
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# SharedMustache
|
2
|
+
|
3
|
+
Add the ability to share [mustache][mustache] templates in Rails and
|
4
|
+
JavaScript. They will be compiled using [hogan][hogan] for production and
|
5
|
+
then compiled into your JavaScript using the asset-pipeline.
|
6
|
+
|
7
|
+
In development they will be rendered into script blocks on the page.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add 'shared_mustache' to your your application's Gemfile.
|
12
|
+
|
13
|
+
By default when you run `rake shared_mustache:compile` it will put the outputed
|
14
|
+
templates JavaScript at `Rails.root/app/assets/javascript/templates.js`. This
|
15
|
+
can be configured using an intializer.
|
16
|
+
|
17
|
+
You should add the following lines to your `application.js`:
|
18
|
+
|
19
|
+
//= require shared_mustache
|
20
|
+
//= require templates
|
21
|
+
|
22
|
+
At the bottom of your main layout you should add:
|
23
|
+
|
24
|
+
<%= render_mustache_templates if Rails.env.development? %>
|
25
|
+
|
26
|
+
Add `rake shared_mustache:compile` before the `assets:precompile` step of your
|
27
|
+
deploy script.
|
28
|
+
|
29
|
+
Currently it expects your views to be found under `app/views`.
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
Create mustache templates as you would erb partials. So for example at
|
34
|
+
`app/views/home/_row.mustache`. You can then render it in an `erb` template
|
35
|
+
using:
|
36
|
+
|
37
|
+
<%= render_mustache('home/row', context) %>
|
38
|
+
|
39
|
+
It tries to use the same conventions as the native render methods. Such that
|
40
|
+
you can ommit the controller if the mustache is in the same folder as the
|
41
|
+
currently executing controller.
|
42
|
+
|
43
|
+
In your JavaScript you should get access to jQuery methods to interact with the
|
44
|
+
mustache templates:
|
45
|
+
|
46
|
+
$.mustache('home/_row', context);
|
47
|
+
|
48
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "gem_publisher"
|
2
|
+
|
3
|
+
desc "Publish gem to RubyGems.org"
|
4
|
+
task :publish_gem do |t|
|
5
|
+
gem = GemPublisher.publish_if_updated("shared_mustache.gemspec", :rubygems)
|
6
|
+
puts "Published #{gem}" if gem
|
7
|
+
end
|
8
|
+
|
9
|
+
task :test do |t|
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :test
|
data/jenkins.sh
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module SharedMustache
|
2
|
+
# Change config options in an initializer:
|
3
|
+
#
|
4
|
+
# SharedMustache::Config.template_file = 'app/assets/javascriopt/mustache_templates.js'
|
5
|
+
#
|
6
|
+
module Config
|
7
|
+
extend self
|
8
|
+
|
9
|
+
attr_writer :template_file
|
10
|
+
|
11
|
+
def template_file
|
12
|
+
@template_file ||= File.join(Rails.root, 'app', 'assets', 'javascripts', 'templates.js')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'execjs'
|
2
|
+
|
3
|
+
module SharedMustache
|
4
|
+
module Hogan
|
5
|
+
def self.compile(source)
|
6
|
+
context.eval("(typeof exports !== 'undefined' ? exports : Hogan).compile(#{source.inspect}, {asString: true})")
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def self.context
|
12
|
+
@context ||= ExecJS.compile(hogan_source)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.hogan_source
|
16
|
+
hogan_path = File.expand_path('../../vendor/assets/javascripts/hogan-2.0.0.js', File.dirname(__FILE__))
|
17
|
+
File.read(hogan_path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'shared_mustache/view_helpers'
|
2
|
+
|
3
|
+
module SharedMustache
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer "shared_mustache.view_helpers" do
|
6
|
+
ActionView::Base.send :include, ViewHelpers
|
7
|
+
end
|
8
|
+
rake_tasks do
|
9
|
+
Dir[File.expand_path('../tasks/*.rake', File.dirname(__FILE__))].each { |f| load f }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
|
3
|
+
module SharedMustache
|
4
|
+
module ViewHelpers
|
5
|
+
def render_mustache_templates
|
6
|
+
file_list = SharedMustache.file_list
|
7
|
+
|
8
|
+
script_tags = file_list.map do |file|
|
9
|
+
content_tag(:script, File.read(file), id: SharedMustache.file_name_to_id(file), type: 'text/mustache')
|
10
|
+
end
|
11
|
+
script_tags << javascript_include_tag("hogan-2.0.0.js")
|
12
|
+
script_tags.join('').html_safe
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_mustache(template, options={})
|
16
|
+
view = Mustache.new
|
17
|
+
view.template = File.read(SharedMustache.find_template_path(template, params[:controller]))
|
18
|
+
view.render(options).html_safe
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'shared_mustache/config'
|
2
|
+
require 'shared_mustache/engine'
|
3
|
+
require 'shared_mustache/railtie' if defined?(Rails)
|
4
|
+
require 'shared_mustache/version'
|
5
|
+
|
6
|
+
module SharedMustache
|
7
|
+
def self.view_dir
|
8
|
+
File.join(Rails.root, 'app', 'views')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.file_list
|
12
|
+
Dir[File.join(view_dir, '**', '*.mustache')]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.file_name_to_id(filename)
|
16
|
+
filename.gsub('.mustache', '').gsub("#{view_dir}/", '').gsub('/', '-')
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.find_template_path(filename, controller)
|
20
|
+
directory = File.dirname(filename)
|
21
|
+
if directory == '.'
|
22
|
+
directory = controller
|
23
|
+
end
|
24
|
+
|
25
|
+
File.join(view_dir, directory, "_#{File.basename(filename)}.mustache")
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'shared_mustache/config'
|
2
|
+
require 'shared_mustache/hogan'
|
3
|
+
|
4
|
+
namespace :shared_mustache do
|
5
|
+
desc 'Compiles all mustache templates down for production use'
|
6
|
+
task :compile do
|
7
|
+
file_list = SharedMustache.file_list
|
8
|
+
|
9
|
+
javascript = []
|
10
|
+
javascript << 'window.templates = {};'
|
11
|
+
|
12
|
+
file_list.each do |file|
|
13
|
+
template_id = SharedMustache.file_name_to_id(file)
|
14
|
+
compiled_template = SharedMustache::Hogan.compile(File.read(file))
|
15
|
+
javascript << "window.templates['#{template_id}'] = new Hogan.Template(#{compiled_template});"
|
16
|
+
end
|
17
|
+
|
18
|
+
File.open(SharedMustache::Config.template_file, 'w') { |f| f.write javascript.join("\n") }
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'shared_mustache/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "shared_mustache"
|
8
|
+
gem.version = SharedMustache::VERSION
|
9
|
+
gem.authors = ["Edd Sowden"]
|
10
|
+
gem.email = ["edd.sowden@digital.cabinet-office.gov.uk"]
|
11
|
+
gem.description = %q{Share mustache between Rails templates and the browser (using compiled hogan.js templates).}
|
12
|
+
gem.summary = %q{Share mustache between Rails templates and the browser (using compiled hogan.js templates).}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.add_dependency 'mustache', '~> 0.99.4'
|
16
|
+
gem.add_dependency 'execjs', '~> 1.2.4'
|
17
|
+
|
18
|
+
gem.add_development_dependency "gem_publisher", "~> 1.1.1"
|
19
|
+
|
20
|
+
gem.files = `git ls-files`.split($/)
|
21
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
22
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
|
+
gem.require_paths = ["lib"]
|
24
|
+
end
|
@@ -0,0 +1,576 @@
|
|
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
|
+
|
17
|
+
|
18
|
+
var Hogan = {};
|
19
|
+
|
20
|
+
(function (Hogan, useArrayBuffer) {
|
21
|
+
Hogan.Template = function (renderFunc, text, compiler, options) {
|
22
|
+
this.r = renderFunc || this.r;
|
23
|
+
this.c = compiler;
|
24
|
+
this.options = options;
|
25
|
+
this.text = text || '';
|
26
|
+
this.buf = (useArrayBuffer) ? [] : '';
|
27
|
+
}
|
28
|
+
|
29
|
+
Hogan.Template.prototype = {
|
30
|
+
// render: replaced by generated code.
|
31
|
+
r: function (context, partials, indent) { return ''; },
|
32
|
+
|
33
|
+
// variable escaping
|
34
|
+
v: hoganEscape,
|
35
|
+
|
36
|
+
// triple stache
|
37
|
+
t: coerceToString,
|
38
|
+
|
39
|
+
render: function render(context, partials, indent) {
|
40
|
+
return this.ri([context], partials || {}, indent);
|
41
|
+
},
|
42
|
+
|
43
|
+
// render internal -- a hook for overrides that catches partials too
|
44
|
+
ri: function (context, partials, indent) {
|
45
|
+
return this.r(context, partials, indent);
|
46
|
+
},
|
47
|
+
|
48
|
+
// tries to find a partial in the curent scope and render it
|
49
|
+
rp: function(name, context, partials, indent) {
|
50
|
+
var partial = partials[name];
|
51
|
+
|
52
|
+
if (!partial) {
|
53
|
+
return '';
|
54
|
+
}
|
55
|
+
|
56
|
+
if (this.c && typeof partial == 'string') {
|
57
|
+
partial = this.c.compile(partial, this.options);
|
58
|
+
}
|
59
|
+
|
60
|
+
return partial.ri(context, partials, indent);
|
61
|
+
},
|
62
|
+
|
63
|
+
// render a section
|
64
|
+
rs: function(context, partials, section) {
|
65
|
+
var tail = context[context.length - 1];
|
66
|
+
|
67
|
+
if (!isArray(tail)) {
|
68
|
+
section(context, partials, this);
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
|
72
|
+
for (var i = 0; i < tail.length; i++) {
|
73
|
+
context.push(tail[i]);
|
74
|
+
section(context, partials, this);
|
75
|
+
context.pop();
|
76
|
+
}
|
77
|
+
},
|
78
|
+
|
79
|
+
// maybe start a section
|
80
|
+
s: function(val, ctx, partials, inverted, start, end, tags) {
|
81
|
+
var pass;
|
82
|
+
|
83
|
+
if (isArray(val) && val.length === 0) {
|
84
|
+
return false;
|
85
|
+
}
|
86
|
+
|
87
|
+
if (typeof val == 'function') {
|
88
|
+
val = this.ls(val, ctx, partials, inverted, start, end, tags);
|
89
|
+
}
|
90
|
+
|
91
|
+
pass = (val === '') || !!val;
|
92
|
+
|
93
|
+
if (!inverted && pass && ctx) {
|
94
|
+
ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
|
95
|
+
}
|
96
|
+
|
97
|
+
return pass;
|
98
|
+
},
|
99
|
+
|
100
|
+
// find values with dotted names
|
101
|
+
d: function(key, ctx, partials, returnFound) {
|
102
|
+
var names = key.split('.'),
|
103
|
+
val = this.f(names[0], ctx, partials, returnFound),
|
104
|
+
cx = null;
|
105
|
+
|
106
|
+
if (key === '.' && isArray(ctx[ctx.length - 2])) {
|
107
|
+
return ctx[ctx.length - 1];
|
108
|
+
}
|
109
|
+
|
110
|
+
for (var i = 1; i < names.length; i++) {
|
111
|
+
if (val && typeof val == 'object' && names[i] in val) {
|
112
|
+
cx = val;
|
113
|
+
val = val[names[i]];
|
114
|
+
} else {
|
115
|
+
val = '';
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
if (returnFound && !val) {
|
120
|
+
return false;
|
121
|
+
}
|
122
|
+
|
123
|
+
if (!returnFound && typeof val == 'function') {
|
124
|
+
ctx.push(cx);
|
125
|
+
val = this.lv(val, ctx, partials);
|
126
|
+
ctx.pop();
|
127
|
+
}
|
128
|
+
|
129
|
+
return val;
|
130
|
+
},
|
131
|
+
|
132
|
+
// find values with normal names
|
133
|
+
f: function(key, ctx, partials, returnFound) {
|
134
|
+
var val = false,
|
135
|
+
v = null,
|
136
|
+
found = false;
|
137
|
+
|
138
|
+
for (var i = ctx.length - 1; i >= 0; i--) {
|
139
|
+
v = ctx[i];
|
140
|
+
if (v && typeof v == 'object' && key in v) {
|
141
|
+
val = v[key];
|
142
|
+
found = true;
|
143
|
+
break;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
if (!found) {
|
148
|
+
return (returnFound) ? false : "";
|
149
|
+
}
|
150
|
+
|
151
|
+
if (!returnFound && typeof val == 'function') {
|
152
|
+
val = this.lv(val, ctx, partials);
|
153
|
+
}
|
154
|
+
|
155
|
+
return val;
|
156
|
+
},
|
157
|
+
|
158
|
+
// higher order templates
|
159
|
+
ho: function(val, cx, partials, text, tags) {
|
160
|
+
var compiler = this.c;
|
161
|
+
var options = this.options;
|
162
|
+
options.delimiters = tags;
|
163
|
+
var text = val.call(cx, text);
|
164
|
+
text = (text == null) ? String(text) : text.toString();
|
165
|
+
this.b(compiler.compile(text, options).render(cx, partials));
|
166
|
+
return false;
|
167
|
+
},
|
168
|
+
|
169
|
+
// template result buffering
|
170
|
+
b: (useArrayBuffer) ? function(s) { this.buf.push(s); } :
|
171
|
+
function(s) { this.buf += s; },
|
172
|
+
fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } :
|
173
|
+
function() { var r = this.buf; this.buf = ''; return r; },
|
174
|
+
|
175
|
+
// lambda replace section
|
176
|
+
ls: function(val, ctx, partials, inverted, start, end, tags) {
|
177
|
+
var cx = ctx[ctx.length - 1],
|
178
|
+
t = null;
|
179
|
+
|
180
|
+
if (!inverted && this.c && val.length > 0) {
|
181
|
+
return this.ho(val, cx, partials, this.text.substring(start, end), tags);
|
182
|
+
}
|
183
|
+
|
184
|
+
t = val.call(cx);
|
185
|
+
|
186
|
+
if (typeof t == 'function') {
|
187
|
+
if (inverted) {
|
188
|
+
return true;
|
189
|
+
} else if (this.c) {
|
190
|
+
return this.ho(t, cx, partials, this.text.substring(start, end), tags);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
return t;
|
195
|
+
},
|
196
|
+
|
197
|
+
// lambda replace variable
|
198
|
+
lv: function(val, ctx, partials) {
|
199
|
+
var cx = ctx[ctx.length - 1];
|
200
|
+
var result = val.call(cx);
|
201
|
+
|
202
|
+
if (typeof result == 'function') {
|
203
|
+
result = coerceToString(result.call(cx));
|
204
|
+
if (this.c && ~result.indexOf("{\u007B")) {
|
205
|
+
return this.c.compile(result, this.options).render(cx, partials);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
return coerceToString(result);
|
210
|
+
}
|
211
|
+
|
212
|
+
};
|
213
|
+
|
214
|
+
var rAmp = /&/g,
|
215
|
+
rLt = /</g,
|
216
|
+
rGt = />/g,
|
217
|
+
rApos =/\'/g,
|
218
|
+
rQuot = /\"/g,
|
219
|
+
hChars =/[&<>\"\']/;
|
220
|
+
|
221
|
+
|
222
|
+
function coerceToString(val) {
|
223
|
+
return String((val === null || val === undefined) ? '' : val);
|
224
|
+
}
|
225
|
+
|
226
|
+
function hoganEscape(str) {
|
227
|
+
str = coerceToString(str);
|
228
|
+
return hChars.test(str) ?
|
229
|
+
str
|
230
|
+
.replace(rAmp,'&')
|
231
|
+
.replace(rLt,'<')
|
232
|
+
.replace(rGt,'>')
|
233
|
+
.replace(rApos,''')
|
234
|
+
.replace(rQuot, '"') :
|
235
|
+
str;
|
236
|
+
}
|
237
|
+
|
238
|
+
var isArray = Array.isArray || function(a) {
|
239
|
+
return Object.prototype.toString.call(a) === '[object Array]';
|
240
|
+
};
|
241
|
+
|
242
|
+
})(typeof exports !== 'undefined' ? exports : Hogan);
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
(function (Hogan) {
|
248
|
+
// Setup regex assignments
|
249
|
+
// remove whitespace according to Mustache spec
|
250
|
+
var rIsWhitespace = /\S/,
|
251
|
+
rQuot = /\"/g,
|
252
|
+
rNewline = /\n/g,
|
253
|
+
rCr = /\r/g,
|
254
|
+
rSlash = /\\/g,
|
255
|
+
tagTypes = {
|
256
|
+
'#': 1, '^': 2, '/': 3, '!': 4, '>': 5,
|
257
|
+
'<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
|
258
|
+
};
|
259
|
+
|
260
|
+
Hogan.scan = function scan(text, delimiters) {
|
261
|
+
var len = text.length,
|
262
|
+
IN_TEXT = 0,
|
263
|
+
IN_TAG_TYPE = 1,
|
264
|
+
IN_TAG = 2,
|
265
|
+
state = IN_TEXT,
|
266
|
+
tagType = null,
|
267
|
+
tag = null,
|
268
|
+
buf = '',
|
269
|
+
tokens = [],
|
270
|
+
seenTag = false,
|
271
|
+
i = 0,
|
272
|
+
lineStart = 0,
|
273
|
+
otag = '{{',
|
274
|
+
ctag = '}}';
|
275
|
+
|
276
|
+
function addBuf() {
|
277
|
+
if (buf.length > 0) {
|
278
|
+
tokens.push(new String(buf));
|
279
|
+
buf = '';
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
function lineIsWhitespace() {
|
284
|
+
var isAllWhitespace = true;
|
285
|
+
for (var j = lineStart; j < tokens.length; j++) {
|
286
|
+
isAllWhitespace =
|
287
|
+
(tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
|
288
|
+
(!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
|
289
|
+
if (!isAllWhitespace) {
|
290
|
+
return false;
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
return isAllWhitespace;
|
295
|
+
}
|
296
|
+
|
297
|
+
function filterLine(haveSeenTag, noNewLine) {
|
298
|
+
addBuf();
|
299
|
+
|
300
|
+
if (haveSeenTag && lineIsWhitespace()) {
|
301
|
+
for (var j = lineStart, next; j < tokens.length; j++) {
|
302
|
+
if (!tokens[j].tag) {
|
303
|
+
if ((next = tokens[j+1]) && next.tag == '>') {
|
304
|
+
// set indent to token value
|
305
|
+
next.indent = tokens[j].toString()
|
306
|
+
}
|
307
|
+
tokens.splice(j, 1);
|
308
|
+
}
|
309
|
+
}
|
310
|
+
} else if (!noNewLine) {
|
311
|
+
tokens.push({tag:'\n'});
|
312
|
+
}
|
313
|
+
|
314
|
+
seenTag = false;
|
315
|
+
lineStart = tokens.length;
|
316
|
+
}
|
317
|
+
|
318
|
+
function changeDelimiters(text, index) {
|
319
|
+
var close = '=' + ctag,
|
320
|
+
closeIndex = text.indexOf(close, index),
|
321
|
+
delimiters = trim(
|
322
|
+
text.substring(text.indexOf('=', index) + 1, closeIndex)
|
323
|
+
).split(' ');
|
324
|
+
|
325
|
+
otag = delimiters[0];
|
326
|
+
ctag = delimiters[1];
|
327
|
+
|
328
|
+
return closeIndex + close.length - 1;
|
329
|
+
}
|
330
|
+
|
331
|
+
if (delimiters) {
|
332
|
+
delimiters = delimiters.split(' ');
|
333
|
+
otag = delimiters[0];
|
334
|
+
ctag = delimiters[1];
|
335
|
+
}
|
336
|
+
|
337
|
+
for (i = 0; i < len; i++) {
|
338
|
+
if (state == IN_TEXT) {
|
339
|
+
if (tagChange(otag, text, i)) {
|
340
|
+
--i;
|
341
|
+
addBuf();
|
342
|
+
state = IN_TAG_TYPE;
|
343
|
+
} else {
|
344
|
+
if (text.charAt(i) == '\n') {
|
345
|
+
filterLine(seenTag);
|
346
|
+
} else {
|
347
|
+
buf += text.charAt(i);
|
348
|
+
}
|
349
|
+
}
|
350
|
+
} else if (state == IN_TAG_TYPE) {
|
351
|
+
i += otag.length - 1;
|
352
|
+
tag = tagTypes[text.charAt(i + 1)];
|
353
|
+
tagType = tag ? text.charAt(i + 1) : '_v';
|
354
|
+
if (tagType == '=') {
|
355
|
+
i = changeDelimiters(text, i);
|
356
|
+
state = IN_TEXT;
|
357
|
+
} else {
|
358
|
+
if (tag) {
|
359
|
+
i++;
|
360
|
+
}
|
361
|
+
state = IN_TAG;
|
362
|
+
}
|
363
|
+
seenTag = i;
|
364
|
+
} else {
|
365
|
+
if (tagChange(ctag, text, i)) {
|
366
|
+
tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
|
367
|
+
i: (tagType == '/') ? seenTag - ctag.length : i + otag.length});
|
368
|
+
buf = '';
|
369
|
+
i += ctag.length - 1;
|
370
|
+
state = IN_TEXT;
|
371
|
+
if (tagType == '{') {
|
372
|
+
if (ctag == '}}') {
|
373
|
+
i++;
|
374
|
+
} else {
|
375
|
+
cleanTripleStache(tokens[tokens.length - 1]);
|
376
|
+
}
|
377
|
+
}
|
378
|
+
} else {
|
379
|
+
buf += text.charAt(i);
|
380
|
+
}
|
381
|
+
}
|
382
|
+
}
|
383
|
+
|
384
|
+
filterLine(seenTag, true);
|
385
|
+
|
386
|
+
return tokens;
|
387
|
+
}
|
388
|
+
|
389
|
+
function cleanTripleStache(token) {
|
390
|
+
if (token.n.substr(token.n.length - 1) === '}') {
|
391
|
+
token.n = token.n.substring(0, token.n.length - 1);
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
function trim(s) {
|
396
|
+
if (s.trim) {
|
397
|
+
return s.trim();
|
398
|
+
}
|
399
|
+
|
400
|
+
return s.replace(/^\s*|\s*$/g, '');
|
401
|
+
}
|
402
|
+
|
403
|
+
function tagChange(tag, text, index) {
|
404
|
+
if (text.charAt(index) != tag.charAt(0)) {
|
405
|
+
return false;
|
406
|
+
}
|
407
|
+
|
408
|
+
for (var i = 1, l = tag.length; i < l; i++) {
|
409
|
+
if (text.charAt(index + i) != tag.charAt(i)) {
|
410
|
+
return false;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
return true;
|
415
|
+
}
|
416
|
+
|
417
|
+
function buildTree(tokens, kind, stack, customTags) {
|
418
|
+
var instructions = [],
|
419
|
+
opener = null,
|
420
|
+
token = null;
|
421
|
+
|
422
|
+
while (tokens.length > 0) {
|
423
|
+
token = tokens.shift();
|
424
|
+
if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) {
|
425
|
+
stack.push(token);
|
426
|
+
token.nodes = buildTree(tokens, token.tag, stack, customTags);
|
427
|
+
instructions.push(token);
|
428
|
+
} else if (token.tag == '/') {
|
429
|
+
if (stack.length === 0) {
|
430
|
+
throw new Error('Closing tag without opener: /' + token.n);
|
431
|
+
}
|
432
|
+
opener = stack.pop();
|
433
|
+
if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
|
434
|
+
throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
|
435
|
+
}
|
436
|
+
opener.end = token.i;
|
437
|
+
return instructions;
|
438
|
+
} else {
|
439
|
+
instructions.push(token);
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
443
|
+
if (stack.length > 0) {
|
444
|
+
throw new Error('missing closing tag: ' + stack.pop().n);
|
445
|
+
}
|
446
|
+
|
447
|
+
return instructions;
|
448
|
+
}
|
449
|
+
|
450
|
+
function isOpener(token, tags) {
|
451
|
+
for (var i = 0, l = tags.length; i < l; i++) {
|
452
|
+
if (tags[i].o == token.n) {
|
453
|
+
token.tag = '#';
|
454
|
+
return true;
|
455
|
+
}
|
456
|
+
}
|
457
|
+
}
|
458
|
+
|
459
|
+
function isCloser(close, open, tags) {
|
460
|
+
for (var i = 0, l = tags.length; i < l; i++) {
|
461
|
+
if (tags[i].c == close && tags[i].o == open) {
|
462
|
+
return true;
|
463
|
+
}
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
Hogan.generate = function (tree, text, options) {
|
468
|
+
var code = 'var _=this;_.b(i=i||"");' + walk(tree) + 'return _.fl();';
|
469
|
+
if (options.asString) {
|
470
|
+
return 'function(c,p,i){' + code + ';}';
|
471
|
+
}
|
472
|
+
|
473
|
+
return new Hogan.Template(new Function('c', 'p', 'i', code), text, Hogan, options);
|
474
|
+
}
|
475
|
+
|
476
|
+
function esc(s) {
|
477
|
+
return s.replace(rSlash, '\\\\')
|
478
|
+
.replace(rQuot, '\\\"')
|
479
|
+
.replace(rNewline, '\\n')
|
480
|
+
.replace(rCr, '\\r');
|
481
|
+
}
|
482
|
+
|
483
|
+
function chooseMethod(s) {
|
484
|
+
return (~s.indexOf('.')) ? 'd' : 'f';
|
485
|
+
}
|
486
|
+
|
487
|
+
function walk(tree) {
|
488
|
+
var code = '';
|
489
|
+
for (var i = 0, l = tree.length; i < l; i++) {
|
490
|
+
var tag = tree[i].tag;
|
491
|
+
if (tag == '#') {
|
492
|
+
code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
|
493
|
+
tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag);
|
494
|
+
} else if (tag == '^') {
|
495
|
+
code += invertedSection(tree[i].nodes, tree[i].n,
|
496
|
+
chooseMethod(tree[i].n));
|
497
|
+
} else if (tag == '<' || tag == '>') {
|
498
|
+
code += partial(tree[i]);
|
499
|
+
} else if (tag == '{' || tag == '&') {
|
500
|
+
code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
|
501
|
+
} else if (tag == '\n') {
|
502
|
+
code += text('"\\n"' + (tree.length-1 == i ? '' : ' + i'));
|
503
|
+
} else if (tag == '_v') {
|
504
|
+
code += variable(tree[i].n, chooseMethod(tree[i].n));
|
505
|
+
} else if (tag === undefined) {
|
506
|
+
code += text('"' + esc(tree[i]) + '"');
|
507
|
+
}
|
508
|
+
}
|
509
|
+
return code;
|
510
|
+
}
|
511
|
+
|
512
|
+
function section(nodes, id, method, start, end, tags) {
|
513
|
+
return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
|
514
|
+
'c,p,0,' + start + ',' + end + ',"' + tags + '")){' +
|
515
|
+
'_.rs(c,p,' +
|
516
|
+
'function(c,p,_){' +
|
517
|
+
walk(nodes) +
|
518
|
+
'});c.pop();}';
|
519
|
+
}
|
520
|
+
|
521
|
+
function invertedSection(nodes, id, method) {
|
522
|
+
return 'if(!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' +
|
523
|
+
walk(nodes) +
|
524
|
+
'};';
|
525
|
+
}
|
526
|
+
|
527
|
+
function partial(tok) {
|
528
|
+
return '_.b(_.rp("' + esc(tok.n) + '",c,p,"' + (tok.indent || '') + '"));';
|
529
|
+
}
|
530
|
+
|
531
|
+
function tripleStache(id, method) {
|
532
|
+
return '_.b(_.t(_.' + method + '("' + esc(id) + '",c,p,0)));';
|
533
|
+
}
|
534
|
+
|
535
|
+
function variable(id, method) {
|
536
|
+
return '_.b(_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
|
537
|
+
}
|
538
|
+
|
539
|
+
function text(id) {
|
540
|
+
return '_.b(' + id + ');';
|
541
|
+
}
|
542
|
+
|
543
|
+
Hogan.parse = function(tokens, text, options) {
|
544
|
+
options = options || {};
|
545
|
+
return buildTree(tokens, '', [], options.sectionTags || []);
|
546
|
+
},
|
547
|
+
|
548
|
+
Hogan.cache = {};
|
549
|
+
|
550
|
+
Hogan.compile = function(text, options) {
|
551
|
+
// options
|
552
|
+
//
|
553
|
+
// asString: false (default)
|
554
|
+
//
|
555
|
+
// sectionTags: [{o: '_foo', c: 'foo'}]
|
556
|
+
// An array of object with o and c fields that indicate names for custom
|
557
|
+
// section tags. The example above allows parsing of {{_foo}}{{/foo}}.
|
558
|
+
//
|
559
|
+
// delimiters: A string that overrides the default delimiters.
|
560
|
+
// Example: "<% %>"
|
561
|
+
//
|
562
|
+
options = options || {};
|
563
|
+
|
564
|
+
var key = text + '||' + !!options.asString;
|
565
|
+
|
566
|
+
var t = this.cache[key];
|
567
|
+
|
568
|
+
if (t) {
|
569
|
+
return t;
|
570
|
+
}
|
571
|
+
|
572
|
+
t = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
|
573
|
+
return this.cache[key] = t;
|
574
|
+
};
|
575
|
+
})(typeof exports !== 'undefined' ? exports : Hogan);
|
576
|
+
|
@@ -0,0 +1,156 @@
|
|
1
|
+
/*
|
2
|
+
|
3
|
+
Copyright (c) 2012, Bradley Wright
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
* Redistributions in binary form must reproduce the above copyright
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
12
|
+
documentation and/or other materials provided with the distribution.
|
13
|
+
* Neither the name of the <organization> nor the
|
14
|
+
names of its contributors may be used to endorse or promote products
|
15
|
+
derived from this software without specific prior written permission.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
21
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
22
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
23
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
24
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
25
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
|
28
|
+
*/
|
29
|
+
/*
|
30
|
+
* mustache-loader.js - Mustache template loader to go with flask-mustache
|
31
|
+
*
|
32
|
+
* This depends on jQuery, and either:
|
33
|
+
* - Twitter's Hogan.js: https://github.com/twitter/hogan.js or
|
34
|
+
* - Mustache.js: https://github.com/janl/mustache.js
|
35
|
+
*
|
36
|
+
* Usage:
|
37
|
+
*
|
38
|
+
* $('#target').mustache('includes/_user.mustache', {user_name:'Jan'});
|
39
|
+
* var html = $.mustache('includes/_user.mustache', {user_name:'Jan'});
|
40
|
+
* $.mustacheAsFunction('includes/_user.mustache')({user_name:'Jan'});
|
41
|
+
*/
|
42
|
+
|
43
|
+
/*jslint
|
44
|
+
browser: true,
|
45
|
+
white: true,
|
46
|
+
vars: true
|
47
|
+
*/
|
48
|
+
|
49
|
+
/*globals jQuery */
|
50
|
+
|
51
|
+
// authored as a jQuery plugin
|
52
|
+
(function($) {
|
53
|
+
"use strict";
|
54
|
+
|
55
|
+
// this is a cached lookup table of templates
|
56
|
+
var cache = {};
|
57
|
+
|
58
|
+
var load = function(templateName) {
|
59
|
+
// this function takes names like: "includes/_user.mustache"
|
60
|
+
// and loads them from somewhere else.
|
61
|
+
|
62
|
+
// first we need to convert slashes to hyphens, since
|
63
|
+
// they're DOM valid
|
64
|
+
templateName = templateName.replace('/', '-');
|
65
|
+
|
66
|
+
// they can be cached as functions, or as strings.
|
67
|
+
// Strings are template content.
|
68
|
+
if (typeof cache[templateName] === 'undefined') {
|
69
|
+
if (document.getElementById(templateName)) {
|
70
|
+
// stupid hack to turn HTML-encoded templates into strings, see:
|
71
|
+
// http://stackoverflow.com/a/2419664/61435
|
72
|
+
cache[templateName] = $('<div />').html(
|
73
|
+
$(document.getElementById(templateName)).html().trim()).text();
|
74
|
+
}
|
75
|
+
else if (templates[templateName]){
|
76
|
+
cache[templateName] = templates[templateName];
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
return cache[templateName];
|
81
|
+
};
|
82
|
+
|
83
|
+
var compile = function(templateName) {
|
84
|
+
// returns a compiled template.
|
85
|
+
// only works with Hogan.js or if templates pre-compiled.
|
86
|
+
var templateContent = load(templateName),
|
87
|
+
template = null;
|
88
|
+
|
89
|
+
if (typeof templateContent === 'string' && window.Hogan) {
|
90
|
+
template = cache[templateName] = window.Hogan.compile(templateContent);
|
91
|
+
}
|
92
|
+
if (template === null) {
|
93
|
+
$.error("Couldn't compile template " + templateName);
|
94
|
+
}
|
95
|
+
return template;
|
96
|
+
};
|
97
|
+
|
98
|
+
var renderFunction = function(templateName) {
|
99
|
+
// returns a wrapped `render` function
|
100
|
+
// only works with Hogan.js or if templates pre-compiled.
|
101
|
+
var template = compile(templateName);
|
102
|
+
|
103
|
+
return function(context) {
|
104
|
+
return template.render(context);
|
105
|
+
};
|
106
|
+
};
|
107
|
+
|
108
|
+
var render = function(templateName, context) {
|
109
|
+
|
110
|
+
// first we need to try and load the template
|
111
|
+
var template = load(templateName);
|
112
|
+
|
113
|
+
if (typeof template === 'undefined') {
|
114
|
+
$.error('Unknown template ' + templateName);
|
115
|
+
}
|
116
|
+
// pre-compiled hogan templates are objects
|
117
|
+
else if (typeof template === 'object') {
|
118
|
+
// template has been pre-compiled, just render and return it
|
119
|
+
return template.render(context);
|
120
|
+
}
|
121
|
+
|
122
|
+
// template hasn't been pre-compiled yet
|
123
|
+
// so we need to do other things
|
124
|
+
if (window.Hogan) {
|
125
|
+
return window.Hogan.compile(template).render(context);
|
126
|
+
}
|
127
|
+
|
128
|
+
if (window.Mustache) {
|
129
|
+
return window.Mustache.render(template, context);
|
130
|
+
}
|
131
|
+
|
132
|
+
// we don't have Hogan or Mustache, so we need to bail
|
133
|
+
$.error('Must have either Hogan.js or Mustache.js to load string templates');
|
134
|
+
};
|
135
|
+
|
136
|
+
$.fn.mustache = function(templateName, context) {
|
137
|
+
// replaces the content of the passed in element with the content
|
138
|
+
// rendered by Mustache
|
139
|
+
|
140
|
+
return this.html(render(templateName, context));
|
141
|
+
};
|
142
|
+
|
143
|
+
$.mustache = function(templateName, context) {
|
144
|
+
// returns the compiled HTML
|
145
|
+
|
146
|
+
return render(templateName, context);
|
147
|
+
};
|
148
|
+
|
149
|
+
$.mustacheAsFunction = function(templateName) {
|
150
|
+
// returns a function that can be used to render the
|
151
|
+
// mustache template
|
152
|
+
|
153
|
+
return renderFunction(templateName);
|
154
|
+
};
|
155
|
+
|
156
|
+
}(jQuery));
|
@@ -0,0 +1,243 @@
|
|
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
|
+
if(typeof window.Hogan === 'undefined'){
|
17
|
+
window.Hogan = {};
|
18
|
+
}
|
19
|
+
|
20
|
+
(function (Hogan, useArrayBuffer) {
|
21
|
+
Hogan.Template = function (renderFunc, text, compiler, options) {
|
22
|
+
this.r = renderFunc || this.r;
|
23
|
+
this.c = compiler;
|
24
|
+
this.options = options;
|
25
|
+
this.text = text || '';
|
26
|
+
this.buf = (useArrayBuffer) ? [] : '';
|
27
|
+
}
|
28
|
+
|
29
|
+
Hogan.Template.prototype = {
|
30
|
+
// render: replaced by generated code.
|
31
|
+
r: function (context, partials, indent) { return ''; },
|
32
|
+
|
33
|
+
// variable escaping
|
34
|
+
v: hoganEscape,
|
35
|
+
|
36
|
+
// triple stache
|
37
|
+
t: coerceToString,
|
38
|
+
|
39
|
+
render: function render(context, partials, indent) {
|
40
|
+
return this.ri([context], partials || {}, indent);
|
41
|
+
},
|
42
|
+
|
43
|
+
// render internal -- a hook for overrides that catches partials too
|
44
|
+
ri: function (context, partials, indent) {
|
45
|
+
return this.r(context, partials, indent);
|
46
|
+
},
|
47
|
+
|
48
|
+
// tries to find a partial in the curent scope and render it
|
49
|
+
rp: function(name, context, partials, indent) {
|
50
|
+
var partial = partials[name];
|
51
|
+
|
52
|
+
if (!partial) {
|
53
|
+
return '';
|
54
|
+
}
|
55
|
+
|
56
|
+
if (this.c && typeof partial == 'string') {
|
57
|
+
partial = this.c.compile(partial, this.options);
|
58
|
+
}
|
59
|
+
|
60
|
+
return partial.ri(context, partials, indent);
|
61
|
+
},
|
62
|
+
|
63
|
+
// render a section
|
64
|
+
rs: function(context, partials, section) {
|
65
|
+
var tail = context[context.length - 1];
|
66
|
+
|
67
|
+
if (!isArray(tail)) {
|
68
|
+
section(context, partials, this);
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
|
72
|
+
for (var i = 0; i < tail.length; i++) {
|
73
|
+
context.push(tail[i]);
|
74
|
+
section(context, partials, this);
|
75
|
+
context.pop();
|
76
|
+
}
|
77
|
+
},
|
78
|
+
|
79
|
+
// maybe start a section
|
80
|
+
s: function(val, ctx, partials, inverted, start, end, tags) {
|
81
|
+
var pass;
|
82
|
+
|
83
|
+
if (isArray(val) && val.length === 0) {
|
84
|
+
return false;
|
85
|
+
}
|
86
|
+
|
87
|
+
if (typeof val == 'function') {
|
88
|
+
val = this.ls(val, ctx, partials, inverted, start, end, tags);
|
89
|
+
}
|
90
|
+
|
91
|
+
pass = (val === '') || !!val;
|
92
|
+
|
93
|
+
if (!inverted && pass && ctx) {
|
94
|
+
ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
|
95
|
+
}
|
96
|
+
|
97
|
+
return pass;
|
98
|
+
},
|
99
|
+
|
100
|
+
// find values with dotted names
|
101
|
+
d: function(key, ctx, partials, returnFound) {
|
102
|
+
var names = key.split('.'),
|
103
|
+
val = this.f(names[0], ctx, partials, returnFound),
|
104
|
+
cx = null;
|
105
|
+
|
106
|
+
if (key === '.' && isArray(ctx[ctx.length - 2])) {
|
107
|
+
return ctx[ctx.length - 1];
|
108
|
+
}
|
109
|
+
|
110
|
+
for (var i = 1; i < names.length; i++) {
|
111
|
+
if (val && typeof val == 'object' && names[i] in val) {
|
112
|
+
cx = val;
|
113
|
+
val = val[names[i]];
|
114
|
+
} else {
|
115
|
+
val = '';
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
if (returnFound && !val) {
|
120
|
+
return false;
|
121
|
+
}
|
122
|
+
|
123
|
+
if (!returnFound && typeof val == 'function') {
|
124
|
+
ctx.push(cx);
|
125
|
+
val = this.lv(val, ctx, partials);
|
126
|
+
ctx.pop();
|
127
|
+
}
|
128
|
+
|
129
|
+
return val;
|
130
|
+
},
|
131
|
+
|
132
|
+
// find values with normal names
|
133
|
+
f: function(key, ctx, partials, returnFound) {
|
134
|
+
var val = false,
|
135
|
+
v = null,
|
136
|
+
found = false;
|
137
|
+
|
138
|
+
for (var i = ctx.length - 1; i >= 0; i--) {
|
139
|
+
v = ctx[i];
|
140
|
+
if (v && typeof v == 'object' && key in v) {
|
141
|
+
val = v[key];
|
142
|
+
found = true;
|
143
|
+
break;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
if (!found) {
|
148
|
+
return (returnFound) ? false : "";
|
149
|
+
}
|
150
|
+
|
151
|
+
if (!returnFound && typeof val == 'function') {
|
152
|
+
val = this.lv(val, ctx, partials);
|
153
|
+
}
|
154
|
+
|
155
|
+
return val;
|
156
|
+
},
|
157
|
+
|
158
|
+
// higher order templates
|
159
|
+
ho: function(val, cx, partials, text, tags) {
|
160
|
+
var compiler = this.c;
|
161
|
+
var options = this.options;
|
162
|
+
options.delimiters = tags;
|
163
|
+
var text = val.call(cx, text);
|
164
|
+
text = (text == null) ? String(text) : text.toString();
|
165
|
+
this.b(compiler.compile(text, options).render(cx, partials));
|
166
|
+
return false;
|
167
|
+
},
|
168
|
+
|
169
|
+
// template result buffering
|
170
|
+
b: (useArrayBuffer) ? function(s) { this.buf.push(s); } :
|
171
|
+
function(s) { this.buf += s; },
|
172
|
+
fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } :
|
173
|
+
function() { var r = this.buf; this.buf = ''; return r; },
|
174
|
+
|
175
|
+
// lambda replace section
|
176
|
+
ls: function(val, ctx, partials, inverted, start, end, tags) {
|
177
|
+
var cx = ctx[ctx.length - 1],
|
178
|
+
t = null;
|
179
|
+
|
180
|
+
if (!inverted && this.c && val.length > 0) {
|
181
|
+
return this.ho(val, cx, partials, this.text.substring(start, end), tags);
|
182
|
+
}
|
183
|
+
|
184
|
+
t = val.call(cx);
|
185
|
+
|
186
|
+
if (typeof t == 'function') {
|
187
|
+
if (inverted) {
|
188
|
+
return true;
|
189
|
+
} else if (this.c) {
|
190
|
+
return this.ho(t, cx, partials, this.text.substring(start, end), tags);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
return t;
|
195
|
+
},
|
196
|
+
|
197
|
+
// lambda replace variable
|
198
|
+
lv: function(val, ctx, partials) {
|
199
|
+
var cx = ctx[ctx.length - 1];
|
200
|
+
var result = val.call(cx);
|
201
|
+
|
202
|
+
if (typeof result == 'function') {
|
203
|
+
result = coerceToString(result.call(cx));
|
204
|
+
if (this.c && ~result.indexOf("{\u007B")) {
|
205
|
+
return this.c.compile(result, this.options).render(cx, partials);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
return coerceToString(result);
|
210
|
+
}
|
211
|
+
|
212
|
+
};
|
213
|
+
|
214
|
+
var rAmp = /&/g,
|
215
|
+
rLt = /</g,
|
216
|
+
rGt = />/g,
|
217
|
+
rApos =/\'/g,
|
218
|
+
rQuot = /\"/g,
|
219
|
+
hChars =/[&<>\"\']/;
|
220
|
+
|
221
|
+
|
222
|
+
function coerceToString(val) {
|
223
|
+
return String((val === null || val === undefined) ? '' : val);
|
224
|
+
}
|
225
|
+
|
226
|
+
function hoganEscape(str) {
|
227
|
+
str = coerceToString(str);
|
228
|
+
return hChars.test(str) ?
|
229
|
+
str
|
230
|
+
.replace(rAmp,'&')
|
231
|
+
.replace(rLt,'<')
|
232
|
+
.replace(rGt,'>')
|
233
|
+
.replace(rApos,''')
|
234
|
+
.replace(rQuot, '"') :
|
235
|
+
str;
|
236
|
+
}
|
237
|
+
|
238
|
+
var isArray = Array.isArray || function(a) {
|
239
|
+
return Object.prototype.toString.call(a) === '[object Array]';
|
240
|
+
};
|
241
|
+
|
242
|
+
})(typeof exports !== 'undefined' ? exports : Hogan);
|
243
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require_tree ./shared_mustache
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shared_mustache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Edd Sowden
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2013-01-29 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mustache
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.99.4
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: execjs
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.2.4
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: gem_publisher
|
39
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.1.1
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id003
|
48
|
+
description: Share mustache between Rails templates and the browser (using compiled hogan.js templates).
|
49
|
+
email:
|
50
|
+
- edd.sowden@digital.cabinet-office.gov.uk
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- LICENSE.txt
|
61
|
+
- README.md
|
62
|
+
- Rakefile
|
63
|
+
- jenkins.sh
|
64
|
+
- lib/shared_mustache.rb
|
65
|
+
- lib/shared_mustache/config.rb
|
66
|
+
- lib/shared_mustache/engine.rb
|
67
|
+
- lib/shared_mustache/hogan.rb
|
68
|
+
- lib/shared_mustache/railtie.rb
|
69
|
+
- lib/shared_mustache/version.rb
|
70
|
+
- lib/shared_mustache/view_helpers.rb
|
71
|
+
- lib/tasks/shared_mustache.rake
|
72
|
+
- shared_mustache.gemspec
|
73
|
+
- vendor/assets/javascripts/hogan-2.0.0.js
|
74
|
+
- vendor/assets/javascripts/shared_mustache.js
|
75
|
+
- vendor/assets/javascripts/shared_mustache/mustache-loader.js
|
76
|
+
- vendor/assets/javascripts/shared_mustache/template-2.0.0.js
|
77
|
+
- vendor/assets/javascripts/templates.js
|
78
|
+
homepage: ""
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 1469856526765400623
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 1469856526765400623
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.24
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Share mustache between Rails templates and the browser (using compiled hogan.js templates).
|
111
|
+
test_files: []
|
112
|
+
|