├── .gitignore
├── CONTRIBUTORS.md
├── MIT-LICENSE.txt
├── README.md
├── build.js
├── build.sh
├── example-build
├── build.txt
├── index.html
├── scripts
│ ├── libs
│ │ ├── handlebars.runtime.js
│ │ └── require.js
│ └── main.js
└── templates
│ └── message.html
├── example
├── index.html
├── scripts
│ ├── libs
│ │ ├── handlebars.js
│ │ ├── handlebars.runtime.js
│ │ ├── hbars.js
│ │ ├── require.js
│ │ └── text.js
│ └── main.js
└── templates
│ └── message.html
├── hbars.js
├── libs
└── r.js
├── package.json
└── server.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS generated files #
2 | ######################
3 | .DS_Store
4 | .DS_Store?
5 | ._*
6 | .Spotlight-V100
7 | .Trashes
8 | Icon?
9 | ehthumbs.db
10 | Thumbs.db
11 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | # requirejs-handlebars contributors
2 |
3 | You can check out the [contribution graphs on github](https://github.com/jfparadis/requirejs-handlebars/contributors).
4 |
5 | ```
6 | $ git shortlog -s -n | cut -c8-
7 | Jean-Francois Paradis
8 | Chris Contolini
9 | Miller Medeiros
10 | ```
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2012 J.F. Paradis and other contributors
2 | http://github.com/skaimauve/reword
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | requirejs-handlebars
2 | ====================
3 |
4 | This is an AMD loader for [Handlebars semantic templates](http://handlebarsjs.com/) which can be used as a drop-in replacement to [SlexAxton/require-handlebars-plugin](http://github.com/SlexAxton/require-handlebars-plugin/blob/master/hbs.js)
5 |
6 |
7 | ## Overview
8 |
9 | - Uses an external HandlebarsJS engine (distributed by the Handlebars team).
10 | - Uses the official ``text`` loader plugin maintained by the RequireJS team.
11 | - You don't have to specify the template file extension (``.html is assumed``, but this is configurable).
12 |
13 | Notes:
14 |
15 | - The ``text`` and ``hbar`` plugins can be removed at build-time using ``r.js`` (with the ``stubModules`` setting).
16 | - The extension ``.html`` is assumed, and this makes loading templates similar to loading JavaScript files with RequireJS (all extensions are assumed).
17 |
18 | ## Changelog
19 |
20 | 0.0.1 Initial version
21 |
22 | 0.0.2 Various updates:
23 | - Add template path option to hbar.js (thanks drewrichards)
24 | - Updated require.js to 2.1.8 , and r.js to 2.1.8
25 | - Updated handlebar.js to 1.0.0
26 |
27 | ## Installation
28 |
29 | Download HandlebarsJS and RequireJS-text:
30 |
31 | - [HandlebarsJS](http://handlebarsjs.com/)
32 | - [RequireJS-text](http://requirejs.org/docs/download.html#text)
33 |
34 | Typically, you would place them in a ``scripts/libs`` folder then create a ``scripts/main.js`` file to alias them and to shim Handlebars:
35 |
36 | ```
37 | require.config({
38 | paths: {
39 | Handlebars: 'libs/handlebars',
40 | text: 'libs/text'
41 | hbars: 'libs/hbars'
42 | },
43 | shim: {
44 | Handlebars: {
45 | exports: 'Handlebars'
46 | }
47 | }
48 | });
49 | ```
50 |
51 | ## Usage
52 |
53 | Specify the plugin using ``hbars!`` followed by the template file:
54 |
55 | ```
56 | require(['backbone', 'hbars!template'], function (Backbone, template) {
57 | return Backbone.View.extend({
58 | initialize: function(){
59 | this.render();
60 | },
61 | render: function(){
62 | this.$el.html(template({message: 'hello'}));
63 | });
64 | });
65 | ```
66 |
67 | ## Customization
68 |
69 | You can specify the template file extension in your main.js:
70 |
71 | ```
72 | require.config({
73 |
74 | // some paths and shims
75 |
76 | hbars: {
77 | extension: '.hbs', // default = '.html'
78 | compileOptions: {} // options object which is passed to Handlebars compiler
79 | }
80 | });
81 | ```
82 |
83 | ## Optimization
84 |
85 | This plugin is compatible with [r.js](http://requirejs.org/docs/optimization.html).
86 |
87 | Optimization brings three benefits to a project:
88 |
89 | - The templates are bundled within your code and not dynamically loaded which reduces the number of HTTP requests.
90 |
91 | - The templates are pre-compiled before being bundled which reduces the work the client has to do.
92 |
93 | - Since templates are pre-compiled during build you can use the handlebars.runtime which is ~1KB after minification+gzip.
94 |
95 | If you don't use any extra tool to manage your libraries, need to manually replace the handlebars.runtime during compilation by adding this routine to your main.js. See the example for how it works:
96 |
97 | ```
98 | onBuildWrite : function(moduleName, path, content){
99 |
100 | // replace handlebars with the runtime version
101 | if (moduleName === 'Handlebars') {
102 | path = path.replace('handlebars.js','handlebars.runtime.js');
103 | content = fs.readFileSync(path).toString();
104 | content = content.replace(/(define\()(function)/, '$1"handlebars", $2');
105 | }
106 | return content;
107 | }
108 | ```
109 |
110 | The most important build options are:
111 |
112 | ```stubModules: ['text', 'hbars']```
113 |
114 | The list of modules to stub out in the optimized file, i.e. the code is replaced with ``define('module',{});`` by ``r.js``
115 |
116 | ```removeCombined: true```
117 |
118 | Removes from the output folder the files combined into a build.
119 |
120 | ## Example
121 |
122 | Sample build options are in the root ``build.js``. Compile the example using:
123 |
124 | ```
125 | $ ./build.sh
126 | ```
127 |
128 | Copy the ``example`` and ``example-build`` folders to your web server (``text`` is not compatible with the ``file://`` protocol and opening ``index.hml`` directly from your browser will not work).
129 |
130 | ### Using a test server
131 |
132 | Alternatively, you can use Connect and NodeJS to spin a web server:
133 |
134 | - Install ``connect`` using ``npm`` and launch the server with NodeJS:
135 |
136 | ```
137 | $ npm install -g connect
138 | $ npm link connect
139 | $ node server.js
140 | ```
141 |
142 | Go to [http://localhost:9000/example](http://localhost:9000/example). Your browser should load:
143 |
144 | - index.html
145 | - require.js
146 | - main.js
147 | - hbars.js
148 | - handlebars.js
149 | - text.js
150 | - message.html
151 |
152 | Go to [http://localhost:9000/example-build](http://localhost:9000/example-build). Your browser should load:
153 |
154 | - index.html
155 | - require.js
156 | - main.js
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | // See https://github.com/jrburke/r.js/blob/master/build/example.build.js
2 | ({
3 | // Assume your scripts are in a subdirectory under this path.
4 | appDir: 'example',
5 |
6 | // By default, all modules are located relative to this path.
7 | baseUrl: 'scripts',
8 |
9 | // Location of the runtime config be read for the build.
10 | mainConfigFile: 'example/scripts/main.js',
11 |
12 | //The directory path to save the output.
13 | dir: 'example-build',
14 |
15 | // If you do not want uglifyjs optimization.
16 | optimize: 'none',
17 |
18 | // Inlines any text! dependencies, to avoid separate requests.
19 | inlineText: true,
20 |
21 | // Modules to stub out in the optimized file.
22 | stubModules: ['text', 'hbars'],
23 |
24 | // Files combined into a build layer will be removed from the output folder.
25 | removeCombined: true,
26 |
27 | // This option will turn off the auto-preservation.
28 | preserveLicenseComments: false,
29 |
30 | //List the modules that will be optimized.
31 | modules: [
32 | {
33 | name: "main" // main config file
34 | }
35 | ]
36 | })
37 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | rm -rf example-build
4 | node libs/r.js -o build.js
--------------------------------------------------------------------------------
/example-build/build.txt:
--------------------------------------------------------------------------------
1 |
2 | scripts/main.js
3 | ----------------
4 | scripts/libs/text.js
5 | scripts/libs/handlebars.js
6 | scripts/libs/hbars.js
7 | hbars!templates/message
8 | scripts/main.js
9 |
--------------------------------------------------------------------------------
/example-build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
RequireJS Plugin for Handlebars semantic templates
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example-build/scripts/libs/handlebars.runtime.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Copyright (C) 2011 by Yehuda Katz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 | */
24 |
25 | // lib/handlebars/browser-prefix.js
26 | var Handlebars = {};
27 |
28 | (function(Handlebars, undefined) {
29 | ;
30 | // lib/handlebars/base.js
31 |
32 | Handlebars.VERSION = "1.0.0";
33 | Handlebars.COMPILER_REVISION = 4;
34 |
35 | Handlebars.REVISION_CHANGES = {
36 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
37 | 2: '== 1.0.0-rc.3',
38 | 3: '== 1.0.0-rc.4',
39 | 4: '>= 1.0.0'
40 | };
41 |
42 | Handlebars.helpers = {};
43 | Handlebars.partials = {};
44 |
45 | var toString = Object.prototype.toString,
46 | functionType = '[object Function]',
47 | objectType = '[object Object]';
48 |
49 | Handlebars.registerHelper = function(name, fn, inverse) {
50 | if (toString.call(name) === objectType) {
51 | if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
52 | Handlebars.Utils.extend(this.helpers, name);
53 | } else {
54 | if (inverse) { fn.not = inverse; }
55 | this.helpers[name] = fn;
56 | }
57 | };
58 |
59 | Handlebars.registerPartial = function(name, str) {
60 | if (toString.call(name) === objectType) {
61 | Handlebars.Utils.extend(this.partials, name);
62 | } else {
63 | this.partials[name] = str;
64 | }
65 | };
66 |
67 | Handlebars.registerHelper('helperMissing', function(arg) {
68 | if(arguments.length === 2) {
69 | return undefined;
70 | } else {
71 | throw new Error("Missing helper: '" + arg + "'");
72 | }
73 | });
74 |
75 | Handlebars.registerHelper('blockHelperMissing', function(context, options) {
76 | var inverse = options.inverse || function() {}, fn = options.fn;
77 |
78 | var type = toString.call(context);
79 |
80 | if(type === functionType) { context = context.call(this); }
81 |
82 | if(context === true) {
83 | return fn(this);
84 | } else if(context === false || context == null) {
85 | return inverse(this);
86 | } else if(type === "[object Array]") {
87 | if(context.length > 0) {
88 | return Handlebars.helpers.each(context, options);
89 | } else {
90 | return inverse(this);
91 | }
92 | } else {
93 | return fn(context);
94 | }
95 | });
96 |
97 | Handlebars.K = function() {};
98 |
99 | Handlebars.createFrame = Object.create || function(object) {
100 | Handlebars.K.prototype = object;
101 | var obj = new Handlebars.K();
102 | Handlebars.K.prototype = null;
103 | return obj;
104 | };
105 |
106 | Handlebars.logger = {
107 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
108 |
109 | methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
110 |
111 | // can be overridden in the host environment
112 | log: function(level, obj) {
113 | if (Handlebars.logger.level <= level) {
114 | var method = Handlebars.logger.methodMap[level];
115 | if (typeof console !== 'undefined' && console[method]) {
116 | console[method].call(console, obj);
117 | }
118 | }
119 | }
120 | };
121 |
122 | Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
123 |
124 | Handlebars.registerHelper('each', function(context, options) {
125 | var fn = options.fn, inverse = options.inverse;
126 | var i = 0, ret = "", data;
127 |
128 | var type = toString.call(context);
129 | if(type === functionType) { context = context.call(this); }
130 |
131 | if (options.data) {
132 | data = Handlebars.createFrame(options.data);
133 | }
134 |
135 | if(context && typeof context === 'object') {
136 | if(context instanceof Array){
137 | for(var j = context.length; i": ">",
212 | '"': """,
213 | "'": "'",
214 | "`": "`"
215 | };
216 |
217 | var badChars = /[&<>"'`]/g;
218 | var possible = /[&<>"'`]/;
219 |
220 | var escapeChar = function(chr) {
221 | return escape[chr] || "&";
222 | };
223 |
224 | Handlebars.Utils = {
225 | extend: function(obj, value) {
226 | for(var key in value) {
227 | if(value.hasOwnProperty(key)) {
228 | obj[key] = value[key];
229 | }
230 | }
231 | },
232 |
233 | escapeExpression: function(string) {
234 | // don't escape SafeStrings, since they're already safe
235 | if (string instanceof Handlebars.SafeString) {
236 | return string.toString();
237 | } else if (string == null || string === false) {
238 | return "";
239 | }
240 |
241 | // Force a string conversion as this will be done by the append regardless and
242 | // the regex test will do this transparently behind the scenes, causing issues if
243 | // an object's to string has escaped characters in it.
244 | string = string.toString();
245 |
246 | if(!possible.test(string)) { return string; }
247 | return string.replace(badChars, escapeChar);
248 | },
249 |
250 | isEmpty: function(value) {
251 | if (!value && value !== 0) {
252 | return true;
253 | } else if(toString.call(value) === "[object Array]" && value.length === 0) {
254 | return true;
255 | } else {
256 | return false;
257 | }
258 | }
259 | };
260 | ;
261 | // lib/handlebars/runtime.js
262 |
263 | Handlebars.VM = {
264 | template: function(templateSpec) {
265 | // Just add water
266 | var container = {
267 | escapeExpression: Handlebars.Utils.escapeExpression,
268 | invokePartial: Handlebars.VM.invokePartial,
269 | programs: [],
270 | program: function(i, fn, data) {
271 | var programWrapper = this.programs[i];
272 | if(data) {
273 | programWrapper = Handlebars.VM.program(i, fn, data);
274 | } else if (!programWrapper) {
275 | programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
276 | }
277 | return programWrapper;
278 | },
279 | merge: function(param, common) {
280 | var ret = param || common;
281 |
282 | if (param && common) {
283 | ret = {};
284 | Handlebars.Utils.extend(ret, common);
285 | Handlebars.Utils.extend(ret, param);
286 | }
287 | return ret;
288 | },
289 | programWithDepth: Handlebars.VM.programWithDepth,
290 | noop: Handlebars.VM.noop,
291 | compilerInfo: null
292 | };
293 |
294 | return function(context, options) {
295 | options = options || {};
296 | var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
297 |
298 | var compilerInfo = container.compilerInfo || [],
299 | compilerRevision = compilerInfo[0] || 1,
300 | currentRevision = Handlebars.COMPILER_REVISION;
301 |
302 | if (compilerRevision !== currentRevision) {
303 | if (compilerRevision < currentRevision) {
304 | var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
305 | compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
306 | throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
307 | "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
308 | } else {
309 | // Use the embedded version info since the runtime doesn't know about this revision yet
310 | throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
311 | "Please update your runtime to a newer version ("+compilerInfo[1]+").";
312 | }
313 | }
314 |
315 | return result;
316 | };
317 | },
318 |
319 | programWithDepth: function(i, fn, data /*, $depth */) {
320 | var args = Array.prototype.slice.call(arguments, 3);
321 |
322 | var program = function(context, options) {
323 | options = options || {};
324 |
325 | return fn.apply(this, [context, options.data || data].concat(args));
326 | };
327 | program.program = i;
328 | program.depth = args.length;
329 | return program;
330 | },
331 | program: function(i, fn, data) {
332 | var program = function(context, options) {
333 | options = options || {};
334 |
335 | return fn(context, options.data || data);
336 | };
337 | program.program = i;
338 | program.depth = 0;
339 | return program;
340 | },
341 | noop: function() { return ""; },
342 | invokePartial: function(partial, name, context, helpers, partials, data) {
343 | var options = { helpers: helpers, partials: partials, data: data };
344 |
345 | if(partial === undefined) {
346 | throw new Handlebars.Exception("The partial " + name + " could not be found");
347 | } else if(partial instanceof Function) {
348 | return partial(context, options);
349 | } else if (!Handlebars.compile) {
350 | throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
351 | } else {
352 | partials[name] = Handlebars.compile(partial, {data: data !== undefined});
353 | return partials[name](context, options);
354 | }
355 | }
356 | };
357 |
358 | Handlebars.template = Handlebars.VM.template;
359 | ;
360 | // lib/handlebars/browser-suffix.js
361 | })(Handlebars);
362 | ;
363 |
--------------------------------------------------------------------------------
/example-build/scripts/main.js:
--------------------------------------------------------------------------------
1 |
2 | define('text',{});
3 | /*
4 |
5 | Copyright (C) 2011 by Yehuda Katz
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
25 | */
26 |
27 | // lib/handlebars/browser-prefix.js
28 | var Handlebars = {};
29 |
30 | (function(Handlebars, undefined) {
31 | ;
32 | // lib/handlebars/base.js
33 |
34 | Handlebars.VERSION = "1.0.0";
35 | Handlebars.COMPILER_REVISION = 4;
36 |
37 | Handlebars.REVISION_CHANGES = {
38 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
39 | 2: '== 1.0.0-rc.3',
40 | 3: '== 1.0.0-rc.4',
41 | 4: '>= 1.0.0'
42 | };
43 |
44 | Handlebars.helpers = {};
45 | Handlebars.partials = {};
46 |
47 | var toString = Object.prototype.toString,
48 | functionType = '[object Function]',
49 | objectType = '[object Object]';
50 |
51 | Handlebars.registerHelper = function(name, fn, inverse) {
52 | if (toString.call(name) === objectType) {
53 | if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
54 | Handlebars.Utils.extend(this.helpers, name);
55 | } else {
56 | if (inverse) { fn.not = inverse; }
57 | this.helpers[name] = fn;
58 | }
59 | };
60 |
61 | Handlebars.registerPartial = function(name, str) {
62 | if (toString.call(name) === objectType) {
63 | Handlebars.Utils.extend(this.partials, name);
64 | } else {
65 | this.partials[name] = str;
66 | }
67 | };
68 |
69 | Handlebars.registerHelper('helperMissing', function(arg) {
70 | if(arguments.length === 2) {
71 | return undefined;
72 | } else {
73 | throw new Error("Missing helper: '" + arg + "'");
74 | }
75 | });
76 |
77 | Handlebars.registerHelper('blockHelperMissing', function(context, options) {
78 | var inverse = options.inverse || function() {}, fn = options.fn;
79 |
80 | var type = toString.call(context);
81 |
82 | if(type === functionType) { context = context.call(this); }
83 |
84 | if(context === true) {
85 | return fn(this);
86 | } else if(context === false || context == null) {
87 | return inverse(this);
88 | } else if(type === "[object Array]") {
89 | if(context.length > 0) {
90 | return Handlebars.helpers.each(context, options);
91 | } else {
92 | return inverse(this);
93 | }
94 | } else {
95 | return fn(context);
96 | }
97 | });
98 |
99 | Handlebars.K = function() {};
100 |
101 | Handlebars.createFrame = Object.create || function(object) {
102 | Handlebars.K.prototype = object;
103 | var obj = new Handlebars.K();
104 | Handlebars.K.prototype = null;
105 | return obj;
106 | };
107 |
108 | Handlebars.logger = {
109 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
110 |
111 | methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
112 |
113 | // can be overridden in the host environment
114 | log: function(level, obj) {
115 | if (Handlebars.logger.level <= level) {
116 | var method = Handlebars.logger.methodMap[level];
117 | if (typeof console !== 'undefined' && console[method]) {
118 | console[method].call(console, obj);
119 | }
120 | }
121 | }
122 | };
123 |
124 | Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
125 |
126 | Handlebars.registerHelper('each', function(context, options) {
127 | var fn = options.fn, inverse = options.inverse;
128 | var i = 0, ret = "", data;
129 |
130 | var type = toString.call(context);
131 | if(type === functionType) { context = context.call(this); }
132 |
133 | if (options.data) {
134 | data = Handlebars.createFrame(options.data);
135 | }
136 |
137 | if(context && typeof context === 'object') {
138 | if(context instanceof Array){
139 | for(var j = context.length; i": ">",
214 | '"': """,
215 | "'": "'",
216 | "`": "`"
217 | };
218 |
219 | var badChars = /[&<>"'`]/g;
220 | var possible = /[&<>"'`]/;
221 |
222 | var escapeChar = function(chr) {
223 | return escape[chr] || "&";
224 | };
225 |
226 | Handlebars.Utils = {
227 | extend: function(obj, value) {
228 | for(var key in value) {
229 | if(value.hasOwnProperty(key)) {
230 | obj[key] = value[key];
231 | }
232 | }
233 | },
234 |
235 | escapeExpression: function(string) {
236 | // don't escape SafeStrings, since they're already safe
237 | if (string instanceof Handlebars.SafeString) {
238 | return string.toString();
239 | } else if (string == null || string === false) {
240 | return "";
241 | }
242 |
243 | // Force a string conversion as this will be done by the append regardless and
244 | // the regex test will do this transparently behind the scenes, causing issues if
245 | // an object's to string has escaped characters in it.
246 | string = string.toString();
247 |
248 | if(!possible.test(string)) { return string; }
249 | return string.replace(badChars, escapeChar);
250 | },
251 |
252 | isEmpty: function(value) {
253 | if (!value && value !== 0) {
254 | return true;
255 | } else if(toString.call(value) === "[object Array]" && value.length === 0) {
256 | return true;
257 | } else {
258 | return false;
259 | }
260 | }
261 | };
262 | ;
263 | // lib/handlebars/runtime.js
264 |
265 | Handlebars.VM = {
266 | template: function(templateSpec) {
267 | // Just add water
268 | var container = {
269 | escapeExpression: Handlebars.Utils.escapeExpression,
270 | invokePartial: Handlebars.VM.invokePartial,
271 | programs: [],
272 | program: function(i, fn, data) {
273 | var programWrapper = this.programs[i];
274 | if(data) {
275 | programWrapper = Handlebars.VM.program(i, fn, data);
276 | } else if (!programWrapper) {
277 | programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
278 | }
279 | return programWrapper;
280 | },
281 | merge: function(param, common) {
282 | var ret = param || common;
283 |
284 | if (param && common) {
285 | ret = {};
286 | Handlebars.Utils.extend(ret, common);
287 | Handlebars.Utils.extend(ret, param);
288 | }
289 | return ret;
290 | },
291 | programWithDepth: Handlebars.VM.programWithDepth,
292 | noop: Handlebars.VM.noop,
293 | compilerInfo: null
294 | };
295 |
296 | return function(context, options) {
297 | options = options || {};
298 | var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
299 |
300 | var compilerInfo = container.compilerInfo || [],
301 | compilerRevision = compilerInfo[0] || 1,
302 | currentRevision = Handlebars.COMPILER_REVISION;
303 |
304 | if (compilerRevision !== currentRevision) {
305 | if (compilerRevision < currentRevision) {
306 | var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
307 | compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
308 | throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
309 | "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
310 | } else {
311 | // Use the embedded version info since the runtime doesn't know about this revision yet
312 | throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
313 | "Please update your runtime to a newer version ("+compilerInfo[1]+").";
314 | }
315 | }
316 |
317 | return result;
318 | };
319 | },
320 |
321 | programWithDepth: function(i, fn, data /*, $depth */) {
322 | var args = Array.prototype.slice.call(arguments, 3);
323 |
324 | var program = function(context, options) {
325 | options = options || {};
326 |
327 | return fn.apply(this, [context, options.data || data].concat(args));
328 | };
329 | program.program = i;
330 | program.depth = args.length;
331 | return program;
332 | },
333 | program: function(i, fn, data) {
334 | var program = function(context, options) {
335 | options = options || {};
336 |
337 | return fn(context, options.data || data);
338 | };
339 | program.program = i;
340 | program.depth = 0;
341 | return program;
342 | },
343 | noop: function() { return ""; },
344 | invokePartial: function(partial, name, context, helpers, partials, data) {
345 | var options = { helpers: helpers, partials: partials, data: data };
346 |
347 | if(partial === undefined) {
348 | throw new Handlebars.Exception("The partial " + name + " could not be found");
349 | } else if(partial instanceof Function) {
350 | return partial(context, options);
351 | } else if (!Handlebars.compile) {
352 | throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
353 | } else {
354 | partials[name] = Handlebars.compile(partial, {data: data !== undefined});
355 | return partials[name](context, options);
356 | }
357 | }
358 | };
359 |
360 | Handlebars.template = Handlebars.VM.template;
361 | ;
362 | // lib/handlebars/browser-suffix.js
363 | })(Handlebars);
364 | ;
365 |
366 | define("Handlebars", (function (global) {
367 | return function () {
368 | var ret, fn;
369 | return ret || global.Handlebars;
370 | };
371 | }(this)));
372 |
373 | define('hbars',{load: function(id){throw new Error("Dynamic load not allowed: " + id);}});
374 | define('hbars!templates/message', ['Handlebars'], function (Handlebars) { return Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
375 | this.compilerInfo = [4,'>= 1.0.0'];
376 | helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
377 | var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression;
378 |
379 |
380 | buffer += "\n Message\n
\n\n ";
381 | if (stack1 = helpers.message) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
382 | else { stack1 = depth0.message; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
383 | buffer += escapeExpression(stack1)
384 | + "\n
";
385 | return buffer;
386 | }); });
387 |
388 | require({
389 | paths: {
390 | templates: '../templates',
391 | Handlebars: 'libs/handlebars',
392 | text: 'libs/text',
393 | hbars: 'libs/hbars'
394 | },
395 | shim: {
396 | Handlebars: {
397 | exports: 'Handlebars'
398 | }
399 | },
400 |
401 | onBuildWrite : function(moduleName, path, content){
402 |
403 | // replace handlebars with the runtime version
404 | if (moduleName === 'Handlebars') {
405 | path = path.replace('handlebars.js','handlebars.runtime.js');
406 | content = fs.readFileSync(path).toString();
407 | content = content.replace(/(define\()(function)/, '$1"handlebars", $2');
408 | }
409 | return content;
410 | }
411 |
412 | }, ['hbars!templates/message'], function (template) {
413 |
414 |
415 | console.log('template = ' + template);
416 | document.body.innerHTML += template({message: 'Hello World!'});
417 | });
418 |
419 | define("main", function(){});
420 |
--------------------------------------------------------------------------------
/example-build/templates/message.html:
--------------------------------------------------------------------------------
1 |
2 | Message
3 |
4 |
5 | {{ message }}
6 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 | RequireJS Plugin for Handlebars semantic templates
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/scripts/libs/handlebars.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Copyright (C) 2011 by Yehuda Katz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 | */
24 |
25 | // lib/handlebars/browser-prefix.js
26 | var Handlebars = {};
27 |
28 | (function(Handlebars, undefined) {
29 | ;
30 | // lib/handlebars/base.js
31 |
32 | Handlebars.VERSION = "1.0.0";
33 | Handlebars.COMPILER_REVISION = 4;
34 |
35 | Handlebars.REVISION_CHANGES = {
36 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
37 | 2: '== 1.0.0-rc.3',
38 | 3: '== 1.0.0-rc.4',
39 | 4: '>= 1.0.0'
40 | };
41 |
42 | Handlebars.helpers = {};
43 | Handlebars.partials = {};
44 |
45 | var toString = Object.prototype.toString,
46 | functionType = '[object Function]',
47 | objectType = '[object Object]';
48 |
49 | Handlebars.registerHelper = function(name, fn, inverse) {
50 | if (toString.call(name) === objectType) {
51 | if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
52 | Handlebars.Utils.extend(this.helpers, name);
53 | } else {
54 | if (inverse) { fn.not = inverse; }
55 | this.helpers[name] = fn;
56 | }
57 | };
58 |
59 | Handlebars.registerPartial = function(name, str) {
60 | if (toString.call(name) === objectType) {
61 | Handlebars.Utils.extend(this.partials, name);
62 | } else {
63 | this.partials[name] = str;
64 | }
65 | };
66 |
67 | Handlebars.registerHelper('helperMissing', function(arg) {
68 | if(arguments.length === 2) {
69 | return undefined;
70 | } else {
71 | throw new Error("Missing helper: '" + arg + "'");
72 | }
73 | });
74 |
75 | Handlebars.registerHelper('blockHelperMissing', function(context, options) {
76 | var inverse = options.inverse || function() {}, fn = options.fn;
77 |
78 | var type = toString.call(context);
79 |
80 | if(type === functionType) { context = context.call(this); }
81 |
82 | if(context === true) {
83 | return fn(this);
84 | } else if(context === false || context == null) {
85 | return inverse(this);
86 | } else if(type === "[object Array]") {
87 | if(context.length > 0) {
88 | return Handlebars.helpers.each(context, options);
89 | } else {
90 | return inverse(this);
91 | }
92 | } else {
93 | return fn(context);
94 | }
95 | });
96 |
97 | Handlebars.K = function() {};
98 |
99 | Handlebars.createFrame = Object.create || function(object) {
100 | Handlebars.K.prototype = object;
101 | var obj = new Handlebars.K();
102 | Handlebars.K.prototype = null;
103 | return obj;
104 | };
105 |
106 | Handlebars.logger = {
107 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
108 |
109 | methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
110 |
111 | // can be overridden in the host environment
112 | log: function(level, obj) {
113 | if (Handlebars.logger.level <= level) {
114 | var method = Handlebars.logger.methodMap[level];
115 | if (typeof console !== 'undefined' && console[method]) {
116 | console[method].call(console, obj);
117 | }
118 | }
119 | }
120 | };
121 |
122 | Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
123 |
124 | Handlebars.registerHelper('each', function(context, options) {
125 | var fn = options.fn, inverse = options.inverse;
126 | var i = 0, ret = "", data;
127 |
128 | var type = toString.call(context);
129 | if(type === functionType) { context = context.call(this); }
130 |
131 | if (options.data) {
132 | data = Handlebars.createFrame(options.data);
133 | }
134 |
135 | if(context && typeof context === 'object') {
136 | if(context instanceof Array){
137 | for(var j = context.length; i 2) {
351 | expected.push("'" + this.terminals_[p] + "'");
352 | }
353 | if (this.lexer.showPosition) {
354 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
355 | } else {
356 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
357 | }
358 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
359 | }
360 | }
361 | if (action[0] instanceof Array && action.length > 1) {
362 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
363 | }
364 | switch (action[0]) {
365 | case 1:
366 | stack.push(symbol);
367 | vstack.push(this.lexer.yytext);
368 | lstack.push(this.lexer.yylloc);
369 | stack.push(action[1]);
370 | symbol = null;
371 | if (!preErrorSymbol) {
372 | yyleng = this.lexer.yyleng;
373 | yytext = this.lexer.yytext;
374 | yylineno = this.lexer.yylineno;
375 | yyloc = this.lexer.yylloc;
376 | if (recovering > 0)
377 | recovering--;
378 | } else {
379 | symbol = preErrorSymbol;
380 | preErrorSymbol = null;
381 | }
382 | break;
383 | case 2:
384 | len = this.productions_[action[1]][1];
385 | yyval.$ = vstack[vstack.length - len];
386 | yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
387 | if (ranges) {
388 | yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
389 | }
390 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
391 | if (typeof r !== "undefined") {
392 | return r;
393 | }
394 | if (len) {
395 | stack = stack.slice(0, -1 * len * 2);
396 | vstack = vstack.slice(0, -1 * len);
397 | lstack = lstack.slice(0, -1 * len);
398 | }
399 | stack.push(this.productions_[action[1]][0]);
400 | vstack.push(yyval.$);
401 | lstack.push(yyval._$);
402 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
403 | stack.push(newState);
404 | break;
405 | case 3:
406 | return true;
407 | }
408 | }
409 | return true;
410 | }
411 | };
412 | /* Jison generated lexer */
413 | var lexer = (function(){
414 | var lexer = ({EOF:1,
415 | parseError:function parseError(str, hash) {
416 | if (this.yy.parser) {
417 | this.yy.parser.parseError(str, hash);
418 | } else {
419 | throw new Error(str);
420 | }
421 | },
422 | setInput:function (input) {
423 | this._input = input;
424 | this._more = this._less = this.done = false;
425 | this.yylineno = this.yyleng = 0;
426 | this.yytext = this.matched = this.match = '';
427 | this.conditionStack = ['INITIAL'];
428 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
429 | if (this.options.ranges) this.yylloc.range = [0,0];
430 | this.offset = 0;
431 | return this;
432 | },
433 | input:function () {
434 | var ch = this._input[0];
435 | this.yytext += ch;
436 | this.yyleng++;
437 | this.offset++;
438 | this.match += ch;
439 | this.matched += ch;
440 | var lines = ch.match(/(?:\r\n?|\n).*/g);
441 | if (lines) {
442 | this.yylineno++;
443 | this.yylloc.last_line++;
444 | } else {
445 | this.yylloc.last_column++;
446 | }
447 | if (this.options.ranges) this.yylloc.range[1]++;
448 |
449 | this._input = this._input.slice(1);
450 | return ch;
451 | },
452 | unput:function (ch) {
453 | var len = ch.length;
454 | var lines = ch.split(/(?:\r\n?|\n)/g);
455 |
456 | this._input = ch + this._input;
457 | this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
458 | //this.yyleng -= len;
459 | this.offset -= len;
460 | var oldLines = this.match.split(/(?:\r\n?|\n)/g);
461 | this.match = this.match.substr(0, this.match.length-1);
462 | this.matched = this.matched.substr(0, this.matched.length-1);
463 |
464 | if (lines.length-1) this.yylineno -= lines.length-1;
465 | var r = this.yylloc.range;
466 |
467 | this.yylloc = {first_line: this.yylloc.first_line,
468 | last_line: this.yylineno+1,
469 | first_column: this.yylloc.first_column,
470 | last_column: lines ?
471 | (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
472 | this.yylloc.first_column - len
473 | };
474 |
475 | if (this.options.ranges) {
476 | this.yylloc.range = [r[0], r[0] + this.yyleng - len];
477 | }
478 | return this;
479 | },
480 | more:function () {
481 | this._more = true;
482 | return this;
483 | },
484 | less:function (n) {
485 | this.unput(this.match.slice(n));
486 | },
487 | pastInput:function () {
488 | var past = this.matched.substr(0, this.matched.length - this.match.length);
489 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
490 | },
491 | upcomingInput:function () {
492 | var next = this.match;
493 | if (next.length < 20) {
494 | next += this._input.substr(0, 20-next.length);
495 | }
496 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
497 | },
498 | showPosition:function () {
499 | var pre = this.pastInput();
500 | var c = new Array(pre.length + 1).join("-");
501 | return pre + this.upcomingInput() + "\n" + c+"^";
502 | },
503 | next:function () {
504 | if (this.done) {
505 | return this.EOF;
506 | }
507 | if (!this._input) this.done = true;
508 |
509 | var token,
510 | match,
511 | tempMatch,
512 | index,
513 | col,
514 | lines;
515 | if (!this._more) {
516 | this.yytext = '';
517 | this.match = '';
518 | }
519 | var rules = this._currentRules();
520 | for (var i=0;i < rules.length; i++) {
521 | tempMatch = this._input.match(this.rules[rules[i]]);
522 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
523 | match = tempMatch;
524 | index = i;
525 | if (!this.options.flex) break;
526 | }
527 | }
528 | if (match) {
529 | lines = match[0].match(/(?:\r\n?|\n).*/g);
530 | if (lines) this.yylineno += lines.length;
531 | this.yylloc = {first_line: this.yylloc.last_line,
532 | last_line: this.yylineno+1,
533 | first_column: this.yylloc.last_column,
534 | last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
535 | this.yytext += match[0];
536 | this.match += match[0];
537 | this.matches = match;
538 | this.yyleng = this.yytext.length;
539 | if (this.options.ranges) {
540 | this.yylloc.range = [this.offset, this.offset += this.yyleng];
541 | }
542 | this._more = false;
543 | this._input = this._input.slice(match[0].length);
544 | this.matched += match[0];
545 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
546 | if (this.done && this._input) this.done = false;
547 | if (token) return token;
548 | else return;
549 | }
550 | if (this._input === "") {
551 | return this.EOF;
552 | } else {
553 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
554 | {text: "", token: null, line: this.yylineno});
555 | }
556 | },
557 | lex:function lex() {
558 | var r = this.next();
559 | if (typeof r !== 'undefined') {
560 | return r;
561 | } else {
562 | return this.lex();
563 | }
564 | },
565 | begin:function begin(condition) {
566 | this.conditionStack.push(condition);
567 | },
568 | popState:function popState() {
569 | return this.conditionStack.pop();
570 | },
571 | _currentRules:function _currentRules() {
572 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
573 | },
574 | topState:function () {
575 | return this.conditionStack[this.conditionStack.length-2];
576 | },
577 | pushState:function begin(condition) {
578 | this.begin(condition);
579 | }});
580 | lexer.options = {};
581 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
582 |
583 | var YYSTATE=YY_START
584 | switch($avoiding_name_collisions) {
585 | case 0: yy_.yytext = "\\"; return 14;
586 | break;
587 | case 1:
588 | if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
589 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
590 | if(yy_.yytext) return 14;
591 |
592 | break;
593 | case 2: return 14;
594 | break;
595 | case 3:
596 | if(yy_.yytext.slice(-1) !== "\\") this.popState();
597 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
598 | return 14;
599 |
600 | break;
601 | case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
602 | break;
603 | case 5: return 25;
604 | break;
605 | case 6: return 16;
606 | break;
607 | case 7: return 20;
608 | break;
609 | case 8: return 19;
610 | break;
611 | case 9: return 19;
612 | break;
613 | case 10: return 23;
614 | break;
615 | case 11: return 22;
616 | break;
617 | case 12: this.popState(); this.begin('com');
618 | break;
619 | case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
620 | break;
621 | case 14: return 22;
622 | break;
623 | case 15: return 37;
624 | break;
625 | case 16: return 36;
626 | break;
627 | case 17: return 36;
628 | break;
629 | case 18: return 40;
630 | break;
631 | case 19: /*ignore whitespace*/
632 | break;
633 | case 20: this.popState(); return 24;
634 | break;
635 | case 21: this.popState(); return 18;
636 | break;
637 | case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 31;
638 | break;
639 | case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 31;
640 | break;
641 | case 24: return 38;
642 | break;
643 | case 25: return 33;
644 | break;
645 | case 26: return 33;
646 | break;
647 | case 27: return 32;
648 | break;
649 | case 28: return 36;
650 | break;
651 | case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 36;
652 | break;
653 | case 30: return 'INVALID';
654 | break;
655 | case 31: return 5;
656 | break;
657 | }
658 | };
659 | lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
660 | lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"INITIAL":{"rules":[0,1,2,31],"inclusive":true}};
661 | return lexer;})()
662 | parser.lexer = lexer;
663 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
664 | return new Parser;
665 | })();;
666 | // lib/handlebars/compiler/base.js
667 |
668 | Handlebars.Parser = handlebars;
669 |
670 | Handlebars.parse = function(input) {
671 |
672 | // Just return if an already-compile AST was passed in.
673 | if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
674 |
675 | Handlebars.Parser.yy = Handlebars.AST;
676 | return Handlebars.Parser.parse(input);
677 | };
678 | ;
679 | // lib/handlebars/compiler/ast.js
680 | Handlebars.AST = {};
681 |
682 | Handlebars.AST.ProgramNode = function(statements, inverse) {
683 | this.type = "program";
684 | this.statements = statements;
685 | if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
686 | };
687 |
688 | Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
689 | this.type = "mustache";
690 | this.escaped = !unescaped;
691 | this.hash = hash;
692 |
693 | var id = this.id = rawParams[0];
694 | var params = this.params = rawParams.slice(1);
695 |
696 | // a mustache is an eligible helper if:
697 | // * its id is simple (a single part, not `this` or `..`)
698 | var eligibleHelper = this.eligibleHelper = id.isSimple;
699 |
700 | // a mustache is definitely a helper if:
701 | // * it is an eligible helper, and
702 | // * it has at least one parameter or hash segment
703 | this.isHelper = eligibleHelper && (params.length || hash);
704 |
705 | // if a mustache is an eligible helper but not a definite
706 | // helper, it is ambiguous, and will be resolved in a later
707 | // pass or at runtime.
708 | };
709 |
710 | Handlebars.AST.PartialNode = function(partialName, context) {
711 | this.type = "partial";
712 | this.partialName = partialName;
713 | this.context = context;
714 | };
715 |
716 | Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
717 | var verifyMatch = function(open, close) {
718 | if(open.original !== close.original) {
719 | throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
720 | }
721 | };
722 |
723 | verifyMatch(mustache.id, close);
724 | this.type = "block";
725 | this.mustache = mustache;
726 | this.program = program;
727 | this.inverse = inverse;
728 |
729 | if (this.inverse && !this.program) {
730 | this.isInverse = true;
731 | }
732 | };
733 |
734 | Handlebars.AST.ContentNode = function(string) {
735 | this.type = "content";
736 | this.string = string;
737 | };
738 |
739 | Handlebars.AST.HashNode = function(pairs) {
740 | this.type = "hash";
741 | this.pairs = pairs;
742 | };
743 |
744 | Handlebars.AST.IdNode = function(parts) {
745 | this.type = "ID";
746 |
747 | var original = "",
748 | dig = [],
749 | depth = 0;
750 |
751 | for(var i=0,l=parts.length; i 0) { throw new Handlebars.Exception("Invalid path: " + original); }
757 | else if (part === "..") { depth++; }
758 | else { this.isScoped = true; }
759 | }
760 | else { dig.push(part); }
761 | }
762 |
763 | this.original = original;
764 | this.parts = dig;
765 | this.string = dig.join('.');
766 | this.depth = depth;
767 |
768 | // an ID is simple if it only has one part, and that part is not
769 | // `..` or `this`.
770 | this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
771 |
772 | this.stringModeValue = this.string;
773 | };
774 |
775 | Handlebars.AST.PartialNameNode = function(name) {
776 | this.type = "PARTIAL_NAME";
777 | this.name = name.original;
778 | };
779 |
780 | Handlebars.AST.DataNode = function(id) {
781 | this.type = "DATA";
782 | this.id = id;
783 | };
784 |
785 | Handlebars.AST.StringNode = function(string) {
786 | this.type = "STRING";
787 | this.original =
788 | this.string =
789 | this.stringModeValue = string;
790 | };
791 |
792 | Handlebars.AST.IntegerNode = function(integer) {
793 | this.type = "INTEGER";
794 | this.original =
795 | this.integer = integer;
796 | this.stringModeValue = Number(integer);
797 | };
798 |
799 | Handlebars.AST.BooleanNode = function(bool) {
800 | this.type = "BOOLEAN";
801 | this.bool = bool;
802 | this.stringModeValue = bool === "true";
803 | };
804 |
805 | Handlebars.AST.CommentNode = function(comment) {
806 | this.type = "comment";
807 | this.comment = comment;
808 | };
809 | ;
810 | // lib/handlebars/utils.js
811 |
812 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
813 |
814 | Handlebars.Exception = function(message) {
815 | var tmp = Error.prototype.constructor.apply(this, arguments);
816 |
817 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
818 | for (var idx = 0; idx < errorProps.length; idx++) {
819 | this[errorProps[idx]] = tmp[errorProps[idx]];
820 | }
821 | };
822 | Handlebars.Exception.prototype = new Error();
823 |
824 | // Build out our basic SafeString type
825 | Handlebars.SafeString = function(string) {
826 | this.string = string;
827 | };
828 | Handlebars.SafeString.prototype.toString = function() {
829 | return this.string.toString();
830 | };
831 |
832 | var escape = {
833 | "&": "&",
834 | "<": "<",
835 | ">": ">",
836 | '"': """,
837 | "'": "'",
838 | "`": "`"
839 | };
840 |
841 | var badChars = /[&<>"'`]/g;
842 | var possible = /[&<>"'`]/;
843 |
844 | var escapeChar = function(chr) {
845 | return escape[chr] || "&";
846 | };
847 |
848 | Handlebars.Utils = {
849 | extend: function(obj, value) {
850 | for(var key in value) {
851 | if(value.hasOwnProperty(key)) {
852 | obj[key] = value[key];
853 | }
854 | }
855 | },
856 |
857 | escapeExpression: function(string) {
858 | // don't escape SafeStrings, since they're already safe
859 | if (string instanceof Handlebars.SafeString) {
860 | return string.toString();
861 | } else if (string == null || string === false) {
862 | return "";
863 | }
864 |
865 | // Force a string conversion as this will be done by the append regardless and
866 | // the regex test will do this transparently behind the scenes, causing issues if
867 | // an object's to string has escaped characters in it.
868 | string = string.toString();
869 |
870 | if(!possible.test(string)) { return string; }
871 | return string.replace(badChars, escapeChar);
872 | },
873 |
874 | isEmpty: function(value) {
875 | if (!value && value !== 0) {
876 | return true;
877 | } else if(toString.call(value) === "[object Array]" && value.length === 0) {
878 | return true;
879 | } else {
880 | return false;
881 | }
882 | }
883 | };
884 | ;
885 | // lib/handlebars/compiler/compiler.js
886 |
887 | /*jshint eqnull:true*/
888 | var Compiler = Handlebars.Compiler = function() {};
889 | var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {};
890 |
891 | // the foundHelper register will disambiguate helper lookup from finding a
892 | // function in a context. This is necessary for mustache compatibility, which
893 | // requires that context functions in blocks are evaluated by blockHelperMissing,
894 | // and then proceed as if the resulting value was provided to blockHelperMissing.
895 |
896 | Compiler.prototype = {
897 | compiler: Compiler,
898 |
899 | disassemble: function() {
900 | var opcodes = this.opcodes, opcode, out = [], params, param;
901 |
902 | for (var i=0, l=opcodes.length; i 0) {
1414 | this.source[1] = this.source[1] + ", " + locals.join(", ");
1415 | }
1416 |
1417 | // Generate minimizer alias mappings
1418 | if (!this.isChild) {
1419 | for (var alias in this.context.aliases) {
1420 | if (this.context.aliases.hasOwnProperty(alias)) {
1421 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
1422 | }
1423 | }
1424 | }
1425 |
1426 | if (this.source[1]) {
1427 | this.source[1] = "var " + this.source[1].substring(2) + ";";
1428 | }
1429 |
1430 | // Merge children
1431 | if (!this.isChild) {
1432 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
1433 | }
1434 |
1435 | if (!this.environment.isSimple) {
1436 | this.source.push("return buffer;");
1437 | }
1438 |
1439 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
1440 |
1441 | for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
1975 | return this.topStackName();
1976 | },
1977 | topStackName: function() {
1978 | return "stack" + this.stackSlot;
1979 | },
1980 | flushInline: function() {
1981 | var inlineStack = this.inlineStack;
1982 | if (inlineStack.length) {
1983 | this.inlineStack = [];
1984 | for (var i = 0, len = inlineStack.length; i < len; i++) {
1985 | var entry = inlineStack[i];
1986 | if (entry instanceof Literal) {
1987 | this.compileStack.push(entry);
1988 | } else {
1989 | this.pushStack(entry);
1990 | }
1991 | }
1992 | }
1993 | },
1994 | isInline: function() {
1995 | return this.inlineStack.length;
1996 | },
1997 |
1998 | popStack: function(wrapped) {
1999 | var inline = this.isInline(),
2000 | item = (inline ? this.inlineStack : this.compileStack).pop();
2001 |
2002 | if (!wrapped && (item instanceof Literal)) {
2003 | return item.value;
2004 | } else {
2005 | if (!inline) {
2006 | this.stackSlot--;
2007 | }
2008 | return item;
2009 | }
2010 | },
2011 |
2012 | topStack: function(wrapped) {
2013 | var stack = (this.isInline() ? this.inlineStack : this.compileStack),
2014 | item = stack[stack.length - 1];
2015 |
2016 | if (!wrapped && (item instanceof Literal)) {
2017 | return item.value;
2018 | } else {
2019 | return item;
2020 | }
2021 | },
2022 |
2023 | quotedString: function(str) {
2024 | return '"' + str
2025 | .replace(/\\/g, '\\\\')
2026 | .replace(/"/g, '\\"')
2027 | .replace(/\n/g, '\\n')
2028 | .replace(/\r/g, '\\r')
2029 | .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
2030 | .replace(/\u2029/g, '\\u2029') + '"';
2031 | },
2032 |
2033 | setupHelper: function(paramSize, name, missingParams) {
2034 | var params = [];
2035 | this.setupParams(paramSize, params, missingParams);
2036 | var foundHelper = this.nameLookup('helpers', name, 'helper');
2037 |
2038 | return {
2039 | params: params,
2040 | name: foundHelper,
2041 | callParams: ["depth0"].concat(params).join(", "),
2042 | helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
2043 | };
2044 | },
2045 |
2046 | // the params and contexts arguments are passed in arrays
2047 | // to fill in
2048 | setupParams: function(paramSize, params, useRegister) {
2049 | var options = [], contexts = [], types = [], param, inverse, program;
2050 |
2051 | options.push("hash:" + this.popStack());
2052 |
2053 | inverse = this.popStack();
2054 | program = this.popStack();
2055 |
2056 | // Avoid setting fn and inverse if neither are set. This allows
2057 | // helpers to do a check for `if (options.fn)`
2058 | if (program || inverse) {
2059 | if (!program) {
2060 | this.context.aliases.self = "this";
2061 | program = "self.noop";
2062 | }
2063 |
2064 | if (!inverse) {
2065 | this.context.aliases.self = "this";
2066 | inverse = "self.noop";
2067 | }
2068 |
2069 | options.push("inverse:" + inverse);
2070 | options.push("fn:" + program);
2071 | }
2072 |
2073 | for(var i=0; i= 1.0.0'
40 | };
41 |
42 | Handlebars.helpers = {};
43 | Handlebars.partials = {};
44 |
45 | var toString = Object.prototype.toString,
46 | functionType = '[object Function]',
47 | objectType = '[object Object]';
48 |
49 | Handlebars.registerHelper = function(name, fn, inverse) {
50 | if (toString.call(name) === objectType) {
51 | if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
52 | Handlebars.Utils.extend(this.helpers, name);
53 | } else {
54 | if (inverse) { fn.not = inverse; }
55 | this.helpers[name] = fn;
56 | }
57 | };
58 |
59 | Handlebars.registerPartial = function(name, str) {
60 | if (toString.call(name) === objectType) {
61 | Handlebars.Utils.extend(this.partials, name);
62 | } else {
63 | this.partials[name] = str;
64 | }
65 | };
66 |
67 | Handlebars.registerHelper('helperMissing', function(arg) {
68 | if(arguments.length === 2) {
69 | return undefined;
70 | } else {
71 | throw new Error("Missing helper: '" + arg + "'");
72 | }
73 | });
74 |
75 | Handlebars.registerHelper('blockHelperMissing', function(context, options) {
76 | var inverse = options.inverse || function() {}, fn = options.fn;
77 |
78 | var type = toString.call(context);
79 |
80 | if(type === functionType) { context = context.call(this); }
81 |
82 | if(context === true) {
83 | return fn(this);
84 | } else if(context === false || context == null) {
85 | return inverse(this);
86 | } else if(type === "[object Array]") {
87 | if(context.length > 0) {
88 | return Handlebars.helpers.each(context, options);
89 | } else {
90 | return inverse(this);
91 | }
92 | } else {
93 | return fn(context);
94 | }
95 | });
96 |
97 | Handlebars.K = function() {};
98 |
99 | Handlebars.createFrame = Object.create || function(object) {
100 | Handlebars.K.prototype = object;
101 | var obj = new Handlebars.K();
102 | Handlebars.K.prototype = null;
103 | return obj;
104 | };
105 |
106 | Handlebars.logger = {
107 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
108 |
109 | methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
110 |
111 | // can be overridden in the host environment
112 | log: function(level, obj) {
113 | if (Handlebars.logger.level <= level) {
114 | var method = Handlebars.logger.methodMap[level];
115 | if (typeof console !== 'undefined' && console[method]) {
116 | console[method].call(console, obj);
117 | }
118 | }
119 | }
120 | };
121 |
122 | Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
123 |
124 | Handlebars.registerHelper('each', function(context, options) {
125 | var fn = options.fn, inverse = options.inverse;
126 | var i = 0, ret = "", data;
127 |
128 | var type = toString.call(context);
129 | if(type === functionType) { context = context.call(this); }
130 |
131 | if (options.data) {
132 | data = Handlebars.createFrame(options.data);
133 | }
134 |
135 | if(context && typeof context === 'object') {
136 | if(context instanceof Array){
137 | for(var j = context.length; i": ">",
212 | '"': """,
213 | "'": "'",
214 | "`": "`"
215 | };
216 |
217 | var badChars = /[&<>"'`]/g;
218 | var possible = /[&<>"'`]/;
219 |
220 | var escapeChar = function(chr) {
221 | return escape[chr] || "&";
222 | };
223 |
224 | Handlebars.Utils = {
225 | extend: function(obj, value) {
226 | for(var key in value) {
227 | if(value.hasOwnProperty(key)) {
228 | obj[key] = value[key];
229 | }
230 | }
231 | },
232 |
233 | escapeExpression: function(string) {
234 | // don't escape SafeStrings, since they're already safe
235 | if (string instanceof Handlebars.SafeString) {
236 | return string.toString();
237 | } else if (string == null || string === false) {
238 | return "";
239 | }
240 |
241 | // Force a string conversion as this will be done by the append regardless and
242 | // the regex test will do this transparently behind the scenes, causing issues if
243 | // an object's to string has escaped characters in it.
244 | string = string.toString();
245 |
246 | if(!possible.test(string)) { return string; }
247 | return string.replace(badChars, escapeChar);
248 | },
249 |
250 | isEmpty: function(value) {
251 | if (!value && value !== 0) {
252 | return true;
253 | } else if(toString.call(value) === "[object Array]" && value.length === 0) {
254 | return true;
255 | } else {
256 | return false;
257 | }
258 | }
259 | };
260 | ;
261 | // lib/handlebars/runtime.js
262 |
263 | Handlebars.VM = {
264 | template: function(templateSpec) {
265 | // Just add water
266 | var container = {
267 | escapeExpression: Handlebars.Utils.escapeExpression,
268 | invokePartial: Handlebars.VM.invokePartial,
269 | programs: [],
270 | program: function(i, fn, data) {
271 | var programWrapper = this.programs[i];
272 | if(data) {
273 | programWrapper = Handlebars.VM.program(i, fn, data);
274 | } else if (!programWrapper) {
275 | programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
276 | }
277 | return programWrapper;
278 | },
279 | merge: function(param, common) {
280 | var ret = param || common;
281 |
282 | if (param && common) {
283 | ret = {};
284 | Handlebars.Utils.extend(ret, common);
285 | Handlebars.Utils.extend(ret, param);
286 | }
287 | return ret;
288 | },
289 | programWithDepth: Handlebars.VM.programWithDepth,
290 | noop: Handlebars.VM.noop,
291 | compilerInfo: null
292 | };
293 |
294 | return function(context, options) {
295 | options = options || {};
296 | var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
297 |
298 | var compilerInfo = container.compilerInfo || [],
299 | compilerRevision = compilerInfo[0] || 1,
300 | currentRevision = Handlebars.COMPILER_REVISION;
301 |
302 | if (compilerRevision !== currentRevision) {
303 | if (compilerRevision < currentRevision) {
304 | var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
305 | compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
306 | throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
307 | "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
308 | } else {
309 | // Use the embedded version info since the runtime doesn't know about this revision yet
310 | throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
311 | "Please update your runtime to a newer version ("+compilerInfo[1]+").";
312 | }
313 | }
314 |
315 | return result;
316 | };
317 | },
318 |
319 | programWithDepth: function(i, fn, data /*, $depth */) {
320 | var args = Array.prototype.slice.call(arguments, 3);
321 |
322 | var program = function(context, options) {
323 | options = options || {};
324 |
325 | return fn.apply(this, [context, options.data || data].concat(args));
326 | };
327 | program.program = i;
328 | program.depth = args.length;
329 | return program;
330 | },
331 | program: function(i, fn, data) {
332 | var program = function(context, options) {
333 | options = options || {};
334 |
335 | return fn(context, options.data || data);
336 | };
337 | program.program = i;
338 | program.depth = 0;
339 | return program;
340 | },
341 | noop: function() { return ""; },
342 | invokePartial: function(partial, name, context, helpers, partials, data) {
343 | var options = { helpers: helpers, partials: partials, data: data };
344 |
345 | if(partial === undefined) {
346 | throw new Handlebars.Exception("The partial " + name + " could not be found");
347 | } else if(partial instanceof Function) {
348 | return partial(context, options);
349 | } else if (!Handlebars.compile) {
350 | throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
351 | } else {
352 | partials[name] = Handlebars.compile(partial, {data: data !== undefined});
353 | return partials[name](context, options);
354 | }
355 | }
356 | };
357 |
358 | Handlebars.template = Handlebars.VM.template;
359 | ;
360 | // lib/handlebars/browser-suffix.js
361 | })(Handlebars);
362 | ;
363 |
--------------------------------------------------------------------------------
/example/scripts/libs/hbars.js:
--------------------------------------------------------------------------------
1 | // RequireJS Handlebars template plugin
2 | // http://github.com/jfparadis/requirejs-handlebars
3 | //
4 | // An alternative to http://github.com/SlexAxton/require-handlebars-plugin/blob/master/hbs.js
5 | //
6 | // Using Handlebars Semantic templates at http://handlebarsjs.com
7 | // Using and RequireJS text.js at http://requirejs.org/docs/api.html#text
8 | // @author JF Paradis
9 | // @version 0.0.2
10 | //
11 | // Released under the MIT license
12 | //
13 | // Usage:
14 | // require(['backbone', 'hbar!mytemplate'], function (Backbone, mytemplate) {
15 | // return Backbone.View.extend({
16 | // initialize: function(){
17 | // this.render();
18 | // },
19 | // render: function(){
20 | // this.$el.html(mytemplate({message: 'hello'}));
21 | // });
22 | // });
23 | //
24 | // Configuration: (optional)
25 | // require.config({
26 | // hbars: {
27 | // extension: '.hbar' // default = '.html'
28 | // }
29 | // });
30 |
31 | /*jslint nomen: true */
32 | /*global define: false */
33 |
34 | define(['text', 'Handlebars'], function (text, Handlebars) {
35 | 'use strict';
36 |
37 | var buildMap = {},
38 | buildTemplateSource = "define('{pluginName}!{moduleName}', ['Handlebars'], function (Handlebars) { return Handlebars.template({content}); });\n";
39 |
40 | return {
41 | version: '0.0.2',
42 |
43 | load: function (moduleName, parentRequire, onload, config) {
44 | if (buildMap[moduleName]) {
45 | onload(buildMap[moduleName]);
46 |
47 | } else {
48 | var ext = (config.hbars && config.hbars.extension) || '.html',
49 | path = (config.hbars && config.hbars.path) || '',
50 | compileOptions = (config.hbars && config.hbars.compileOptions) || {};
51 |
52 | text.load(path + moduleName + ext, parentRequire, function (source) {
53 | if (config.isBuild) {
54 | // We store the precompiled template so we can use the
55 | // handlebars.runtime after build.
56 | buildMap[moduleName] = Handlebars.precompile(source, compileOptions);
57 | // Don't bother doing anything else during build.
58 | onload();
59 | } else {
60 | // We store the compiled template for reuse
61 | buildMap[moduleName] = Handlebars.compile(source);
62 | onload(buildMap[moduleName]);
63 | }
64 | }, config);
65 | }
66 | },
67 |
68 | write: function (pluginName, moduleName, write, config) {
69 | var content = buildMap[moduleName];
70 | if (content) {
71 | write.asModule(pluginName + '!' + moduleName,
72 | buildTemplateSource
73 | .replace('{pluginName}', pluginName)
74 | .replace('{moduleName}', moduleName)
75 | .replace('{content}', content));
76 | }
77 | }
78 | };
79 | });
80 |
--------------------------------------------------------------------------------
/example/scripts/libs/text.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license RequireJS text 2.0.10 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
3 | * Available via the MIT or new BSD license.
4 | * see: http://github.com/requirejs/text for details
5 | */
6 | /*jslint regexp: true */
7 | /*global require, XMLHttpRequest, ActiveXObject,
8 | define, window, process, Packages,
9 | java, location, Components, FileUtils */
10 |
11 | define(['module'], function (module) {
12 | 'use strict';
13 |
14 | var text, fs, Cc, Ci, xpcIsWindows,
15 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
16 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
17 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im,
18 | hasLocation = typeof location !== 'undefined' && location.href,
19 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
20 | defaultHostName = hasLocation && location.hostname,
21 | defaultPort = hasLocation && (location.port || undefined),
22 | buildMap = {},
23 | masterConfig = (module.config && module.config()) || {};
24 |
25 | text = {
26 | version: '2.0.10',
27 |
28 | strip: function (content) {
29 | //Strips declarations so that external SVG and XML
30 | //documents can be added to a document without worry. Also, if the string
31 | //is an HTML document, only the part inside the body tag is returned.
32 | if (content) {
33 | content = content.replace(xmlRegExp, "");
34 | var matches = content.match(bodyRegExp);
35 | if (matches) {
36 | content = matches[1];
37 | }
38 | } else {
39 | content = "";
40 | }
41 | return content;
42 | },
43 |
44 | jsEscape: function (content) {
45 | return content.replace(/(['\\])/g, '\\$1')
46 | .replace(/[\f]/g, "\\f")
47 | .replace(/[\b]/g, "\\b")
48 | .replace(/[\n]/g, "\\n")
49 | .replace(/[\t]/g, "\\t")
50 | .replace(/[\r]/g, "\\r")
51 | .replace(/[\u2028]/g, "\\u2028")
52 | .replace(/[\u2029]/g, "\\u2029");
53 | },
54 |
55 | createXhr: masterConfig.createXhr || function () {
56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
57 | var xhr, i, progId;
58 | if (typeof XMLHttpRequest !== "undefined") {
59 | return new XMLHttpRequest();
60 | } else if (typeof ActiveXObject !== "undefined") {
61 | for (i = 0; i < 3; i += 1) {
62 | progId = progIds[i];
63 | try {
64 | xhr = new ActiveXObject(progId);
65 | } catch (e) {}
66 |
67 | if (xhr) {
68 | progIds = [progId]; // so faster next time
69 | break;
70 | }
71 | }
72 | }
73 |
74 | return xhr;
75 | },
76 |
77 | /**
78 | * Parses a resource name into its component parts. Resource names
79 | * look like: module/name.ext!strip, where the !strip part is
80 | * optional.
81 | * @param {String} name the resource name
82 | * @returns {Object} with properties "moduleName", "ext" and "strip"
83 | * where strip is a boolean.
84 | */
85 | parseName: function (name) {
86 | var modName, ext, temp,
87 | strip = false,
88 | index = name.indexOf("."),
89 | isRelative = name.indexOf('./') === 0 ||
90 | name.indexOf('../') === 0;
91 |
92 | if (index !== -1 && (!isRelative || index > 1)) {
93 | modName = name.substring(0, index);
94 | ext = name.substring(index + 1, name.length);
95 | } else {
96 | modName = name;
97 | }
98 |
99 | temp = ext || modName;
100 | index = temp.indexOf("!");
101 | if (index !== -1) {
102 | //Pull off the strip arg.
103 | strip = temp.substring(index + 1) === "strip";
104 | temp = temp.substring(0, index);
105 | if (ext) {
106 | ext = temp;
107 | } else {
108 | modName = temp;
109 | }
110 | }
111 |
112 | return {
113 | moduleName: modName,
114 | ext: ext,
115 | strip: strip
116 | };
117 | },
118 |
119 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
120 |
121 | /**
122 | * Is an URL on another domain. Only works for browser use, returns
123 | * false in non-browser environments. Only used to know if an
124 | * optimized .js version of a text resource should be loaded
125 | * instead.
126 | * @param {String} url
127 | * @returns Boolean
128 | */
129 | useXhr: function (url, protocol, hostname, port) {
130 | var uProtocol, uHostName, uPort,
131 | match = text.xdRegExp.exec(url);
132 | if (!match) {
133 | return true;
134 | }
135 | uProtocol = match[2];
136 | uHostName = match[3];
137 |
138 | uHostName = uHostName.split(':');
139 | uPort = uHostName[1];
140 | uHostName = uHostName[0];
141 |
142 | return (!uProtocol || uProtocol === protocol) &&
143 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
144 | ((!uPort && !uHostName) || uPort === port);
145 | },
146 |
147 | finishLoad: function (name, strip, content, onLoad) {
148 | content = strip ? text.strip(content) : content;
149 | if (masterConfig.isBuild) {
150 | buildMap[name] = content;
151 | }
152 | onLoad(content);
153 | },
154 |
155 | load: function (name, req, onLoad, config) {
156 | //Name has format: some.module.filext!strip
157 | //The strip part is optional.
158 | //if strip is present, then that means only get the string contents
159 | //inside a body tag in an HTML string. For XML/SVG content it means
160 | //removing the declarations so the content can be inserted
161 | //into the current doc without problems.
162 |
163 | // Do not bother with the work if a build and text will
164 | // not be inlined.
165 | if (config.isBuild && !config.inlineText) {
166 | onLoad();
167 | return;
168 | }
169 |
170 | masterConfig.isBuild = config.isBuild;
171 |
172 | var parsed = text.parseName(name),
173 | nonStripName = parsed.moduleName +
174 | (parsed.ext ? '.' + parsed.ext : ''),
175 | url = req.toUrl(nonStripName),
176 | useXhr = (masterConfig.useXhr) ||
177 | text.useXhr;
178 |
179 | // Do not load if it is an empty: url
180 | if (url.indexOf('empty:') === 0) {
181 | onLoad();
182 | return;
183 | }
184 |
185 | //Load the text. Use XHR if possible and in a browser.
186 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
187 | text.get(url, function (content) {
188 | text.finishLoad(name, parsed.strip, content, onLoad);
189 | }, function (err) {
190 | if (onLoad.error) {
191 | onLoad.error(err);
192 | }
193 | });
194 | } else {
195 | //Need to fetch the resource across domains. Assume
196 | //the resource has been optimized into a JS module. Fetch
197 | //by the module name + extension, but do not include the
198 | //!strip part to avoid file system issues.
199 | req([nonStripName], function (content) {
200 | text.finishLoad(parsed.moduleName + '.' + parsed.ext,
201 | parsed.strip, content, onLoad);
202 | });
203 | }
204 | },
205 |
206 | write: function (pluginName, moduleName, write, config) {
207 | if (buildMap.hasOwnProperty(moduleName)) {
208 | var content = text.jsEscape(buildMap[moduleName]);
209 | write.asModule(pluginName + "!" + moduleName,
210 | "define(function () { return '" +
211 | content +
212 | "';});\n");
213 | }
214 | },
215 |
216 | writeFile: function (pluginName, moduleName, req, write, config) {
217 | var parsed = text.parseName(moduleName),
218 | extPart = parsed.ext ? '.' + parsed.ext : '',
219 | nonStripName = parsed.moduleName + extPart,
220 | //Use a '.js' file name so that it indicates it is a
221 | //script that can be loaded across domains.
222 | fileName = req.toUrl(parsed.moduleName + extPart) + '.js';
223 |
224 | //Leverage own load() method to load plugin value, but only
225 | //write out values that do not have the strip argument,
226 | //to avoid any potential issues with ! in file names.
227 | text.load(nonStripName, req, function (value) {
228 | //Use own write() method to construct full module value.
229 | //But need to create shell that translates writeFile's
230 | //write() to the right interface.
231 | var textWrite = function (contents) {
232 | return write(fileName, contents);
233 | };
234 | textWrite.asModule = function (moduleName, contents) {
235 | return write.asModule(moduleName, fileName, contents);
236 | };
237 |
238 | text.write(pluginName, nonStripName, textWrite, config);
239 | }, config);
240 | }
241 | };
242 |
243 | if (masterConfig.env === 'node' || (!masterConfig.env &&
244 | typeof process !== "undefined" &&
245 | process.versions &&
246 | !!process.versions.node &&
247 | !process.versions['node-webkit'])) {
248 | //Using special require.nodeRequire, something added by r.js.
249 | fs = require.nodeRequire('fs');
250 |
251 | text.get = function (url, callback, errback) {
252 | try {
253 | var file = fs.readFileSync(url, 'utf8');
254 | //Remove BOM (Byte Mark Order) from utf8 files if it is there.
255 | if (file.indexOf('\uFEFF') === 0) {
256 | file = file.substring(1);
257 | }
258 | callback(file);
259 | } catch (e) {
260 | errback(e);
261 | }
262 | };
263 | } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
264 | text.createXhr())) {
265 | text.get = function (url, callback, errback, headers) {
266 | var xhr = text.createXhr(), header;
267 | xhr.open('GET', url, true);
268 |
269 | //Allow plugins direct access to xhr headers
270 | if (headers) {
271 | for (header in headers) {
272 | if (headers.hasOwnProperty(header)) {
273 | xhr.setRequestHeader(header.toLowerCase(), headers[header]);
274 | }
275 | }
276 | }
277 |
278 | //Allow overrides specified in config
279 | if (masterConfig.onXhr) {
280 | masterConfig.onXhr(xhr, url);
281 | }
282 |
283 | xhr.onreadystatechange = function (evt) {
284 | var status, err;
285 | //Do not explicitly handle errors, those should be
286 | //visible via console output in the browser.
287 | if (xhr.readyState === 4) {
288 | status = xhr.status;
289 | if (status > 399 && status < 600) {
290 | //An http 4xx or 5xx error. Signal an error.
291 | err = new Error(url + ' HTTP status: ' + status);
292 | err.xhr = xhr;
293 | errback(err);
294 | } else {
295 | callback(xhr.responseText);
296 | }
297 |
298 | if (masterConfig.onXhrComplete) {
299 | masterConfig.onXhrComplete(xhr, url);
300 | }
301 | }
302 | };
303 | xhr.send(null);
304 | };
305 | } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
306 | typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
307 | //Why Java, why is this so awkward?
308 | text.get = function (url, callback) {
309 | var stringBuffer, line,
310 | encoding = "utf-8",
311 | file = new java.io.File(url),
312 | lineSeparator = java.lang.System.getProperty("line.separator"),
313 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
314 | content = '';
315 | try {
316 | stringBuffer = new java.lang.StringBuffer();
317 | line = input.readLine();
318 |
319 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
320 | // http://www.unicode.org/faq/utf_bom.html
321 |
322 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
323 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
324 | if (line && line.length() && line.charAt(0) === 0xfeff) {
325 | // Eat the BOM, since we've already found the encoding on this file,
326 | // and we plan to concatenating this buffer with others; the BOM should
327 | // only appear at the top of a file.
328 | line = line.substring(1);
329 | }
330 |
331 | if (line !== null) {
332 | stringBuffer.append(line);
333 | }
334 |
335 | while ((line = input.readLine()) !== null) {
336 | stringBuffer.append(lineSeparator);
337 | stringBuffer.append(line);
338 | }
339 | //Make sure we return a JavaScript string and not a Java string.
340 | content = String(stringBuffer.toString()); //String
341 | } finally {
342 | input.close();
343 | }
344 | callback(content);
345 | };
346 | } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
347 | typeof Components !== 'undefined' && Components.classes &&
348 | Components.interfaces)) {
349 | //Avert your gaze!
350 | Cc = Components.classes,
351 | Ci = Components.interfaces;
352 | Components.utils['import']('resource://gre/modules/FileUtils.jsm');
353 | xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);
354 |
355 | text.get = function (url, callback) {
356 | var inStream, convertStream, fileObj,
357 | readData = {};
358 |
359 | if (xpcIsWindows) {
360 | url = url.replace(/\//g, '\\');
361 | }
362 |
363 | fileObj = new FileUtils.File(url);
364 |
365 | //XPCOM, you so crazy
366 | try {
367 | inStream = Cc['@mozilla.org/network/file-input-stream;1']
368 | .createInstance(Ci.nsIFileInputStream);
369 | inStream.init(fileObj, 1, 0, false);
370 |
371 | convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
372 | .createInstance(Ci.nsIConverterInputStream);
373 | convertStream.init(inStream, "utf-8", inStream.available(),
374 | Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
375 |
376 | convertStream.readString(inStream.available(), readData);
377 | convertStream.close();
378 | inStream.close();
379 | callback(readData.value);
380 | } catch (e) {
381 | throw new Error((fileObj && fileObj.path || '') + ': ' + e);
382 | }
383 | };
384 | }
385 | return text;
386 | });
387 |
--------------------------------------------------------------------------------
/example/scripts/main.js:
--------------------------------------------------------------------------------
1 | require({
2 | paths: {
3 | templates: '../templates',
4 | Handlebars: 'libs/handlebars',
5 | text: 'libs/text',
6 | hbars: 'libs/hbars'
7 | },
8 | shim: {
9 | Handlebars: {
10 | exports: 'Handlebars'
11 | }
12 | },
13 |
14 | onBuildWrite : function(moduleName, path, content){
15 |
16 | // replace handlebars with the runtime version
17 | if (moduleName === 'Handlebars') {
18 | path = path.replace('handlebars.js','handlebars.runtime.js');
19 | content = fs.readFileSync(path).toString();
20 | content = content.replace(/(define\()(function)/, '$1"handlebars", $2');
21 | }
22 | return content;
23 | }
24 |
25 | }, ['hbars!templates/message'], function (template) {
26 | 'use strict';
27 |
28 | console.log('template = ' + template);
29 | document.body.innerHTML += template({message: 'Hello World!'});
30 | });
31 |
--------------------------------------------------------------------------------
/example/templates/message.html:
--------------------------------------------------------------------------------
1 |
2 | Message
3 |
4 |
5 | {{ message }}
6 |
--------------------------------------------------------------------------------
/hbars.js:
--------------------------------------------------------------------------------
1 | // RequireJS Handlebars template plugin
2 | // http://github.com/jfparadis/requirejs-handlebars
3 | //
4 | // An alternative to http://github.com/SlexAxton/require-handlebars-plugin/blob/master/hbs.js
5 | //
6 | // Using Handlebars Semantic templates at http://handlebarsjs.com
7 | // Using and RequireJS text.js at http://requirejs.org/docs/api.html#text
8 | // @author JF Paradis
9 | // @version 0.0.2
10 | //
11 | // Released under the MIT license
12 | //
13 | // Usage:
14 | // require(['backbone', 'hbar!mytemplate'], function (Backbone, mytemplate) {
15 | // return Backbone.View.extend({
16 | // initialize: function(){
17 | // this.render();
18 | // },
19 | // render: function(){
20 | // this.$el.html(mytemplate({message: 'hello'}));
21 | // });
22 | // });
23 | //
24 | // Configuration: (optional)
25 | // require.config({
26 | // hbars: {
27 | // extension: '.hbar' // default = '.html'
28 | // }
29 | // });
30 |
31 | /*jslint nomen: true */
32 | /*global define: false */
33 |
34 | define(['text', 'Handlebars'], function (text, Handlebars) {
35 | 'use strict';
36 |
37 | var buildMap = {},
38 | buildTemplateSource = "define('{pluginName}!{moduleName}', ['Handlebars'], function (Handlebars) { return Handlebars.template({content}); });\n";
39 |
40 | return {
41 | version: '0.0.2',
42 |
43 | load: function (moduleName, parentRequire, onload, config) {
44 | if (buildMap[moduleName]) {
45 | onload(buildMap[moduleName]);
46 |
47 | } else {
48 | var ext = (config.hbars && config.hbars.extension) || '.html',
49 | path = (config.hbars && config.hbars.path) || '',
50 | compileOptions = (config.hbars && config.hbars.compileOptions) || {},
51 | textOnload = function (source) {
52 | if (config.isBuild) {
53 | // We store the precompiled template so we can use the
54 | // handlebars.runtime after build.
55 | buildMap[moduleName] = Handlebars.precompile(source, compileOptions);
56 | // Don't bother doing anything else during build.
57 | onload();
58 | } else {
59 | // We store the compiled template for reuse
60 | buildMap[moduleName] = Handlebars.compile(source);
61 | onload(buildMap[moduleName]);
62 | }
63 | };
64 |
65 | textOnload.error = onload.error;
66 | text.load(path + moduleName + ext, parentRequire, textOnload, config);
67 | }
68 | },
69 |
70 | write: function (pluginName, moduleName, write, config) {
71 | var content = buildMap[moduleName];
72 | if (content) {
73 | write.asModule(pluginName + '!' + moduleName,
74 | buildTemplateSource
75 | .replace('{pluginName}', pluginName)
76 | .replace('{moduleName}', moduleName)
77 | .replace('{content}', content));
78 | }
79 | }
80 | };
81 | });
82 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "requirejs-handlebars",
3 | "description": "RequireJS Handlebars template plugin",
4 | "version": "0.0.2",
5 | "keywords": [
6 | "requirejs",
7 | "handlebars",
8 | "template",
9 | "javascript"
10 | ],
11 | "maintainers": [
12 | {
13 | "name": "Jean-Francois Paradis",
14 | "twitter": "@jfparadis",
15 | "web": "http://www.jeanfrancoisparadis.com"
16 | }
17 | ],
18 | "dependencies": {
19 | "http://github.com/jfparadis/requirejs-handlebars": "master"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/jfparadis/requirejs-handlebars/issues"
23 | },
24 | "repositories": [
25 | {
26 | "type": "git",
27 | "url": "git@github.com:jfparadis/requirejs-handlebars.git"
28 | }
29 | ],
30 | "licenses": [
31 | {
32 | "name": "MIT",
33 | "url": "http://www.opensource.org/licenses/mit-license.php"
34 | }
35 | ],
36 | "homepage": "http://github.com/jfparadis/requirejs-handlebars"
37 | }
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var connect = require('connect');
2 | connect.createServer(
3 | connect.static(__dirname)
4 | ).listen(9000);
5 |
--------------------------------------------------------------------------------