UNPKG

192 kBJavaScriptView Raw
1/*! Browser bundle of nunjucks 2.5.2 */
2(function webpackUniversalModuleDefinition(root, factory) {
3 if(typeof exports === 'object' && typeof module === 'object')
4 module.exports = factory();
5 else if(typeof define === 'function' && define.amd)
6 define([], factory);
7 else if(typeof exports === 'object')
8 exports["nunjucks"] = factory();
9 else
10 root["nunjucks"] = factory();
11})(this, function() {
12return /******/ (function(modules) { // webpackBootstrap
13/******/ // The module cache
14/******/ var installedModules = {};
15
16/******/ // The require function
17/******/ function __webpack_require__(moduleId) {
18
19/******/ // Check if module is in cache
20/******/ if(installedModules[moduleId])
21/******/ return installedModules[moduleId].exports;
22
23/******/ // Create a new module (and put it into the cache)
24/******/ var module = installedModules[moduleId] = {
25/******/ exports: {},
26/******/ id: moduleId,
27/******/ loaded: false
28/******/ };
29
30/******/ // Execute the module function
31/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
32
33/******/ // Flag the module as loaded
34/******/ module.loaded = true;
35
36/******/ // Return the exports of the module
37/******/ return module.exports;
38/******/ }
39
40
41/******/ // expose the modules object (__webpack_modules__)
42/******/ __webpack_require__.m = modules;
43
44/******/ // expose the module cache
45/******/ __webpack_require__.c = installedModules;
46
47/******/ // __webpack_public_path__
48/******/ __webpack_require__.p = "";
49
50/******/ // Load entry module and return exports
51/******/ return __webpack_require__(0);
52/******/ })
53/************************************************************************/
54/******/ ([
55/* 0 */
56/***/ function(module, exports, __webpack_require__) {
57
58 'use strict';
59
60 var lib = __webpack_require__(1);
61 var env = __webpack_require__(2);
62 var Loader = __webpack_require__(15);
63 var loaders = __webpack_require__(14);
64 var precompile = __webpack_require__(3);
65
66 module.exports = {};
67 module.exports.Environment = env.Environment;
68 module.exports.Template = env.Template;
69
70 module.exports.Loader = Loader;
71 module.exports.FileSystemLoader = loaders.FileSystemLoader;
72 module.exports.PrecompiledLoader = loaders.PrecompiledLoader;
73 module.exports.WebLoader = loaders.WebLoader;
74
75 module.exports.compiler = __webpack_require__(7);
76 module.exports.parser = __webpack_require__(8);
77 module.exports.lexer = __webpack_require__(9);
78 module.exports.runtime = __webpack_require__(12);
79 module.exports.lib = lib;
80 module.exports.nodes = __webpack_require__(10);
81
82 module.exports.installJinjaCompat = __webpack_require__(18);
83
84 // A single instance of an environment, since this is so commonly used
85
86 var e;
87 module.exports.configure = function(templatesPath, opts) {
88 opts = opts || {};
89 if(lib.isObject(templatesPath)) {
90 opts = templatesPath;
91 templatesPath = null;
92 }
93
94 var TemplateLoader;
95 if(loaders.FileSystemLoader) {
96 TemplateLoader = new loaders.FileSystemLoader(templatesPath, {
97 watch: opts.watch,
98 noCache: opts.noCache
99 });
100 }
101 else if(loaders.WebLoader) {
102 TemplateLoader = new loaders.WebLoader(templatesPath, {
103 useCache: opts.web && opts.web.useCache,
104 async: opts.web && opts.web.async
105 });
106 }
107
108 e = new env.Environment(TemplateLoader, opts);
109
110 if(opts && opts.express) {
111 e.express(opts.express);
112 }
113
114 return e;
115 };
116
117 module.exports.compile = function(src, env, path, eagerCompile) {
118 if(!e) {
119 module.exports.configure();
120 }
121 return new module.exports.Template(src, env, path, eagerCompile);
122 };
123
124 module.exports.render = function(name, ctx, cb) {
125 if(!e) {
126 module.exports.configure();
127 }
128
129 return e.render(name, ctx, cb);
130 };
131
132 module.exports.renderString = function(src, ctx, cb) {
133 if(!e) {
134 module.exports.configure();
135 }
136
137 return e.renderString(src, ctx, cb);
138 };
139
140 if(precompile) {
141 module.exports.precompile = precompile.precompile;
142 module.exports.precompileString = precompile.precompileString;
143 }
144
145
146/***/ },
147/* 1 */
148/***/ function(module, exports) {
149
150 'use strict';
151
152 var ArrayProto = Array.prototype;
153 var ObjProto = Object.prototype;
154
155 var escapeMap = {
156 '&': '&',
157 '"': '"',
158 '\'': ''',
159 '<': '&lt;',
160 '>': '&gt;'
161 };
162
163 var escapeRegex = /[&"'<>]/g;
164
165 var lookupEscape = function(ch) {
166 return escapeMap[ch];
167 };
168
169 var exports = module.exports = {};
170
171 exports.prettifyError = function(path, withInternals, err) {
172 // jshint -W022
173 // http://jslinterrors.com/do-not-assign-to-the-exception-parameter
174 if (!err.Update) {
175 // not one of ours, cast it
176 err = new exports.TemplateError(err);
177 }
178 err.Update(path);
179
180 // Unless they marked the dev flag, show them a trace from here
181 if (!withInternals) {
182 var old = err;
183 err = new Error(old.message);
184 err.name = old.name;
185 }
186
187 return err;
188 };
189
190 exports.TemplateError = function(message, lineno, colno) {
191 var err = this;
192
193 if (message instanceof Error) { // for casting regular js errors
194 err = message;
195 message = message.name + ': ' + message.message;
196
197 try {
198 if(err.name = '') {}
199 }
200 catch(e) {
201 // If we can't set the name of the error object in this
202 // environment, don't use it
203 err = this;
204 }
205 } else {
206 if(Error.captureStackTrace) {
207 Error.captureStackTrace(err);
208 }
209 }
210
211 err.name = 'Template render error';
212 err.message = message;
213 err.lineno = lineno;
214 err.colno = colno;
215 err.firstUpdate = true;
216
217 err.Update = function(path) {
218 var message = '(' + (path || 'unknown path') + ')';
219
220 // only show lineno + colno next to path of template
221 // where error occurred
222 if (this.firstUpdate) {
223 if(this.lineno && this.colno) {
224 message += ' [Line ' + this.lineno + ', Column ' + this.colno + ']';
225 }
226 else if(this.lineno) {
227 message += ' [Line ' + this.lineno + ']';
228 }
229 }
230
231 message += '\n ';
232 if (this.firstUpdate) {
233 message += ' ';
234 }
235
236 this.message = message + (this.message || '');
237 this.firstUpdate = false;
238 return this;
239 };
240
241 return err;
242 };
243
244 exports.TemplateError.prototype = Error.prototype;
245
246 exports.escape = function(val) {
247 return val.replace(escapeRegex, lookupEscape);
248 };
249
250 exports.isFunction = function(obj) {
251 return ObjProto.toString.call(obj) === '[object Function]';
252 };
253
254 exports.isArray = Array.isArray || function(obj) {
255 return ObjProto.toString.call(obj) === '[object Array]';
256 };
257
258 exports.isString = function(obj) {
259 return ObjProto.toString.call(obj) === '[object String]';
260 };
261
262 exports.isObject = function(obj) {
263 return ObjProto.toString.call(obj) === '[object Object]';
264 };
265
266 exports.groupBy = function(obj, val) {
267 var result = {};
268 var iterator = exports.isFunction(val) ? val : function(obj) { return obj[val]; };
269 for(var i=0; i<obj.length; i++) {
270 var value = obj[i];
271 var key = iterator(value, i);
272 (result[key] || (result[key] = [])).push(value);
273 }
274 return result;
275 };
276
277 exports.toArray = function(obj) {
278 return Array.prototype.slice.call(obj);
279 };
280
281 exports.without = function(array) {
282 var result = [];
283 if (!array) {
284 return result;
285 }
286 var index = -1,
287 length = array.length,
288 contains = exports.toArray(arguments).slice(1);
289
290 while(++index < length) {
291 if(exports.indexOf(contains, array[index]) === -1) {
292 result.push(array[index]);
293 }
294 }
295 return result;
296 };
297
298 exports.extend = function(obj, obj2) {
299 for(var k in obj2) {
300 obj[k] = obj2[k];
301 }
302 return obj;
303 };
304
305 exports.repeat = function(char_, n) {
306 var str = '';
307 for(var i=0; i<n; i++) {
308 str += char_;
309 }
310 return str;
311 };
312
313 exports.each = function(obj, func, context) {
314 if(obj == null) {
315 return;
316 }
317
318 if(ArrayProto.each && obj.each === ArrayProto.each) {
319 obj.forEach(func, context);
320 }
321 else if(obj.length === +obj.length) {
322 for(var i=0, l=obj.length; i<l; i++) {
323 func.call(context, obj[i], i, obj);
324 }
325 }
326 };
327
328 exports.map = function(obj, func) {
329 var results = [];
330 if(obj == null) {
331 return results;
332 }
333
334 if(ArrayProto.map && obj.map === ArrayProto.map) {
335 return obj.map(func);
336 }
337
338 for(var i=0; i<obj.length; i++) {
339 results[results.length] = func(obj[i], i);
340 }
341
342 if(obj.length === +obj.length) {
343 results.length = obj.length;
344 }
345
346 return results;
347 };
348
349 exports.asyncIter = function(arr, iter, cb) {
350 var i = -1;
351
352 function next() {
353 i++;
354
355 if(i < arr.length) {
356 iter(arr[i], i, next, cb);
357 }
358 else {
359 cb();
360 }
361 }
362
363 next();
364 };
365
366 exports.asyncFor = function(obj, iter, cb) {
367 var keys = exports.keys(obj);
368 var len = keys.length;
369 var i = -1;
370
371 function next() {
372 i++;
373 var k = keys[i];
374
375 if(i < len) {
376 iter(k, obj[k], i, len, next);
377 }
378 else {
379 cb();
380 }
381 }
382
383 next();
384 };
385
386 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
387 exports.indexOf = Array.prototype.indexOf ?
388 function (arr, searchElement, fromIndex) {
389 return Array.prototype.indexOf.call(arr, searchElement, fromIndex);
390 } :
391 function (arr, searchElement, fromIndex) {
392 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
393
394 fromIndex = +fromIndex || 0;
395
396 if(Math.abs(fromIndex) === Infinity) {
397 fromIndex = 0;
398 }
399
400 if(fromIndex < 0) {
401 fromIndex += length;
402 if (fromIndex < 0) {
403 fromIndex = 0;
404 }
405 }
406
407 for(;fromIndex < length; fromIndex++) {
408 if (arr[fromIndex] === searchElement) {
409 return fromIndex;
410 }
411 }
412
413 return -1;
414 };
415
416 if(!Array.prototype.map) {
417 Array.prototype.map = function() {
418 throw new Error('map is unimplemented for this js engine');
419 };
420 }
421
422 exports.keys = function(obj) {
423 if(Object.prototype.keys) {
424 return obj.keys();
425 }
426 else {
427 var keys = [];
428 for(var k in obj) {
429 if(obj.hasOwnProperty(k)) {
430 keys.push(k);
431 }
432 }
433 return keys;
434 }
435 };
436
437 exports.inOperator = function (key, val) {
438 if (exports.isArray(val)) {
439 return exports.indexOf(val, key) !== -1;
440 } else if (exports.isObject(val)) {
441 return key in val;
442 } else if (exports.isString(val)) {
443 return val.indexOf(key) !== -1;
444 } else {
445 throw new Error('Cannot use "in" operator to search for "'
446 + key + '" in unexpected types.');
447 }
448 };
449
450
451/***/ },
452/* 2 */
453/***/ function(module, exports, __webpack_require__) {
454
455 'use strict';
456
457 var path = __webpack_require__(3);
458 var asap = __webpack_require__(4);
459 var lib = __webpack_require__(1);
460 var Obj = __webpack_require__(6);
461 var compiler = __webpack_require__(7);
462 var builtin_filters = __webpack_require__(13);
463 var builtin_loaders = __webpack_require__(14);
464 var runtime = __webpack_require__(12);
465 var globals = __webpack_require__(17);
466 var Frame = runtime.Frame;
467 var Template;
468
469 // Unconditionally load in this loader, even if no other ones are
470 // included (possible in the slim browser build)
471 builtin_loaders.PrecompiledLoader = __webpack_require__(16);
472
473 // If the user is using the async API, *always* call it
474 // asynchronously even if the template was synchronous.
475 function callbackAsap(cb, err, res) {
476 asap(function() { cb(err, res); });
477 }
478
479 var Environment = Obj.extend({
480 init: function(loaders, opts) {
481 // The dev flag determines the trace that'll be shown on errors.
482 // If set to true, returns the full trace from the error point,
483 // otherwise will return trace starting from Template.render
484 // (the full trace from within nunjucks may confuse developers using
485 // the library)
486 // defaults to false
487 opts = this.opts = opts || {};
488 this.opts.dev = !!opts.dev;
489
490 // The autoescape flag sets global autoescaping. If true,
491 // every string variable will be escaped by default.
492 // If false, strings can be manually escaped using the `escape` filter.
493 // defaults to true
494 this.opts.autoescape = opts.autoescape != null ? opts.autoescape : true;
495
496 // If true, this will make the system throw errors if trying
497 // to output a null or undefined value
498 this.opts.throwOnUndefined = !!opts.throwOnUndefined;
499 this.opts.trimBlocks = !!opts.trimBlocks;
500 this.opts.lstripBlocks = !!opts.lstripBlocks;
501
502 this.loaders = [];
503
504 if(!loaders) {
505 // The filesystem loader is only available server-side
506 if(builtin_loaders.FileSystemLoader) {
507 this.loaders = [new builtin_loaders.FileSystemLoader('views')];
508 }
509 else if(builtin_loaders.WebLoader) {
510 this.loaders = [new builtin_loaders.WebLoader('/views')];
511 }
512 }
513 else {
514 this.loaders = lib.isArray(loaders) ? loaders : [loaders];
515 }
516
517 // It's easy to use precompiled templates: just include them
518 // before you configure nunjucks and this will automatically
519 // pick it up and use it
520 if((true) && window.nunjucksPrecompiled) {
521 this.loaders.unshift(
522 new builtin_loaders.PrecompiledLoader(window.nunjucksPrecompiled)
523 );
524 }
525
526 this.initCache();
527
528 this.globals = globals();
529 this.filters = {};
530 this.asyncFilters = [];
531 this.extensions = {};
532 this.extensionsList = [];
533
534 for(var name in builtin_filters) {
535 this.addFilter(name, builtin_filters[name]);
536 }
537 },
538
539 initCache: function() {
540 // Caching and cache busting
541 lib.each(this.loaders, function(loader) {
542 loader.cache = {};
543
544 if(typeof loader.on === 'function') {
545 loader.on('update', function(template) {
546 loader.cache[template] = null;
547 });
548 }
549 });
550 },
551
552 addExtension: function(name, extension) {
553 extension._name = name;
554 this.extensions[name] = extension;
555 this.extensionsList.push(extension);
556 return this;
557 },
558
559 removeExtension: function(name) {
560 var extension = this.getExtension(name);
561 if (!extension) return;
562
563 this.extensionsList = lib.without(this.extensionsList, extension);
564 delete this.extensions[name];
565 },
566
567 getExtension: function(name) {
568 return this.extensions[name];
569 },
570
571 hasExtension: function(name) {
572 return !!this.extensions[name];
573 },
574
575 addGlobal: function(name, value) {
576 this.globals[name] = value;
577 return this;
578 },
579
580 getGlobal: function(name) {
581 if(typeof this.globals[name] === 'undefined') {
582 throw new Error('global not found: ' + name);
583 }
584 return this.globals[name];
585 },
586
587 addFilter: function(name, func, async) {
588 var wrapped = func;
589
590 if(async) {
591 this.asyncFilters.push(name);
592 }
593 this.filters[name] = wrapped;
594 return this;
595 },
596
597 getFilter: function(name) {
598 if(!this.filters[name]) {
599 throw new Error('filter not found: ' + name);
600 }
601 return this.filters[name];
602 },
603
604 resolveTemplate: function(loader, parentName, filename) {
605 var isRelative = (loader.isRelative && parentName)? loader.isRelative(filename) : false;
606 return (isRelative && loader.resolve)? loader.resolve(parentName, filename) : filename;
607 },
608
609 getTemplate: function(name, eagerCompile, parentName, ignoreMissing, cb) {
610 var that = this;
611 var tmpl = null;
612 if(name && name.raw) {
613 // this fixes autoescape for templates referenced in symbols
614 name = name.raw;
615 }
616
617 if(lib.isFunction(parentName)) {
618 cb = parentName;
619 parentName = null;
620 eagerCompile = eagerCompile || false;
621 }
622
623 if(lib.isFunction(eagerCompile)) {
624 cb = eagerCompile;
625 eagerCompile = false;
626 }
627
628 if (name instanceof Template) {
629 tmpl = name;
630 }
631 else if(typeof name !== 'string') {
632 throw new Error('template names must be a string: ' + name);
633 }
634 else {
635 for (var i = 0; i < this.loaders.length; i++) {
636 var _name = this.resolveTemplate(this.loaders[i], parentName, name);
637 tmpl = this.loaders[i].cache[_name];
638 if (tmpl) break;
639 }
640 }
641
642 if(tmpl) {
643 if(eagerCompile) {
644 tmpl.compile();
645 }
646
647 if(cb) {
648 cb(null, tmpl);
649 }
650 else {
651 return tmpl;
652 }
653 } else {
654 var syncResult;
655 var _this = this;
656
657 var createTemplate = function(err, info) {
658 if(!info && !err) {
659 if(!ignoreMissing) {
660 err = new Error('template not found: ' + name);
661 }
662 }
663
664 if (err) {
665 if(cb) {
666 cb(err);
667 }
668 else {
669 throw err;
670 }
671 }
672 else {
673 var tmpl;
674 if(info) {
675 tmpl = new Template(info.src, _this,
676 info.path, eagerCompile);
677
678 if(!info.noCache) {
679 info.loader.cache[name] = tmpl;
680 }
681 }
682 else {
683 tmpl = new Template('', _this,
684 '', eagerCompile);
685 }
686
687 if(cb) {
688 cb(null, tmpl);
689 }
690 else {
691 syncResult = tmpl;
692 }
693 }
694 };
695
696 lib.asyncIter(this.loaders, function(loader, i, next, done) {
697 function handle(err, src) {
698 if(err) {
699 done(err);
700 }
701 else if(src) {
702 src.loader = loader;
703 done(null, src);
704 }
705 else {
706 next();
707 }
708 }
709
710 // Resolve name relative to parentName
711 name = that.resolveTemplate(loader, parentName, name);
712
713 if(loader.async) {
714 loader.getSource(name, handle);
715 }
716 else {
717 handle(null, loader.getSource(name));
718 }
719 }, createTemplate);
720
721 return syncResult;
722 }
723 },
724
725 express: function(app) {
726 var env = this;
727
728 function NunjucksView(name, opts) {
729 this.name = name;
730 this.path = name;
731 this.defaultEngine = opts.defaultEngine;
732 this.ext = path.extname(name);
733 if (!this.ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
734 if (!this.ext) this.name += (this.ext = ('.' !== this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
735 }
736
737 NunjucksView.prototype.render = function(opts, cb) {
738 env.render(this.name, opts, cb);
739 };
740
741 app.set('view', NunjucksView);
742 app.set('nunjucksEnv', this);
743 return this;
744 },
745
746 render: function(name, ctx, cb) {
747 if(lib.isFunction(ctx)) {
748 cb = ctx;
749 ctx = null;
750 }
751
752 // We support a synchronous API to make it easier to migrate
753 // existing code to async. This works because if you don't do
754 // anything async work, the whole thing is actually run
755 // synchronously.
756 var syncResult = null;
757
758 this.getTemplate(name, function(err, tmpl) {
759 if(err && cb) {
760 callbackAsap(cb, err);
761 }
762 else if(err) {
763 throw err;
764 }
765 else {
766 syncResult = tmpl.render(ctx, cb);
767 }
768 });
769
770 return syncResult;
771 },
772
773 renderString: function(src, ctx, opts, cb) {
774 if(lib.isFunction(opts)) {
775 cb = opts;
776 opts = {};
777 }
778 opts = opts || {};
779
780 var tmpl = new Template(src, this, opts.path);
781 return tmpl.render(ctx, cb);
782 }
783 });
784
785 var Context = Obj.extend({
786 init: function(ctx, blocks, env) {
787 // Has to be tied to an environment so we can tap into its globals.
788 this.env = env || new Environment();
789
790 // Make a duplicate of ctx
791 this.ctx = {};
792 for(var k in ctx) {
793 if(ctx.hasOwnProperty(k)) {
794 this.ctx[k] = ctx[k];
795 }
796 }
797
798 this.blocks = {};
799 this.exported = [];
800
801 for(var name in blocks) {
802 this.addBlock(name, blocks[name]);
803 }
804 },
805
806 lookup: function(name) {
807 // This is one of the most called functions, so optimize for
808 // the typical case where the name isn't in the globals
809 if(name in this.env.globals && !(name in this.ctx)) {
810 return this.env.globals[name];
811 }
812 else {
813 return this.ctx[name];
814 }
815 },
816
817 setVariable: function(name, val) {
818 this.ctx[name] = val;
819 },
820
821 getVariables: function() {
822 return this.ctx;
823 },
824
825 addBlock: function(name, block) {
826 this.blocks[name] = this.blocks[name] || [];
827 this.blocks[name].push(block);
828 return this;
829 },
830
831 getBlock: function(name) {
832 if(!this.blocks[name]) {
833 throw new Error('unknown block "' + name + '"');
834 }
835
836 return this.blocks[name][0];
837 },
838
839 getSuper: function(env, name, block, frame, runtime, cb) {
840 var idx = lib.indexOf(this.blocks[name] || [], block);
841 var blk = this.blocks[name][idx + 1];
842 var context = this;
843
844 if(idx === -1 || !blk) {
845 throw new Error('no super block available for "' + name + '"');
846 }
847
848 blk(env, context, frame, runtime, cb);
849 },
850
851 addExport: function(name) {
852 this.exported.push(name);
853 },
854
855 getExported: function() {
856 var exported = {};
857 for(var i=0; i<this.exported.length; i++) {
858 var name = this.exported[i];
859 exported[name] = this.ctx[name];
860 }
861 return exported;
862 }
863 });
864
865 Template = Obj.extend({
866 init: function (src, env, path, eagerCompile) {
867 this.env = env || new Environment();
868
869 if(lib.isObject(src)) {
870 switch(src.type) {
871 case 'code': this.tmplProps = src.obj; break;
872 case 'string': this.tmplStr = src.obj; break;
873 }
874 }
875 else if(lib.isString(src)) {
876 this.tmplStr = src;
877 }
878 else {
879 throw new Error('src must be a string or an object describing ' +
880 'the source');
881 }
882
883 this.path = path;
884
885 if(eagerCompile) {
886 var _this = this;
887 try {
888 _this._compile();
889 }
890 catch(err) {
891 throw lib.prettifyError(this.path, this.env.opts.dev, err);
892 }
893 }
894 else {
895 this.compiled = false;
896 }
897 },
898
899 render: function(ctx, parentFrame, cb) {
900 if (typeof ctx === 'function') {
901 cb = ctx;
902 ctx = {};
903 }
904 else if (typeof parentFrame === 'function') {
905 cb = parentFrame;
906 parentFrame = null;
907 }
908
909 var forceAsync = true;
910 if(parentFrame) {
911 // If there is a frame, we are being called from internal
912 // code of another template, and the internal system
913 // depends on the sync/async nature of the parent template
914 // to be inherited, so force an async callback
915 forceAsync = false;
916 }
917
918 var _this = this;
919 // Catch compile errors for async rendering
920 try {
921 _this.compile();
922 } catch (_err) {
923 var err = lib.prettifyError(this.path, this.env.opts.dev, _err);
924 if (cb) return callbackAsap(cb, err);
925 else throw err;
926 }
927
928 var context = new Context(ctx || {}, _this.blocks, _this.env);
929 var frame = parentFrame ? parentFrame.push(true) : new Frame();
930 frame.topLevel = true;
931 var syncResult = null;
932
933 _this.rootRenderFunc(
934 _this.env,
935 context,
936 frame || new Frame(),
937 runtime,
938 function(err, res) {
939 if(err) {
940 err = lib.prettifyError(_this.path, _this.env.opts.dev, err);
941 }
942
943 if(cb) {
944 if(forceAsync) {
945 callbackAsap(cb, err, res);
946 }
947 else {
948 cb(err, res);
949 }
950 }
951 else {
952 if(err) { throw err; }
953 syncResult = res;
954 }
955 }
956 );
957
958 return syncResult;
959 },
960
961
962 getExported: function(ctx, parentFrame, cb) {
963 if (typeof ctx === 'function') {
964 cb = ctx;
965 ctx = {};
966 }
967
968 if (typeof parentFrame === 'function') {
969 cb = parentFrame;
970 parentFrame = null;
971 }
972
973 // Catch compile errors for async rendering
974 try {
975 this.compile();
976 } catch (e) {
977 if (cb) return cb(e);
978 else throw e;
979 }
980
981 var frame = parentFrame ? parentFrame.push() : new Frame();
982 frame.topLevel = true;
983
984 // Run the rootRenderFunc to populate the context with exported vars
985 var context = new Context(ctx || {}, this.blocks, this.env);
986 this.rootRenderFunc(this.env,
987 context,
988 frame,
989 runtime,
990 function(err) {
991 if ( err ) {
992 cb(err, null);
993 } else {
994 cb(null, context.getExported());
995 }
996 });
997 },
998
999 compile: function() {
1000 if(!this.compiled) {
1001 this._compile();
1002 }
1003 },
1004
1005 _compile: function() {
1006 var props;
1007
1008 if(this.tmplProps) {
1009 props = this.tmplProps;
1010 }
1011 else {
1012 var source = compiler.compile(this.tmplStr,
1013 this.env.asyncFilters,
1014 this.env.extensionsList,
1015 this.path,
1016 this.env.opts);
1017
1018 /* jslint evil: true */
1019 var func = new Function(source);
1020 props = func();
1021 }
1022
1023 this.blocks = this._getBlocks(props);
1024 this.rootRenderFunc = props.root;
1025 this.compiled = true;
1026 },
1027
1028 _getBlocks: function(props) {
1029 var blocks = {};
1030
1031 for(var k in props) {
1032 if(k.slice(0, 2) === 'b_') {
1033 blocks[k.slice(2)] = props[k];
1034 }
1035 }
1036
1037 return blocks;
1038 }
1039 });
1040
1041 module.exports = {
1042 Environment: Environment,
1043 Template: Template
1044 };
1045
1046
1047/***/ },
1048/* 3 */
1049/***/ function(module, exports) {
1050
1051
1052
1053/***/ },
1054/* 4 */
1055/***/ function(module, exports, __webpack_require__) {
1056
1057 "use strict";
1058
1059 // rawAsap provides everything we need except exception management.
1060 var rawAsap = __webpack_require__(5);
1061 // RawTasks are recycled to reduce GC churn.
1062 var freeTasks = [];
1063 // We queue errors to ensure they are thrown in right order (FIFO).
1064 // Array-as-queue is good enough here, since we are just dealing with exceptions.
1065 var pendingErrors = [];
1066 var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);
1067
1068 function throwFirstError() {
1069 if (pendingErrors.length) {
1070 throw pendingErrors.shift();
1071 }
1072 }
1073
1074 /**
1075 * Calls a task as soon as possible after returning, in its own event, with priority
1076 * over other events like animation, reflow, and repaint. An error thrown from an
1077 * event will not interrupt, nor even substantially slow down the processing of
1078 * other events, but will be rather postponed to a lower priority event.
1079 * @param {{call}} task A callable object, typically a function that takes no
1080 * arguments.
1081 */
1082 module.exports = asap;
1083 function asap(task) {
1084 var rawTask;
1085 if (freeTasks.length) {
1086 rawTask = freeTasks.pop();
1087 } else {
1088 rawTask = new RawTask();
1089 }
1090 rawTask.task = task;
1091 rawAsap(rawTask);
1092 }
1093
1094 // We wrap tasks with recyclable task objects. A task object implements
1095 // `call`, just like a function.
1096 function RawTask() {
1097 this.task = null;
1098 }
1099
1100 // The sole purpose of wrapping the task is to catch the exception and recycle
1101 // the task object after its single use.
1102 RawTask.prototype.call = function () {
1103 try {
1104 this.task.call();
1105 } catch (error) {
1106 if (asap.onerror) {
1107 // This hook exists purely for testing purposes.
1108 // Its name will be periodically randomized to break any code that
1109 // depends on its existence.
1110 asap.onerror(error);
1111 } else {
1112 // In a web browser, exceptions are not fatal. However, to avoid
1113 // slowing down the queue of pending tasks, we rethrow the error in a
1114 // lower priority turn.
1115 pendingErrors.push(error);
1116 requestErrorThrow();
1117 }
1118 } finally {
1119 this.task = null;
1120 freeTasks[freeTasks.length] = this;
1121 }
1122 };
1123
1124
1125/***/ },
1126/* 5 */
1127/***/ function(module, exports) {
1128
1129 /* WEBPACK VAR INJECTION */(function(global) {"use strict";
1130
1131 // Use the fastest means possible to execute a task in its own turn, with
1132 // priority over other events including IO, animation, reflow, and redraw
1133 // events in browsers.
1134 //
1135 // An exception thrown by a task will permanently interrupt the processing of
1136 // subsequent tasks. The higher level `asap` function ensures that if an
1137 // exception is thrown by a task, that the task queue will continue flushing as
1138 // soon as possible, but if you use `rawAsap` directly, you are responsible to
1139 // either ensure that no exceptions are thrown from your task, or to manually
1140 // call `rawAsap.requestFlush` if an exception is thrown.
1141 module.exports = rawAsap;
1142 function rawAsap(task) {
1143 if (!queue.length) {
1144 requestFlush();
1145 flushing = true;
1146 }
1147 // Equivalent to push, but avoids a function call.
1148 queue[queue.length] = task;
1149 }
1150
1151 var queue = [];
1152 // Once a flush has been requested, no further calls to `requestFlush` are
1153 // necessary until the next `flush` completes.
1154 var flushing = false;
1155 // `requestFlush` is an implementation-specific method that attempts to kick
1156 // off a `flush` event as quickly as possible. `flush` will attempt to exhaust
1157 // the event queue before yielding to the browser's own event loop.
1158 var requestFlush;
1159 // The position of the next task to execute in the task queue. This is
1160 // preserved between calls to `flush` so that it can be resumed if
1161 // a task throws an exception.
1162 var index = 0;
1163 // If a task schedules additional tasks recursively, the task queue can grow
1164 // unbounded. To prevent memory exhaustion, the task queue will periodically
1165 // truncate already-completed tasks.
1166 var capacity = 1024;
1167
1168 // The flush function processes all tasks that have been scheduled with
1169 // `rawAsap` unless and until one of those tasks throws an exception.
1170 // If a task throws an exception, `flush` ensures that its state will remain
1171 // consistent and will resume where it left off when called again.
1172 // However, `flush` does not make any arrangements to be called again if an
1173 // exception is thrown.
1174 function flush() {
1175 while (index < queue.length) {
1176 var currentIndex = index;
1177 // Advance the index before calling the task. This ensures that we will
1178 // begin flushing on the next task the task throws an error.
1179 index = index + 1;
1180 queue[currentIndex].call();
1181 // Prevent leaking memory for long chains of recursive calls to `asap`.
1182 // If we call `asap` within tasks scheduled by `asap`, the queue will
1183 // grow, but to avoid an O(n) walk for every task we execute, we don't
1184 // shift tasks off the queue after they have been executed.
1185 // Instead, we periodically shift 1024 tasks off the queue.
1186 if (index > capacity) {
1187 // Manually shift all values starting at the index back to the
1188 // beginning of the queue.
1189 for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
1190 queue[scan] = queue[scan + index];
1191 }
1192 queue.length -= index;
1193 index = 0;
1194 }
1195 }
1196 queue.length = 0;
1197 index = 0;
1198 flushing = false;
1199 }
1200
1201 // `requestFlush` is implemented using a strategy based on data collected from
1202 // every available SauceLabs Selenium web driver worker at time of writing.
1203 // https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593
1204
1205 // Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that
1206 // have WebKitMutationObserver but not un-prefixed MutationObserver.
1207 // Must use `global` instead of `window` to work in both frames and web
1208 // workers. `global` is a provision of Browserify, Mr, Mrs, or Mop.
1209 var BrowserMutationObserver = global.MutationObserver || global.WebKitMutationObserver;
1210
1211 // MutationObservers are desirable because they have high priority and work
1212 // reliably everywhere they are implemented.
1213 // They are implemented in all modern browsers.
1214 //
1215 // - Android 4-4.3
1216 // - Chrome 26-34
1217 // - Firefox 14-29
1218 // - Internet Explorer 11
1219 // - iPad Safari 6-7.1
1220 // - iPhone Safari 7-7.1
1221 // - Safari 6-7
1222 if (typeof BrowserMutationObserver === "function") {
1223 requestFlush = makeRequestCallFromMutationObserver(flush);
1224
1225 // MessageChannels are desirable because they give direct access to the HTML
1226 // task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera
1227 // 11-12, and in web workers in many engines.
1228 // Although message channels yield to any queued rendering and IO tasks, they
1229 // would be better than imposing the 4ms delay of timers.
1230 // However, they do not work reliably in Internet Explorer or Safari.
1231
1232 // Internet Explorer 10 is the only browser that has setImmediate but does
1233 // not have MutationObservers.
1234 // Although setImmediate yields to the browser's renderer, it would be
1235 // preferrable to falling back to setTimeout since it does not have
1236 // the minimum 4ms penalty.
1237 // Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and
1238 // Desktop to a lesser extent) that renders both setImmediate and
1239 // MessageChannel useless for the purposes of ASAP.
1240 // https://github.com/kriskowal/q/issues/396
1241
1242 // Timers are implemented universally.
1243 // We fall back to timers in workers in most engines, and in foreground
1244 // contexts in the following browsers.
1245 // However, note that even this simple case requires nuances to operate in a
1246 // broad spectrum of browsers.
1247 //
1248 // - Firefox 3-13
1249 // - Internet Explorer 6-9
1250 // - iPad Safari 4.3
1251 // - Lynx 2.8.7
1252 } else {
1253 requestFlush = makeRequestCallFromTimer(flush);
1254 }
1255
1256 // `requestFlush` requests that the high priority event queue be flushed as
1257 // soon as possible.
1258 // This is useful to prevent an error thrown in a task from stalling the event
1259 // queue if the exception handled by Node.js’s
1260 // `process.on("uncaughtException")` or by a domain.
1261 rawAsap.requestFlush = requestFlush;
1262
1263 // To request a high priority event, we induce a mutation observer by toggling
1264 // the text of a text node between "1" and "-1".
1265 function makeRequestCallFromMutationObserver(callback) {
1266 var toggle = 1;
1267 var observer = new BrowserMutationObserver(callback);
1268 var node = document.createTextNode("");
1269 observer.observe(node, {characterData: true});
1270 return function requestCall() {
1271 toggle = -toggle;
1272 node.data = toggle;
1273 };
1274 }
1275
1276 // The message channel technique was discovered by Malte Ubl and was the
1277 // original foundation for this library.
1278 // http://www.nonblocking.io/2011/06/windownexttick.html
1279
1280 // Safari 6.0.5 (at least) intermittently fails to create message ports on a
1281 // page's first load. Thankfully, this version of Safari supports
1282 // MutationObservers, so we don't need to fall back in that case.
1283
1284 // function makeRequestCallFromMessageChannel(callback) {
1285 // var channel = new MessageChannel();
1286 // channel.port1.onmessage = callback;
1287 // return function requestCall() {
1288 // channel.port2.postMessage(0);
1289 // };
1290 // }
1291
1292 // For reasons explained above, we are also unable to use `setImmediate`
1293 // under any circumstances.
1294 // Even if we were, there is another bug in Internet Explorer 10.
1295 // It is not sufficient to assign `setImmediate` to `requestFlush` because
1296 // `setImmediate` must be called *by name* and therefore must be wrapped in a
1297 // closure.
1298 // Never forget.
1299
1300 // function makeRequestCallFromSetImmediate(callback) {
1301 // return function requestCall() {
1302 // setImmediate(callback);
1303 // };
1304 // }
1305
1306 // Safari 6.0 has a problem where timers will get lost while the user is
1307 // scrolling. This problem does not impact ASAP because Safari 6.0 supports
1308 // mutation observers, so that implementation is used instead.
1309 // However, if we ever elect to use timers in Safari, the prevalent work-around
1310 // is to add a scroll event listener that calls for a flush.
1311
1312 // `setTimeout` does not call the passed callback if the delay is less than
1313 // approximately 7 in web workers in Firefox 8 through 18, and sometimes not
1314 // even then.
1315
1316 function makeRequestCallFromTimer(callback) {
1317 return function requestCall() {
1318 // We dispatch a timeout with a specified delay of 0 for engines that
1319 // can reliably accommodate that request. This will usually be snapped
1320 // to a 4 milisecond delay, but once we're flushing, there's no delay
1321 // between events.
1322 var timeoutHandle = setTimeout(handleTimer, 0);
1323 // However, since this timer gets frequently dropped in Firefox
1324 // workers, we enlist an interval handle that will try to fire
1325 // an event 20 times per second until it succeeds.
1326 var intervalHandle = setInterval(handleTimer, 50);
1327
1328 function handleTimer() {
1329 // Whichever timer succeeds will cancel both timers and
1330 // execute the callback.
1331 clearTimeout(timeoutHandle);
1332 clearInterval(intervalHandle);
1333 callback();
1334 }
1335 };
1336 }
1337
1338 // This is for `asap.js` only.
1339 // Its name will be periodically randomized to break any code that depends on
1340 // its existence.
1341 rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer;
1342
1343 // ASAP was originally a nextTick shim included in Q. This was factored out
1344 // into this ASAP package. It was later adapted to RSVP which made further
1345 // amendments. These decisions, particularly to marginalize MessageChannel and
1346 // to capture the MutationObserver implementation in a closure, were integrated
1347 // back into ASAP proper.
1348 // https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js
1349
1350 /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
1351
1352/***/ },
1353/* 6 */
1354/***/ function(module, exports) {
1355
1356 'use strict';
1357
1358 // A simple class system, more documentation to come
1359
1360 function extend(cls, name, props) {
1361 // This does that same thing as Object.create, but with support for IE8
1362 var F = function() {};
1363 F.prototype = cls.prototype;
1364 var prototype = new F();
1365
1366 // jshint undef: false
1367 var fnTest = /xyz/.test(function(){ xyz; }) ? /\bparent\b/ : /.*/;
1368 props = props || {};
1369
1370 for(var k in props) {
1371 var src = props[k];
1372 var parent = prototype[k];
1373
1374 if(typeof parent === 'function' &&
1375 typeof src === 'function' &&
1376 fnTest.test(src)) {
1377 /*jshint -W083 */
1378 prototype[k] = (function (src, parent) {
1379 return function() {
1380 // Save the current parent method
1381 var tmp = this.parent;
1382
1383 // Set parent to the previous method, call, and restore
1384 this.parent = parent;
1385 var res = src.apply(this, arguments);
1386 this.parent = tmp;
1387
1388 return res;
1389 };
1390 })(src, parent);
1391 }
1392 else {
1393 prototype[k] = src;
1394 }
1395 }
1396
1397 prototype.typename = name;
1398
1399 var new_cls = function() {
1400 if(prototype.init) {
1401 prototype.init.apply(this, arguments);
1402 }
1403 };
1404
1405 new_cls.prototype = prototype;
1406 new_cls.prototype.constructor = new_cls;
1407
1408 new_cls.extend = function(name, props) {
1409 if(typeof name === 'object') {
1410 props = name;
1411 name = 'anonymous';
1412 }
1413 return extend(new_cls, name, props);
1414 };
1415
1416 return new_cls;
1417 }
1418
1419 module.exports = extend(Object, 'Object', {});
1420
1421
1422/***/ },
1423/* 7 */
1424/***/ function(module, exports, __webpack_require__) {
1425
1426 'use strict';
1427
1428 var lib = __webpack_require__(1);
1429 var parser = __webpack_require__(8);
1430 var transformer = __webpack_require__(11);
1431 var nodes = __webpack_require__(10);
1432 // jshint -W079
1433 var Object = __webpack_require__(6);
1434 var Frame = __webpack_require__(12).Frame;
1435
1436 // These are all the same for now, but shouldn't be passed straight
1437 // through
1438 var compareOps = {
1439 '==': '==',
1440 '===': '===',
1441 '!=': '!=',
1442 '!==': '!==',
1443 '<': '<',
1444 '>': '>',
1445 '<=': '<=',
1446 '>=': '>='
1447 };
1448
1449 // A common pattern is to emit binary operators
1450 function binOpEmitter(str) {
1451 return function(node, frame) {
1452 this.compile(node.left, frame);
1453 this.emit(str);
1454 this.compile(node.right, frame);
1455 };
1456 }
1457
1458 var Compiler = Object.extend({
1459 init: function(templateName, throwOnUndefined) {
1460 this.templateName = templateName;
1461 this.codebuf = [];
1462 this.lastId = 0;
1463 this.buffer = null;
1464 this.bufferStack = [];
1465 this.scopeClosers = '';
1466 this.inBlock = false;
1467 this.throwOnUndefined = throwOnUndefined;
1468 },
1469
1470 fail: function (msg, lineno, colno) {
1471 if (lineno !== undefined) lineno += 1;
1472 if (colno !== undefined) colno += 1;
1473
1474 throw new lib.TemplateError(msg, lineno, colno);
1475 },
1476
1477 pushBufferId: function(id) {
1478 this.bufferStack.push(this.buffer);
1479 this.buffer = id;
1480 this.emit('var ' + this.buffer + ' = "";');
1481 },
1482
1483 popBufferId: function() {
1484 this.buffer = this.bufferStack.pop();
1485 },
1486
1487 emit: function(code) {
1488 this.codebuf.push(code);
1489 },
1490
1491 emitLine: function(code) {
1492 this.emit(code + '\n');
1493 },
1494
1495 emitLines: function() {
1496 lib.each(lib.toArray(arguments), function(line) {
1497 this.emitLine(line);
1498 }, this);
1499 },
1500
1501 emitFuncBegin: function(name) {
1502 this.buffer = 'output';
1503 this.scopeClosers = '';
1504 this.emitLine('function ' + name + '(env, context, frame, runtime, cb) {');
1505 this.emitLine('var lineno = null;');
1506 this.emitLine('var colno = null;');
1507 this.emitLine('var ' + this.buffer + ' = "";');
1508 this.emitLine('try {');
1509 },
1510
1511 emitFuncEnd: function(noReturn) {
1512 if(!noReturn) {
1513 this.emitLine('cb(null, ' + this.buffer +');');
1514 }
1515
1516 this.closeScopeLevels();
1517 this.emitLine('} catch (e) {');
1518 this.emitLine(' cb(runtime.handleError(e, lineno, colno));');
1519 this.emitLine('}');
1520 this.emitLine('}');
1521 this.buffer = null;
1522 },
1523
1524 addScopeLevel: function() {
1525 this.scopeClosers += '})';
1526 },
1527
1528 closeScopeLevels: function() {
1529 this.emitLine(this.scopeClosers + ';');
1530 this.scopeClosers = '';
1531 },
1532
1533 withScopedSyntax: function(func) {
1534 var scopeClosers = this.scopeClosers;
1535 this.scopeClosers = '';
1536
1537 func.call(this);
1538
1539 this.closeScopeLevels();
1540 this.scopeClosers = scopeClosers;
1541 },
1542
1543 makeCallback: function(res) {
1544 var err = this.tmpid();
1545
1546 return 'function(' + err + (res ? ',' + res : '') + ') {\n' +
1547 'if(' + err + ') { cb(' + err + '); return; }';
1548 },
1549
1550 tmpid: function() {
1551 this.lastId++;
1552 return 't_' + this.lastId;
1553 },
1554
1555 _templateName: function() {
1556 return this.templateName == null? 'undefined' : JSON.stringify(this.templateName);
1557 },
1558
1559 _compileChildren: function(node, frame) {
1560 var children = node.children;
1561 for(var i=0, l=children.length; i<l; i++) {
1562 this.compile(children[i], frame);
1563 }
1564 },
1565
1566 _compileAggregate: function(node, frame, startChar, endChar) {
1567 if(startChar) {
1568 this.emit(startChar);
1569 }
1570
1571 for(var i=0; i<node.children.length; i++) {
1572 if(i > 0) {
1573 this.emit(',');
1574 }
1575
1576 this.compile(node.children[i], frame);
1577 }
1578
1579 if(endChar) {
1580 this.emit(endChar);
1581 }
1582 },
1583
1584 _compileExpression: function(node, frame) {
1585 // TODO: I'm not really sure if this type check is worth it or
1586 // not.
1587 this.assertType(
1588 node,
1589 nodes.Literal,
1590 nodes.Symbol,
1591 nodes.Group,
1592 nodes.Array,
1593 nodes.Dict,
1594 nodes.FunCall,
1595 nodes.Caller,
1596 nodes.Filter,
1597 nodes.LookupVal,
1598 nodes.Compare,
1599 nodes.InlineIf,
1600 nodes.In,
1601 nodes.And,
1602 nodes.Or,
1603 nodes.Not,
1604 nodes.Add,
1605 nodes.Concat,
1606 nodes.Sub,
1607 nodes.Mul,
1608 nodes.Div,
1609 nodes.FloorDiv,
1610 nodes.Mod,
1611 nodes.Pow,
1612 nodes.Neg,
1613 nodes.Pos,
1614 nodes.Compare,
1615 nodes.NodeList
1616 );
1617 this.compile(node, frame);
1618 },
1619
1620 assertType: function(node /*, types */) {
1621 var types = lib.toArray(arguments).slice(1);
1622 var success = false;
1623
1624 for(var i=0; i<types.length; i++) {
1625 if(node instanceof types[i]) {
1626 success = true;
1627 }
1628 }
1629
1630 if(!success) {
1631 this.fail('assertType: invalid type: ' + node.typename,
1632 node.lineno,
1633 node.colno);
1634 }
1635 },
1636
1637 compileCallExtension: function(node, frame, async) {
1638 var args = node.args;
1639 var contentArgs = node.contentArgs;
1640 var autoescape = typeof node.autoescape === 'boolean' ? node.autoescape : true;
1641
1642 if(!async) {
1643 this.emit(this.buffer + ' += runtime.suppressValue(');
1644 }
1645
1646 this.emit('env.getExtension("' + node.extName + '")["' + node.prop + '"](');
1647 this.emit('context');
1648
1649 if(args || contentArgs) {
1650 this.emit(',');
1651 }
1652
1653 if(args) {
1654 if(!(args instanceof nodes.NodeList)) {
1655 this.fail('compileCallExtension: arguments must be a NodeList, ' +
1656 'use `parser.parseSignature`');
1657 }
1658
1659 lib.each(args.children, function(arg, i) {
1660 // Tag arguments are passed normally to the call. Note
1661 // that keyword arguments are turned into a single js
1662 // object as the last argument, if they exist.
1663 this._compileExpression(arg, frame);
1664
1665 if(i !== args.children.length - 1 || contentArgs.length) {
1666 this.emit(',');
1667 }
1668 }, this);
1669 }
1670
1671 if(contentArgs.length) {
1672 lib.each(contentArgs, function(arg, i) {
1673 if(i > 0) {
1674 this.emit(',');
1675 }
1676
1677 if(arg) {
1678 var id = this.tmpid();
1679
1680 this.emitLine('function(cb) {');
1681 this.emitLine('if(!cb) { cb = function(err) { if(err) { throw err; }}}');
1682 this.pushBufferId(id);
1683
1684 this.withScopedSyntax(function() {
1685 this.compile(arg, frame);
1686 this.emitLine('cb(null, ' + id + ');');
1687 });
1688
1689 this.popBufferId();
1690 this.emitLine('return ' + id + ';');
1691 this.emitLine('}');
1692 }
1693 else {
1694 this.emit('null');
1695 }
1696 }, this);
1697 }
1698
1699 if(async) {
1700 var res = this.tmpid();
1701 this.emitLine(', ' + this.makeCallback(res));
1702 this.emitLine(this.buffer + ' += runtime.suppressValue(' + res + ', ' + autoescape + ' && env.opts.autoescape);');
1703 this.addScopeLevel();
1704 }
1705 else {
1706 this.emit(')');
1707 this.emit(', ' + autoescape + ' && env.opts.autoescape);\n');
1708 }
1709 },
1710
1711 compileCallExtensionAsync: function(node, frame) {
1712 this.compileCallExtension(node, frame, true);
1713 },
1714
1715 compileNodeList: function(node, frame) {
1716 this._compileChildren(node, frame);
1717 },
1718
1719 compileLiteral: function(node) {
1720 if(typeof node.value === 'string') {
1721 var val = node.value.replace(/\\/g, '\\\\');
1722 val = val.replace(/"/g, '\\"');
1723 val = val.replace(/\n/g, '\\n');
1724 val = val.replace(/\r/g, '\\r');
1725 val = val.replace(/\t/g, '\\t');
1726 this.emit('"' + val + '"');
1727 }
1728 else if (node.value === null) {
1729 this.emit('null');
1730 }
1731 else {
1732 this.emit(node.value.toString());
1733 }
1734 },
1735
1736 compileSymbol: function(node, frame) {
1737 var name = node.value;
1738 var v;
1739
1740 if((v = frame.lookup(name))) {
1741 this.emit(v);
1742 }
1743 else {
1744 this.emit('runtime.contextOrFrameLookup(' +
1745 'context, frame, "' + name + '")');
1746 }
1747 },
1748
1749 compileGroup: function(node, frame) {
1750 this._compileAggregate(node, frame, '(', ')');
1751 },
1752
1753 compileArray: function(node, frame) {
1754 this._compileAggregate(node, frame, '[', ']');
1755 },
1756
1757 compileDict: function(node, frame) {
1758 this._compileAggregate(node, frame, '{', '}');
1759 },
1760
1761 compilePair: function(node, frame) {
1762 var key = node.key;
1763 var val = node.value;
1764
1765 if(key instanceof nodes.Symbol) {
1766 key = new nodes.Literal(key.lineno, key.colno, key.value);
1767 }
1768 else if(!(key instanceof nodes.Literal &&
1769 typeof key.value === 'string')) {
1770 this.fail('compilePair: Dict keys must be strings or names',
1771 key.lineno,
1772 key.colno);
1773 }
1774
1775 this.compile(key, frame);
1776 this.emit(': ');
1777 this._compileExpression(val, frame);
1778 },
1779
1780 compileInlineIf: function(node, frame) {
1781 this.emit('(');
1782 this.compile(node.cond, frame);
1783 this.emit('?');
1784 this.compile(node.body, frame);
1785 this.emit(':');
1786 if(node.else_ !== null)
1787 this.compile(node.else_, frame);
1788 else
1789 this.emit('""');
1790 this.emit(')');
1791 },
1792
1793 compileIn: function(node, frame) {
1794 this.emit('runtime.inOperator(');
1795 this.compile(node.left, frame);
1796 this.emit(',');
1797 this.compile(node.right, frame);
1798 this.emit(')');
1799 },
1800
1801 compileOr: binOpEmitter(' || '),
1802 compileAnd: binOpEmitter(' && '),
1803 compileAdd: binOpEmitter(' + '),
1804 // ensure concatenation instead of addition
1805 // by adding empty string in between
1806 compileConcat: binOpEmitter(' + "" + '),
1807 compileSub: binOpEmitter(' - '),
1808 compileMul: binOpEmitter(' * '),
1809 compileDiv: binOpEmitter(' / '),
1810 compileMod: binOpEmitter(' % '),
1811
1812 compileNot: function(node, frame) {
1813 this.emit('!');
1814 this.compile(node.target, frame);
1815 },
1816
1817 compileFloorDiv: function(node, frame) {
1818 this.emit('Math.floor(');
1819 this.compile(node.left, frame);
1820 this.emit(' / ');
1821 this.compile(node.right, frame);
1822 this.emit(')');
1823 },
1824
1825 compilePow: function(node, frame) {
1826 this.emit('Math.pow(');
1827 this.compile(node.left, frame);
1828 this.emit(', ');
1829 this.compile(node.right, frame);
1830 this.emit(')');
1831 },
1832
1833 compileNeg: function(node, frame) {
1834 this.emit('-');
1835 this.compile(node.target, frame);
1836 },
1837
1838 compilePos: function(node, frame) {
1839 this.emit('+');
1840 this.compile(node.target, frame);
1841 },
1842
1843 compileCompare: function(node, frame) {
1844 this.compile(node.expr, frame);
1845
1846 for(var i=0; i<node.ops.length; i++) {
1847 var n = node.ops[i];
1848 this.emit(' ' + compareOps[n.type] + ' ');
1849 this.compile(n.expr, frame);
1850 }
1851 },
1852
1853 compileLookupVal: function(node, frame) {
1854 this.emit('runtime.memberLookup((');
1855 this._compileExpression(node.target, frame);
1856 this.emit('),');
1857 this._compileExpression(node.val, frame);
1858 this.emit(')');
1859 },
1860
1861 _getNodeName: function(node) {
1862 switch (node.typename) {
1863 case 'Symbol':
1864 return node.value;
1865 case 'FunCall':
1866 return 'the return value of (' + this._getNodeName(node.name) + ')';
1867 case 'LookupVal':
1868 return this._getNodeName(node.target) + '["' +
1869 this._getNodeName(node.val) + '"]';
1870 case 'Literal':
1871 return node.value.toString();
1872 default:
1873 return '--expression--';
1874 }
1875 },
1876
1877 compileFunCall: function(node, frame) {
1878 // Keep track of line/col info at runtime by settings
1879 // variables within an expression. An expression in javascript
1880 // like (x, y, z) returns the last value, and x and y can be
1881 // anything
1882 this.emit('(lineno = ' + node.lineno +
1883 ', colno = ' + node.colno + ', ');
1884
1885 this.emit('runtime.callWrap(');
1886 // Compile it as normal.
1887 this._compileExpression(node.name, frame);
1888
1889 // Output the name of what we're calling so we can get friendly errors
1890 // if the lookup fails.
1891 this.emit(', "' + this._getNodeName(node.name).replace(/"/g, '\\"') + '", context, ');
1892
1893 this._compileAggregate(node.args, frame, '[', '])');
1894
1895 this.emit(')');
1896 },
1897
1898 compileFilter: function(node, frame) {
1899 var name = node.name;
1900 this.assertType(name, nodes.Symbol);
1901 this.emit('env.getFilter("' + name.value + '").call(context, ');
1902 this._compileAggregate(node.args, frame);
1903 this.emit(')');
1904 },
1905
1906 compileFilterAsync: function(node, frame) {
1907 var name = node.name;
1908 this.assertType(name, nodes.Symbol);
1909
1910 var symbol = node.symbol.value;
1911 frame.set(symbol, symbol);
1912
1913 this.emit('env.getFilter("' + name.value + '").call(context, ');
1914 this._compileAggregate(node.args, frame);
1915 this.emitLine(', ' + this.makeCallback(symbol));
1916
1917 this.addScopeLevel();
1918 },
1919
1920 compileKeywordArgs: function(node, frame) {
1921 var names = [];
1922
1923 lib.each(node.children, function(pair) {
1924 names.push(pair.key.value);
1925 });
1926
1927 this.emit('runtime.makeKeywordArgs(');
1928 this.compileDict(node, frame);
1929 this.emit(')');
1930 },
1931
1932 compileSet: function(node, frame) {
1933 var ids = [];
1934
1935 // Lookup the variable names for each identifier and create
1936 // new ones if necessary
1937 lib.each(node.targets, function(target) {
1938 var name = target.value;
1939 var id = frame.lookup(name);
1940
1941 if (id === null || id === undefined) {
1942 id = this.tmpid();
1943
1944 // Note: This relies on js allowing scope across
1945 // blocks, in case this is created inside an `if`
1946 this.emitLine('var ' + id + ';');
1947 }
1948
1949 ids.push(id);
1950 }, this);
1951
1952 if (node.value) {
1953 this.emit(ids.join(' = ') + ' = ');
1954 this._compileExpression(node.value, frame);
1955 this.emitLine(';');
1956 }
1957 else {
1958 this.emit(ids.join(' = ') + ' = ');
1959 this.compile(node.body, frame);
1960 this.emitLine(';');
1961 }
1962
1963 lib.each(node.targets, function(target, i) {
1964 var id = ids[i];
1965 var name = target.value;
1966
1967 // We are running this for every var, but it's very
1968 // uncommon to assign to multiple vars anyway
1969 this.emitLine('frame.set("' + name + '", ' + id + ', true);');
1970
1971 this.emitLine('if(frame.topLevel) {');
1972 this.emitLine('context.setVariable("' + name + '", ' + id + ');');
1973 this.emitLine('}');
1974
1975 if(name.charAt(0) !== '_') {
1976 this.emitLine('if(frame.topLevel) {');
1977 this.emitLine('context.addExport("' + name + '", ' + id + ');');
1978 this.emitLine('}');
1979 }
1980 }, this);
1981 },
1982
1983 compileIf: function(node, frame, async) {
1984 this.emit('if(');
1985 this._compileExpression(node.cond, frame);
1986 this.emitLine(') {');
1987
1988 this.withScopedSyntax(function() {
1989 this.compile(node.body, frame);
1990
1991 if(async) {
1992 this.emit('cb()');
1993 }
1994 });
1995
1996 if(node.else_) {
1997 this.emitLine('}\nelse {');
1998
1999 this.withScopedSyntax(function() {
2000 this.compile(node.else_, frame);
2001
2002 if(async) {
2003 this.emit('cb()');
2004 }
2005 });
2006 } else if(async) {
2007 this.emitLine('}\nelse {');
2008 this.emit('cb()');
2009 }
2010
2011 this.emitLine('}');
2012 },
2013
2014 compileIfAsync: function(node, frame) {
2015 this.emit('(function(cb) {');
2016 this.compileIf(node, frame, true);
2017 this.emit('})(' + this.makeCallback());
2018 this.addScopeLevel();
2019 },
2020
2021 emitLoopBindings: function(node, arr, i, len) {
2022 var bindings = {
2023 index: i + ' + 1',
2024 index0: i,
2025 revindex: len + ' - ' + i,
2026 revindex0: len + ' - ' + i + ' - 1',
2027 first: i + ' === 0',
2028 last: i + ' === ' + len + ' - 1',
2029 length: len
2030 };
2031
2032 for (var name in bindings) {
2033 this.emitLine('frame.set("loop.' + name + '", ' + bindings[name] + ');');
2034 }
2035 },
2036
2037 compileFor: function(node, frame) {
2038 // Some of this code is ugly, but it keeps the generated code
2039 // as fast as possible. ForAsync also shares some of this, but
2040 // not much.
2041
2042 var v;
2043 var i = this.tmpid();
2044 var len = this.tmpid();
2045 var arr = this.tmpid();
2046 frame = frame.push();
2047
2048 this.emitLine('frame = frame.push();');
2049
2050 this.emit('var ' + arr + ' = ');
2051 this._compileExpression(node.arr, frame);
2052 this.emitLine(';');
2053
2054 this.emit('if(' + arr + ') {');
2055
2056 // If multiple names are passed, we need to bind them
2057 // appropriately
2058 if(node.name instanceof nodes.Array) {
2059 this.emitLine('var ' + i + ';');
2060
2061 // The object could be an arroy or object. Note that the
2062 // body of the loop is duplicated for each condition, but
2063 // we are optimizing for speed over size.
2064 this.emitLine('if(runtime.isArray(' + arr + ')) {'); {
2065 this.emitLine('var ' + len + ' = ' + arr + '.length;');
2066 this.emitLine('for(' + i + '=0; ' + i + ' < ' + arr + '.length; '
2067 + i + '++) {');
2068
2069 // Bind each declared var
2070 for (var u=0; u < node.name.children.length; u++) {
2071 var tid = this.tmpid();
2072 this.emitLine('var ' + tid + ' = ' + arr + '[' + i + '][' + u + ']');
2073 this.emitLine('frame.set("' + node.name.children[u].value
2074 + '", ' + arr + '[' + i + '][' + u + ']' + ');');
2075 frame.set(node.name.children[u].value, tid);
2076 }
2077
2078 this.emitLoopBindings(node, arr, i, len);
2079 this.withScopedSyntax(function() {
2080 this.compile(node.body, frame);
2081 });
2082 this.emitLine('}');
2083 }
2084
2085 this.emitLine('} else {'); {
2086 // Iterate over the key/values of an object
2087 var key = node.name.children[0];
2088 var val = node.name.children[1];
2089 var k = this.tmpid();
2090 v = this.tmpid();
2091 frame.set(key.value, k);
2092 frame.set(val.value, v);
2093
2094 this.emitLine(i + ' = -1;');
2095 this.emitLine('var ' + len + ' = runtime.keys(' + arr + ').length;');
2096 this.emitLine('for(var ' + k + ' in ' + arr + ') {');
2097 this.emitLine(i + '++;');
2098 this.emitLine('var ' + v + ' = ' + arr + '[' + k + '];');
2099 this.emitLine('frame.set("' + key.value + '", ' + k + ');');
2100 this.emitLine('frame.set("' + val.value + '", ' + v + ');');
2101
2102 this.emitLoopBindings(node, arr, i, len);
2103 this.withScopedSyntax(function() {
2104 this.compile(node.body, frame);
2105 });
2106 this.emitLine('}');
2107 }
2108
2109 this.emitLine('}');
2110 }
2111 else {
2112 // Generate a typical array iteration
2113 v = this.tmpid();
2114 frame.set(node.name.value, v);
2115
2116 this.emitLine('var ' + len + ' = ' + arr + '.length;');
2117 this.emitLine('for(var ' + i + '=0; ' + i + ' < ' + arr + '.length; ' +
2118 i + '++) {');
2119 this.emitLine('var ' + v + ' = ' + arr + '[' + i + '];');
2120 this.emitLine('frame.set("' + node.name.value + '", ' + v + ');');
2121
2122 this.emitLoopBindings(node, arr, i, len);
2123
2124 this.withScopedSyntax(function() {
2125 this.compile(node.body, frame);
2126 });
2127
2128 this.emitLine('}');
2129 }
2130
2131 this.emitLine('}');
2132 if (node.else_) {
2133 this.emitLine('if (!' + len + ') {');
2134 this.compile(node.else_, frame);
2135 this.emitLine('}');
2136 }
2137
2138 this.emitLine('frame = frame.pop();');
2139 },
2140
2141 _compileAsyncLoop: function(node, frame, parallel) {
2142 // This shares some code with the For tag, but not enough to
2143 // worry about. This iterates across an object asynchronously,
2144 // but not in parallel.
2145
2146 var i = this.tmpid();
2147 var len = this.tmpid();
2148 var arr = this.tmpid();
2149 var asyncMethod = parallel ? 'asyncAll' : 'asyncEach';
2150 frame = frame.push();
2151
2152 this.emitLine('frame = frame.push();');
2153
2154 this.emit('var ' + arr + ' = ');
2155 this._compileExpression(node.arr, frame);
2156 this.emitLine(';');
2157
2158 if(node.name instanceof nodes.Array) {
2159 this.emit('runtime.' + asyncMethod + '(' + arr + ', ' +
2160 node.name.children.length + ', function(');
2161
2162 lib.each(node.name.children, function(name) {
2163 this.emit(name.value + ',');
2164 }, this);
2165
2166 this.emit(i + ',' + len + ',next) {');
2167
2168 lib.each(node.name.children, function(name) {
2169 var id = name.value;
2170 frame.set(id, id);
2171 this.emitLine('frame.set("' + id + '", ' + id + ');');
2172 }, this);
2173 }
2174 else {
2175 var id = node.name.value;
2176 this.emitLine('runtime.' + asyncMethod + '(' + arr + ', 1, function(' + id + ', ' + i + ', ' + len + ',next) {');
2177 this.emitLine('frame.set("' + id + '", ' + id + ');');
2178 frame.set(id, id);
2179 }
2180
2181 this.emitLoopBindings(node, arr, i, len);
2182
2183 this.withScopedSyntax(function() {
2184 var buf;
2185 if(parallel) {
2186 buf = this.tmpid();
2187 this.pushBufferId(buf);
2188 }
2189
2190 this.compile(node.body, frame);
2191 this.emitLine('next(' + i + (buf ? ',' + buf : '') + ');');
2192
2193 if(parallel) {
2194 this.popBufferId();
2195 }
2196 });
2197
2198 var output = this.tmpid();
2199 this.emitLine('}, ' + this.makeCallback(output));
2200 this.addScopeLevel();
2201
2202 if(parallel) {
2203 this.emitLine(this.buffer + ' += ' + output + ';');
2204 }
2205
2206 if (node.else_) {
2207 this.emitLine('if (!' + arr + '.length) {');
2208 this.compile(node.else_, frame);
2209 this.emitLine('}');
2210 }
2211
2212 this.emitLine('frame = frame.pop();');
2213 },
2214
2215 compileAsyncEach: function(node, frame) {
2216 this._compileAsyncLoop(node, frame);
2217 },
2218
2219 compileAsyncAll: function(node, frame) {
2220 this._compileAsyncLoop(node, frame, true);
2221 },
2222
2223 _compileMacro: function(node, frame) {
2224 var args = [];
2225 var kwargs = null;
2226 var funcId = 'macro_' + this.tmpid();
2227
2228 // Type check the definition of the args
2229 lib.each(node.args.children, function(arg, i) {
2230 if(i === node.args.children.length - 1 &&
2231 arg instanceof nodes.Dict) {
2232 kwargs = arg;
2233 }
2234 else {
2235 this.assertType(arg, nodes.Symbol);
2236 args.push(arg);
2237 }
2238 }, this);
2239
2240 var realNames = lib.map(args, function(n) { return 'l_' + n.value; });
2241 realNames.push('kwargs');
2242
2243 // Quoted argument names
2244 var argNames = lib.map(args, function(n) { return '"' + n.value + '"'; });
2245 var kwargNames = lib.map((kwargs && kwargs.children) || [],
2246 function(n) { return '"' + n.key.value + '"'; });
2247
2248 // We pass a function to makeMacro which destructures the
2249 // arguments so support setting positional args with keywords
2250 // args and passing keyword args as positional args
2251 // (essentially default values). See runtime.js.
2252 frame = frame.push();
2253 this.emitLines(
2254 'var ' + funcId + ' = runtime.makeMacro(',
2255 '[' + argNames.join(', ') + '], ',
2256 '[' + kwargNames.join(', ') + '], ',
2257 'function (' + realNames.join(', ') + ') {',
2258 'frame = frame.push(true);',
2259 'kwargs = kwargs || {};',
2260 'if (kwargs.hasOwnProperty("caller")) {',
2261 'frame.set("caller", kwargs.caller); }'
2262 );
2263
2264 // Expose the arguments to the template. Don't need to use
2265 // random names because the function
2266 // will create a new run-time scope for us
2267 lib.each(args, function(arg) {
2268 this.emitLine('frame.set("' + arg.value + '", ' +
2269 'l_' + arg.value + ');');
2270 frame.set(arg.value, 'l_' + arg.value);
2271 }, this);
2272
2273 // Expose the keyword arguments
2274 if(kwargs) {
2275 lib.each(kwargs.children, function(pair) {
2276 var name = pair.key.value;
2277 this.emit('frame.set("' + name + '", ' +
2278 'kwargs.hasOwnProperty("' + name + '") ? ' +
2279 'kwargs["' + name + '"] : ');
2280 this._compileExpression(pair.value, frame);
2281 this.emitLine(');');
2282 }, this);
2283 }
2284
2285 var bufferId = this.tmpid();
2286 this.pushBufferId(bufferId);
2287
2288 this.withScopedSyntax(function () {
2289 this.compile(node.body, frame);
2290 });
2291
2292 frame = frame.pop();
2293 this.emitLine('frame = frame.pop();');
2294 this.emitLine('return new runtime.SafeString(' + bufferId + ');');
2295 this.emitLine('});');
2296 this.popBufferId();
2297
2298 return funcId;
2299 },
2300
2301 compileMacro: function(node, frame) {
2302 var funcId = this._compileMacro(node, frame);
2303
2304 // Expose the macro to the templates
2305 var name = node.name.value;
2306 frame.set(name, funcId);
2307
2308 if(frame.parent) {
2309 this.emitLine('frame.set("' + name + '", ' + funcId + ');');
2310 }
2311 else {
2312 if(node.name.value.charAt(0) !== '_') {
2313 this.emitLine('context.addExport("' + name + '");');
2314 }
2315 this.emitLine('context.setVariable("' + name + '", ' + funcId + ');');
2316 }
2317 },
2318
2319 compileCaller: function(node, frame) {
2320 // basically an anonymous "macro expression"
2321 this.emit('(function (){');
2322 var funcId = this._compileMacro(node, frame);
2323 this.emit('return ' + funcId + ';})()');
2324 },
2325
2326 compileImport: function(node, frame) {
2327 var id = this.tmpid();
2328 var target = node.target.value;
2329
2330 this.emit('env.getTemplate(');
2331 this._compileExpression(node.template, frame);
2332 this.emitLine(', false, '+this._templateName()+', false, ' + this.makeCallback(id));
2333 this.addScopeLevel();
2334
2335 this.emitLine(id + '.getExported(' +
2336 (node.withContext ? 'context.getVariables(), frame, ' : '') +
2337 this.makeCallback(id));
2338 this.addScopeLevel();
2339
2340 frame.set(target, id);
2341
2342 if(frame.parent) {
2343 this.emitLine('frame.set("' + target + '", ' + id + ');');
2344 }
2345 else {
2346 this.emitLine('context.setVariable("' + target + '", ' + id + ');');
2347 }
2348 },
2349
2350 compileFromImport: function(node, frame) {
2351 var importedId = this.tmpid();
2352
2353 this.emit('env.getTemplate(');
2354 this._compileExpression(node.template, frame);
2355 this.emitLine(', false, '+this._templateName()+', false, ' + this.makeCallback(importedId));
2356 this.addScopeLevel();
2357
2358 this.emitLine(importedId + '.getExported(' +
2359 (node.withContext ? 'context.getVariables(), frame, ' : '') +
2360 this.makeCallback(importedId));
2361 this.addScopeLevel();
2362
2363 lib.each(node.names.children, function(nameNode) {
2364 var name;
2365 var alias;
2366 var id = this.tmpid();
2367
2368 if(nameNode instanceof nodes.Pair) {
2369 name = nameNode.key.value;
2370 alias = nameNode.value.value;
2371 }
2372 else {
2373 name = nameNode.value;
2374 alias = name;
2375 }
2376
2377 this.emitLine('if(' + importedId + '.hasOwnProperty("' + name + '")) {');
2378 this.emitLine('var ' + id + ' = ' + importedId + '.' + name + ';');
2379 this.emitLine('} else {');
2380 this.emitLine('cb(new Error("cannot import \'' + name + '\'")); return;');
2381 this.emitLine('}');
2382
2383 frame.set(alias, id);
2384
2385 if(frame.parent) {
2386 this.emitLine('frame.set("' + alias + '", ' + id + ');');
2387 }
2388 else {
2389 this.emitLine('context.setVariable("' + alias + '", ' + id + ');');
2390 }
2391 }, this);
2392 },
2393
2394 compileBlock: function(node) {
2395 var id = this.tmpid();
2396
2397 // If we are executing outside a block (creating a top-level
2398 // block), we really don't want to execute its code because it
2399 // will execute twice: once when the child template runs and
2400 // again when the parent template runs. Note that blocks
2401 // within blocks will *always* execute immediately *and*
2402 // wherever else they are invoked (like used in a parent
2403 // template). This may have behavioral differences from jinja
2404 // because blocks can have side effects, but it seems like a
2405 // waste of performance to always execute huge top-level
2406 // blocks twice
2407 if(!this.inBlock) {
2408 this.emit('(parentTemplate ? function(e, c, f, r, cb) { cb(""); } : ');
2409 }
2410 this.emit('context.getBlock("' + node.name.value + '")');
2411 if(!this.inBlock) {
2412 this.emit(')');
2413 }
2414 this.emitLine('(env, context, frame, runtime, ' + this.makeCallback(id));
2415 this.emitLine(this.buffer + ' += ' + id + ';');
2416 this.addScopeLevel();
2417 },
2418
2419 compileSuper: function(node, frame) {
2420 var name = node.blockName.value;
2421 var id = node.symbol.value;
2422
2423 this.emitLine('context.getSuper(env, ' +
2424 '"' + name + '", ' +
2425 'b_' + name + ', ' +
2426 'frame, runtime, '+
2427 this.makeCallback(id));
2428 this.emitLine(id + ' = runtime.markSafe(' + id + ');');
2429 this.addScopeLevel();
2430 frame.set(id, id);
2431 },
2432
2433 compileExtends: function(node, frame) {
2434 var k = this.tmpid();
2435
2436 this.emit('env.getTemplate(');
2437 this._compileExpression(node.template, frame);
2438 this.emitLine(', true, '+this._templateName()+', false, ' + this.makeCallback('_parentTemplate'));
2439
2440 // extends is a dynamic tag and can occur within a block like
2441 // `if`, so if this happens we need to capture the parent
2442 // template in the top-level scope
2443 this.emitLine('parentTemplate = _parentTemplate');
2444
2445 this.emitLine('for(var ' + k + ' in parentTemplate.blocks) {');
2446 this.emitLine('context.addBlock(' + k +
2447 ', parentTemplate.blocks[' + k + ']);');
2448 this.emitLine('}');
2449
2450 this.addScopeLevel();
2451 },
2452
2453 compileInclude: function(node, frame) {
2454 var id = this.tmpid();
2455 var id2 = this.tmpid();
2456
2457 this.emit('env.getTemplate(');
2458 this._compileExpression(node.template, frame);
2459 this.emitLine(', false, '+this._templateName()+', ' + node.ignoreMissing + ', ' + this.makeCallback(id));
2460 this.addScopeLevel();
2461
2462 this.emitLine(id + '.render(' +
2463 'context.getVariables(), frame, ' + this.makeCallback(id2));
2464 this.emitLine(this.buffer + ' += ' + id2);
2465 this.addScopeLevel();
2466 },
2467
2468 compileTemplateData: function(node, frame) {
2469 this.compileLiteral(node, frame);
2470 },
2471
2472 compileCapture: function(node, frame) {
2473 this.emitLine('(function() {');
2474 this.emitLine('var output = "";');
2475 this.withScopedSyntax(function () {
2476 this.compile(node.body, frame);
2477 });
2478 this.emitLine('return output;');
2479 this.emitLine('})()');
2480 },
2481
2482 compileOutput: function(node, frame) {
2483 var children = node.children;
2484 for(var i=0, l=children.length; i<l; i++) {
2485 // TemplateData is a special case because it is never
2486 // autoescaped, so simply output it for optimization
2487 if(children[i] instanceof nodes.TemplateData) {
2488 if(children[i].value) {
2489 this.emit(this.buffer + ' += ');
2490 this.compileLiteral(children[i], frame);
2491 this.emitLine(';');
2492 }
2493 }
2494 else {
2495 this.emit(this.buffer + ' += runtime.suppressValue(');
2496 if(this.throwOnUndefined) {
2497 this.emit('runtime.ensureDefined(');
2498 }
2499 this.compile(children[i], frame);
2500 if(this.throwOnUndefined) {
2501 this.emit(',' + node.lineno + ',' + node.colno + ')');
2502 }
2503 this.emit(', env.opts.autoescape);\n');
2504 }
2505 }
2506 },
2507
2508 compileRoot: function(node, frame) {
2509 if(frame) {
2510 this.fail('compileRoot: root node can\'t have frame');
2511 }
2512
2513 frame = new Frame();
2514
2515 this.emitFuncBegin('root');
2516 this.emitLine('var parentTemplate = null;');
2517 this._compileChildren(node, frame);
2518 this.emitLine('if(parentTemplate) {');
2519 this.emitLine('parentTemplate.rootRenderFunc(env, context, frame, runtime, cb);');
2520 this.emitLine('} else {');
2521 this.emitLine('cb(null, ' + this.buffer +');');
2522 this.emitLine('}');
2523 this.emitFuncEnd(true);
2524
2525 this.inBlock = true;
2526
2527 var blockNames = [];
2528
2529 var i, name, block, blocks = node.findAll(nodes.Block);
2530 for (i = 0; i < blocks.length; i++) {
2531 block = blocks[i];
2532 name = block.name.value;
2533
2534 if (blockNames.indexOf(name) !== -1) {
2535 throw new Error('Block "' + name + '" defined more than once.');
2536 }
2537 blockNames.push(name);
2538
2539 this.emitFuncBegin('b_' + name);
2540
2541 var tmpFrame = new Frame();
2542 this.compile(block.body, tmpFrame);
2543 this.emitFuncEnd();
2544 }
2545
2546 this.emitLine('return {');
2547 for (i = 0; i < blocks.length; i++) {
2548 block = blocks[i];
2549 name = 'b_' + block.name.value;
2550 this.emitLine(name + ': ' + name + ',');
2551 }
2552 this.emitLine('root: root\n};');
2553 },
2554
2555 compile: function (node, frame) {
2556 var _compile = this['compile' + node.typename];
2557 if(_compile) {
2558 _compile.call(this, node, frame);
2559 }
2560 else {
2561 this.fail('compile: Cannot compile node: ' + node.typename,
2562 node.lineno,
2563 node.colno);
2564 }
2565 },
2566
2567 getCode: function() {
2568 return this.codebuf.join('');
2569 }
2570 });
2571
2572 // var c = new Compiler();
2573 // var src = 'hello {% filter title %}' +
2574 // 'Hello madam how are you' +
2575 // '{% endfilter %}'
2576 // var ast = transformer.transform(parser.parse(src));
2577 // nodes.printNodes(ast);
2578 // c.compile(ast);
2579 // var tmpl = c.getCode();
2580 // console.log(tmpl);
2581
2582 module.exports = {
2583 compile: function(src, asyncFilters, extensions, name, opts) {
2584 var c = new Compiler(name, opts.throwOnUndefined);
2585
2586 // Run the extension preprocessors against the source.
2587 if(extensions && extensions.length) {
2588 for(var i=0; i<extensions.length; i++) {
2589 if('preprocess' in extensions[i]) {
2590 src = extensions[i].preprocess(src, name);
2591 }
2592 }
2593 }
2594
2595 c.compile(transformer.transform(
2596 parser.parse(src,
2597 extensions,
2598 opts),
2599 asyncFilters,
2600 name
2601 ));
2602 return c.getCode();
2603 },
2604
2605 Compiler: Compiler
2606 };
2607
2608
2609/***/ },
2610/* 8 */
2611/***/ function(module, exports, __webpack_require__) {
2612
2613 'use strict';
2614
2615 var lexer = __webpack_require__(9);
2616 var nodes = __webpack_require__(10);
2617 // jshint -W079
2618 var Object = __webpack_require__(6);
2619 var lib = __webpack_require__(1);
2620
2621 var Parser = Object.extend({
2622 init: function (tokens) {
2623 this.tokens = tokens;
2624 this.peeked = null;
2625 this.breakOnBlocks = null;
2626 this.dropLeadingWhitespace = false;
2627
2628 this.extensions = [];
2629 },
2630
2631 nextToken: function (withWhitespace) {
2632 var tok;
2633
2634 if(this.peeked) {
2635 if(!withWhitespace && this.peeked.type === lexer.TOKEN_WHITESPACE) {
2636 this.peeked = null;
2637 }
2638 else {
2639 tok = this.peeked;
2640 this.peeked = null;
2641 return tok;
2642 }
2643 }
2644
2645 tok = this.tokens.nextToken();
2646
2647 if(!withWhitespace) {
2648 while(tok && tok.type === lexer.TOKEN_WHITESPACE) {
2649 tok = this.tokens.nextToken();
2650 }
2651 }
2652
2653 return tok;
2654 },
2655
2656 peekToken: function () {
2657 this.peeked = this.peeked || this.nextToken();
2658 return this.peeked;
2659 },
2660
2661 pushToken: function(tok) {
2662 if(this.peeked) {
2663 throw new Error('pushToken: can only push one token on between reads');
2664 }
2665 this.peeked = tok;
2666 },
2667
2668 fail: function (msg, lineno, colno) {
2669 if((lineno === undefined || colno === undefined) && this.peekToken()) {
2670 var tok = this.peekToken();
2671 lineno = tok.lineno;
2672 colno = tok.colno;
2673 }
2674 if (lineno !== undefined) lineno += 1;
2675 if (colno !== undefined) colno += 1;
2676
2677 throw new lib.TemplateError(msg, lineno, colno);
2678 },
2679
2680 skip: function(type) {
2681 var tok = this.nextToken();
2682 if(!tok || tok.type !== type) {
2683 this.pushToken(tok);
2684 return false;
2685 }
2686 return true;
2687 },
2688
2689 expect: function(type) {
2690 var tok = this.nextToken();
2691 if(tok.type !== type) {
2692 this.fail('expected ' + type + ', got ' + tok.type,
2693 tok.lineno,
2694 tok.colno);
2695 }
2696 return tok;
2697 },
2698
2699 skipValue: function(type, val) {
2700 var tok = this.nextToken();
2701 if(!tok || tok.type !== type || tok.value !== val) {
2702 this.pushToken(tok);
2703 return false;
2704 }
2705 return true;
2706 },
2707
2708 skipSymbol: function(val) {
2709 return this.skipValue(lexer.TOKEN_SYMBOL, val);
2710 },
2711
2712 advanceAfterBlockEnd: function(name) {
2713 var tok;
2714 if(!name) {
2715 tok = this.peekToken();
2716
2717 if(!tok) {
2718 this.fail('unexpected end of file');
2719 }
2720
2721 if(tok.type !== lexer.TOKEN_SYMBOL) {
2722 this.fail('advanceAfterBlockEnd: expected symbol token or ' +
2723 'explicit name to be passed');
2724 }
2725
2726 name = this.nextToken().value;
2727 }
2728
2729 tok = this.nextToken();
2730
2731 if(tok && tok.type === lexer.TOKEN_BLOCK_END) {
2732 if(tok.value.charAt(0) === '-') {
2733 this.dropLeadingWhitespace = true;
2734 }
2735 }
2736 else {
2737 this.fail('expected block end in ' + name + ' statement');
2738 }
2739
2740 return tok;
2741 },
2742
2743 advanceAfterVariableEnd: function() {
2744 if(!this.skip(lexer.TOKEN_VARIABLE_END)) {
2745 this.fail('expected variable end');
2746 }
2747 },
2748
2749 parseFor: function() {
2750 var forTok = this.peekToken();
2751 var node;
2752 var endBlock;
2753
2754 if(this.skipSymbol('for')) {
2755 node = new nodes.For(forTok.lineno, forTok.colno);
2756 endBlock = 'endfor';
2757 }
2758 else if(this.skipSymbol('asyncEach')) {
2759 node = new nodes.AsyncEach(forTok.lineno, forTok.colno);
2760 endBlock = 'endeach';
2761 }
2762 else if(this.skipSymbol('asyncAll')) {
2763 node = new nodes.AsyncAll(forTok.lineno, forTok.colno);
2764 endBlock = 'endall';
2765 }
2766 else {
2767 this.fail('parseFor: expected for{Async}', forTok.lineno, forTok.colno);
2768 }
2769
2770 node.name = this.parsePrimary();
2771
2772 if(!(node.name instanceof nodes.Symbol)) {
2773 this.fail('parseFor: variable name expected for loop');
2774 }
2775
2776 var type = this.peekToken().type;
2777 if(type === lexer.TOKEN_COMMA) {
2778 // key/value iteration
2779 var key = node.name;
2780 node.name = new nodes.Array(key.lineno, key.colno);
2781 node.name.addChild(key);
2782
2783 while(this.skip(lexer.TOKEN_COMMA)) {
2784 var prim = this.parsePrimary();
2785 node.name.addChild(prim);
2786 }
2787 }
2788
2789 if(!this.skipSymbol('in')) {
2790 this.fail('parseFor: expected "in" keyword for loop',
2791 forTok.lineno,
2792 forTok.colno);
2793 }
2794
2795 node.arr = this.parseExpression();
2796 this.advanceAfterBlockEnd(forTok.value);
2797
2798 node.body = this.parseUntilBlocks(endBlock, 'else');
2799
2800 if(this.skipSymbol('else')) {
2801 this.advanceAfterBlockEnd('else');
2802 node.else_ = this.parseUntilBlocks(endBlock);
2803 }
2804
2805 this.advanceAfterBlockEnd();
2806
2807 return node;
2808 },
2809
2810 parseMacro: function() {
2811 var macroTok = this.peekToken();
2812 if(!this.skipSymbol('macro')) {
2813 this.fail('expected macro');
2814 }
2815
2816 var name = this.parsePrimary(true);
2817 var args = this.parseSignature();
2818 var node = new nodes.Macro(macroTok.lineno,
2819 macroTok.colno,
2820 name,
2821 args);
2822
2823 this.advanceAfterBlockEnd(macroTok.value);
2824 node.body = this.parseUntilBlocks('endmacro');
2825 this.advanceAfterBlockEnd();
2826
2827 return node;
2828 },
2829
2830 parseCall: function() {
2831 // a call block is parsed as a normal FunCall, but with an added
2832 // 'caller' kwarg which is a Caller node.
2833 var callTok = this.peekToken();
2834 if(!this.skipSymbol('call')) {
2835 this.fail('expected call');
2836 }
2837
2838 var callerArgs = this.parseSignature(true) || new nodes.NodeList();
2839 var macroCall = this.parsePrimary();
2840
2841 this.advanceAfterBlockEnd(callTok.value);
2842 var body = this.parseUntilBlocks('endcall');
2843 this.advanceAfterBlockEnd();
2844
2845 var callerName = new nodes.Symbol(callTok.lineno,
2846 callTok.colno,
2847 'caller');
2848 var callerNode = new nodes.Caller(callTok.lineno,
2849 callTok.colno,
2850 callerName,
2851 callerArgs,
2852 body);
2853
2854 // add the additional caller kwarg, adding kwargs if necessary
2855 var args = macroCall.args.children;
2856 if (!(args[args.length-1] instanceof nodes.KeywordArgs)) {
2857 args.push(new nodes.KeywordArgs());
2858 }
2859 var kwargs = args[args.length - 1];
2860 kwargs.addChild(new nodes.Pair(callTok.lineno,
2861 callTok.colno,
2862 callerName,
2863 callerNode));
2864
2865 return new nodes.Output(callTok.lineno,
2866 callTok.colno,
2867 [macroCall]);
2868 },
2869
2870 parseWithContext: function() {
2871 var tok = this.peekToken();
2872
2873 var withContext = null;
2874
2875 if(this.skipSymbol('with')) {
2876 withContext = true;
2877 }
2878 else if(this.skipSymbol('without')) {
2879 withContext = false;
2880 }
2881
2882 if(withContext !== null) {
2883 if(!this.skipSymbol('context')) {
2884 this.fail('parseFrom: expected context after with/without',
2885 tok.lineno,
2886 tok.colno);
2887 }
2888 }
2889
2890 return withContext;
2891 },
2892
2893 parseImport: function() {
2894 var importTok = this.peekToken();
2895 if(!this.skipSymbol('import')) {
2896 this.fail('parseImport: expected import',
2897 importTok.lineno,
2898 importTok.colno);
2899 }
2900
2901 var template = this.parseExpression();
2902
2903 if(!this.skipSymbol('as')) {
2904 this.fail('parseImport: expected "as" keyword',
2905 importTok.lineno,
2906 importTok.colno);
2907 }
2908
2909 var target = this.parseExpression();
2910
2911 var withContext = this.parseWithContext();
2912
2913 var node = new nodes.Import(importTok.lineno,
2914 importTok.colno,
2915 template,
2916 target,
2917 withContext);
2918
2919 this.advanceAfterBlockEnd(importTok.value);
2920
2921 return node;
2922 },
2923
2924 parseFrom: function() {
2925 var fromTok = this.peekToken();
2926 if(!this.skipSymbol('from')) {
2927 this.fail('parseFrom: expected from');
2928 }
2929
2930 var template = this.parseExpression();
2931
2932 if(!this.skipSymbol('import')) {
2933 this.fail('parseFrom: expected import',
2934 fromTok.lineno,
2935 fromTok.colno);
2936 }
2937
2938 var names = new nodes.NodeList(),
2939 withContext;
2940
2941 while(1) {
2942 var nextTok = this.peekToken();
2943 if(nextTok.type === lexer.TOKEN_BLOCK_END) {
2944 if(!names.children.length) {
2945 this.fail('parseFrom: Expected at least one import name',
2946 fromTok.lineno,
2947 fromTok.colno);
2948 }
2949
2950 // Since we are manually advancing past the block end,
2951 // need to keep track of whitespace control (normally
2952 // this is done in `advanceAfterBlockEnd`
2953 if(nextTok.value.charAt(0) === '-') {
2954 this.dropLeadingWhitespace = true;
2955 }
2956
2957 this.nextToken();
2958 break;
2959 }
2960
2961 if(names.children.length > 0 && !this.skip(lexer.TOKEN_COMMA)) {
2962 this.fail('parseFrom: expected comma',
2963 fromTok.lineno,
2964 fromTok.colno);
2965 }
2966
2967 var name = this.parsePrimary();
2968 if(name.value.charAt(0) === '_') {
2969 this.fail('parseFrom: names starting with an underscore ' +
2970 'cannot be imported',
2971 name.lineno,
2972 name.colno);
2973 }
2974
2975 if(this.skipSymbol('as')) {
2976 var alias = this.parsePrimary();
2977 names.addChild(new nodes.Pair(name.lineno,
2978 name.colno,
2979 name,
2980 alias));
2981 }
2982 else {
2983 names.addChild(name);
2984 }
2985
2986 withContext = this.parseWithContext();
2987 }
2988
2989 return new nodes.FromImport(fromTok.lineno,
2990 fromTok.colno,
2991 template,
2992 names,
2993 withContext);
2994 },
2995
2996 parseBlock: function() {
2997 var tag = this.peekToken();
2998 if(!this.skipSymbol('block')) {
2999 this.fail('parseBlock: expected block', tag.lineno, tag.colno);
3000 }
3001
3002 var node = new nodes.Block(tag.lineno, tag.colno);
3003
3004 node.name = this.parsePrimary();
3005 if(!(node.name instanceof nodes.Symbol)) {
3006 this.fail('parseBlock: variable name expected',
3007 tag.lineno,
3008 tag.colno);
3009 }
3010
3011 this.advanceAfterBlockEnd(tag.value);
3012
3013 node.body = this.parseUntilBlocks('endblock');
3014 this.skipSymbol('endblock');
3015 this.skipSymbol(node.name.value);
3016
3017 var tok = this.peekToken();
3018 if(!tok) {
3019 this.fail('parseBlock: expected endblock, got end of file');
3020 }
3021
3022 this.advanceAfterBlockEnd(tok.value);
3023
3024 return node;
3025 },
3026
3027 parseExtends: function() {
3028 var tagName = 'extends';
3029 var tag = this.peekToken();
3030 if(!this.skipSymbol(tagName)) {
3031 this.fail('parseTemplateRef: expected '+ tagName);
3032 }
3033
3034 var node = new nodes.Extends(tag.lineno, tag.colno);
3035 node.template = this.parseExpression();
3036
3037 this.advanceAfterBlockEnd(tag.value);
3038 return node;
3039 },
3040
3041 parseInclude: function() {
3042 var tagName = 'include';
3043 var tag = this.peekToken();
3044 if(!this.skipSymbol(tagName)) {
3045 this.fail('parseInclude: expected '+ tagName);
3046 }
3047
3048 var node = new nodes.Include(tag.lineno, tag.colno);
3049 node.template = this.parseExpression();
3050
3051 if(this.skipSymbol('ignore') && this.skipSymbol('missing')) {
3052 node.ignoreMissing = true;
3053 }
3054
3055 this.advanceAfterBlockEnd(tag.value);
3056 return node;
3057 },
3058
3059 parseIf: function() {
3060 var tag = this.peekToken();
3061 var node;
3062
3063 if(this.skipSymbol('if') || this.skipSymbol('elif') || this.skipSymbol('elseif')) {
3064 node = new nodes.If(tag.lineno, tag.colno);
3065 }
3066 else if(this.skipSymbol('ifAsync')) {
3067 node = new nodes.IfAsync(tag.lineno, tag.colno);
3068 }
3069 else {
3070 this.fail('parseIf: expected if, elif, or elseif',
3071 tag.lineno,
3072 tag.colno);
3073 }
3074
3075 node.cond = this.parseExpression();
3076 this.advanceAfterBlockEnd(tag.value);
3077
3078 node.body = this.parseUntilBlocks('elif', 'elseif', 'else', 'endif');
3079 var tok = this.peekToken();
3080
3081 switch(tok && tok.value) {
3082 case 'elseif':
3083 case 'elif':
3084 node.else_ = this.parseIf();
3085 break;
3086 case 'else':
3087 this.advanceAfterBlockEnd();
3088 node.else_ = this.parseUntilBlocks('endif');
3089 this.advanceAfterBlockEnd();
3090 break;
3091 case 'endif':
3092 node.else_ = null;
3093 this.advanceAfterBlockEnd();
3094 break;
3095 default:
3096 this.fail('parseIf: expected elif, else, or endif, ' +
3097 'got end of file');
3098 }
3099
3100 return node;
3101 },
3102
3103 parseSet: function() {
3104 var tag = this.peekToken();
3105 if(!this.skipSymbol('set')) {
3106 this.fail('parseSet: expected set', tag.lineno, tag.colno);
3107 }
3108
3109 var node = new nodes.Set(tag.lineno, tag.colno, []);
3110
3111 var target;
3112 while((target = this.parsePrimary())) {
3113 node.targets.push(target);
3114
3115 if(!this.skip(lexer.TOKEN_COMMA)) {
3116 break;
3117 }
3118 }
3119
3120 if(!this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
3121 if (!this.skip(lexer.TOKEN_BLOCK_END)) {
3122 this.fail('parseSet: expected = or block end in set tag',
3123 tag.lineno,
3124 tag.colno);
3125 }
3126 else {
3127 node.body = new nodes.Capture(
3128 tag.lineno,
3129 tag.colno,
3130 this.parseUntilBlocks('endset')
3131 );
3132 node.value = null;
3133 this.advanceAfterBlockEnd();
3134 }
3135 }
3136 else {
3137 node.value = this.parseExpression();
3138 this.advanceAfterBlockEnd(tag.value);
3139 }
3140
3141 return node;
3142 },
3143
3144 parseStatement: function () {
3145 var tok = this.peekToken();
3146 var node;
3147
3148 if(tok.type !== lexer.TOKEN_SYMBOL) {
3149 this.fail('tag name expected', tok.lineno, tok.colno);
3150 }
3151
3152 if(this.breakOnBlocks &&
3153 lib.indexOf(this.breakOnBlocks, tok.value) !== -1) {
3154 return null;
3155 }
3156
3157 switch(tok.value) {
3158 case 'raw': return this.parseRaw();
3159 case 'if':
3160 case 'ifAsync':
3161 return this.parseIf();
3162 case 'for':
3163 case 'asyncEach':
3164 case 'asyncAll':
3165 return this.parseFor();
3166 case 'block': return this.parseBlock();
3167 case 'extends': return this.parseExtends();
3168 case 'include': return this.parseInclude();
3169 case 'set': return this.parseSet();
3170 case 'macro': return this.parseMacro();
3171 case 'call': return this.parseCall();
3172 case 'import': return this.parseImport();
3173 case 'from': return this.parseFrom();
3174 case 'filter': return this.parseFilterStatement();
3175 default:
3176 if (this.extensions.length) {
3177 for (var i = 0; i < this.extensions.length; i++) {
3178 var ext = this.extensions[i];
3179 if (lib.indexOf(ext.tags || [], tok.value) !== -1) {
3180 return ext.parse(this, nodes, lexer);
3181 }
3182 }
3183 }
3184 this.fail('unknown block tag: ' + tok.value, tok.lineno, tok.colno);
3185 }
3186
3187 return node;
3188 },
3189
3190 parseRaw: function() {
3191 // Look for upcoming raw blocks (ignore all other kinds of blocks)
3192 var rawBlockRegex = /([\s\S]*?){%\s*(raw|endraw)\s*(?=%})%}/;
3193 var rawLevel = 1;
3194 var str = '';
3195 var matches = null;
3196
3197 // Skip opening raw token
3198 // Keep this token to track line and column numbers
3199 var begun = this.advanceAfterBlockEnd();
3200
3201 // Exit when there's nothing to match
3202 // or when we've found the matching "endraw" block
3203 while((matches = this.tokens._extractRegex(rawBlockRegex)) && rawLevel > 0) {
3204 var all = matches[0];
3205 var pre = matches[1];
3206 var blockName = matches[2];
3207
3208 // Adjust rawlevel
3209 if(blockName === 'raw') {
3210 rawLevel += 1;
3211 } else if(blockName === 'endraw') {
3212 rawLevel -= 1;
3213 }
3214
3215 // Add to str
3216 if(rawLevel === 0) {
3217 // We want to exclude the last "endraw"
3218 str += pre;
3219 // Move tokenizer to beginning of endraw block
3220 this.tokens.backN(all.length - pre.length);
3221 } else {
3222 str += all;
3223 }
3224 }
3225
3226 return new nodes.Output(
3227 begun.lineno,
3228 begun.colno,
3229 [new nodes.TemplateData(begun.lineno, begun.colno, str)]
3230 );
3231 },
3232
3233 parsePostfix: function(node) {
3234 var lookup, tok = this.peekToken();
3235
3236 while(tok) {
3237 if(tok.type === lexer.TOKEN_LEFT_PAREN) {
3238 // Function call
3239 node = new nodes.FunCall(tok.lineno,
3240 tok.colno,
3241 node,
3242 this.parseSignature());
3243 }
3244 else if(tok.type === lexer.TOKEN_LEFT_BRACKET) {
3245 // Reference
3246 lookup = this.parseAggregate();
3247 if(lookup.children.length > 1) {
3248 this.fail('invalid index');
3249 }
3250
3251 node = new nodes.LookupVal(tok.lineno,
3252 tok.colno,
3253 node,
3254 lookup.children[0]);
3255 }
3256 else if(tok.type === lexer.TOKEN_OPERATOR && tok.value === '.') {
3257 // Reference
3258 this.nextToken();
3259 var val = this.nextToken();
3260
3261 if(val.type !== lexer.TOKEN_SYMBOL) {
3262 this.fail('expected name as lookup value, got ' + val.value,
3263 val.lineno,
3264 val.colno);
3265 }
3266
3267 // Make a literal string because it's not a variable
3268 // reference
3269 lookup = new nodes.Literal(val.lineno,
3270 val.colno,
3271 val.value);
3272
3273 node = new nodes.LookupVal(tok.lineno,
3274 tok.colno,
3275 node,
3276 lookup);
3277 }
3278 else {
3279 break;
3280 }
3281
3282 tok = this.peekToken();
3283 }
3284
3285 return node;
3286 },
3287
3288 parseExpression: function() {
3289 var node = this.parseInlineIf();
3290 return node;
3291 },
3292
3293 parseInlineIf: function() {
3294 var node = this.parseOr();
3295 if(this.skipSymbol('if')) {
3296 var cond_node = this.parseOr();
3297 var body_node = node;
3298 node = new nodes.InlineIf(node.lineno, node.colno);
3299 node.body = body_node;
3300 node.cond = cond_node;
3301 if(this.skipSymbol('else')) {
3302 node.else_ = this.parseOr();
3303 } else {
3304 node.else_ = null;
3305 }
3306 }
3307
3308 return node;
3309 },
3310
3311 parseOr: function() {
3312 var node = this.parseAnd();
3313 while(this.skipSymbol('or')) {
3314 var node2 = this.parseAnd();
3315 node = new nodes.Or(node.lineno,
3316 node.colno,
3317 node,
3318 node2);
3319 }
3320 return node;
3321 },
3322
3323 parseAnd: function() {
3324 var node = this.parseNot();
3325 while(this.skipSymbol('and')) {
3326 var node2 = this.parseNot();
3327 node = new nodes.And(node.lineno,
3328 node.colno,
3329 node,
3330 node2);
3331 }
3332 return node;
3333 },
3334
3335 parseNot: function() {
3336 var tok = this.peekToken();
3337 if(this.skipSymbol('not')) {
3338 return new nodes.Not(tok.lineno,
3339 tok.colno,
3340 this.parseNot());
3341 }
3342 return this.parseIn();
3343 },
3344
3345 parseIn: function() {
3346 var node = this.parseCompare();
3347 while(1) {
3348 // check if the next token is 'not'
3349 var tok = this.nextToken();
3350 if (!tok) { break; }
3351 var invert = tok.type === lexer.TOKEN_SYMBOL && tok.value === 'not';
3352 // if it wasn't 'not', put it back
3353 if (!invert) { this.pushToken(tok); }
3354 if (this.skipSymbol('in')) {
3355 var node2 = this.parseCompare();
3356 node = new nodes.In(node.lineno,
3357 node.colno,
3358 node,
3359 node2);
3360 if (invert) {
3361 node = new nodes.Not(node.lineno,
3362 node.colno,
3363 node);
3364 }
3365 }
3366 else {
3367 // if we'd found a 'not' but this wasn't an 'in', put back the 'not'
3368 if (invert) { this.pushToken(tok); }
3369 break;
3370 }
3371 }
3372 return node;
3373 },
3374
3375 parseCompare: function() {
3376 var compareOps = ['==', '===', '!=', '!==', '<', '>', '<=', '>='];
3377 var expr = this.parseConcat();
3378 var ops = [];
3379
3380 while(1) {
3381 var tok = this.nextToken();
3382
3383 if(!tok) {
3384 break;
3385 }
3386 else if(lib.indexOf(compareOps, tok.value) !== -1) {
3387 ops.push(new nodes.CompareOperand(tok.lineno,
3388 tok.colno,
3389 this.parseConcat(),
3390 tok.value));
3391 }
3392 else {
3393 this.pushToken(tok);
3394 break;
3395 }
3396 }
3397
3398 if(ops.length) {
3399 return new nodes.Compare(ops[0].lineno,
3400 ops[0].colno,
3401 expr,
3402 ops);
3403 }
3404 else {
3405 return expr;
3406 }
3407 },
3408
3409 // finds the '~' for string concatenation
3410 parseConcat: function(){
3411 var node = this.parseAdd();
3412 while(this.skipValue(lexer.TOKEN_TILDE, '~')) {
3413 var node2 = this.parseAdd();
3414 node = new nodes.Concat(node.lineno,
3415 node.colno,
3416 node,
3417 node2);
3418 }
3419 return node;
3420 },
3421
3422 parseAdd: function() {
3423 var node = this.parseSub();
3424 while(this.skipValue(lexer.TOKEN_OPERATOR, '+')) {
3425 var node2 = this.parseSub();
3426 node = new nodes.Add(node.lineno,
3427 node.colno,
3428 node,
3429 node2);
3430 }
3431 return node;
3432 },
3433
3434 parseSub: function() {
3435 var node = this.parseMul();
3436 while(this.skipValue(lexer.TOKEN_OPERATOR, '-')) {
3437 var node2 = this.parseMul();
3438 node = new nodes.Sub(node.lineno,
3439 node.colno,
3440 node,
3441 node2);
3442 }
3443 return node;
3444 },
3445
3446 parseMul: function() {
3447 var node = this.parseDiv();
3448 while(this.skipValue(lexer.TOKEN_OPERATOR, '*')) {
3449 var node2 = this.parseDiv();
3450 node = new nodes.Mul(node.lineno,
3451 node.colno,
3452 node,
3453 node2);
3454 }
3455 return node;
3456 },
3457
3458 parseDiv: function() {
3459 var node = this.parseFloorDiv();
3460 while(this.skipValue(lexer.TOKEN_OPERATOR, '/')) {
3461 var node2 = this.parseFloorDiv();
3462 node = new nodes.Div(node.lineno,
3463 node.colno,
3464 node,
3465 node2);
3466 }
3467 return node;
3468 },
3469
3470 parseFloorDiv: function() {
3471 var node = this.parseMod();
3472 while(this.skipValue(lexer.TOKEN_OPERATOR, '//')) {
3473 var node2 = this.parseMod();
3474 node = new nodes.FloorDiv(node.lineno,
3475 node.colno,
3476 node,
3477 node2);
3478 }
3479 return node;
3480 },
3481
3482 parseMod: function() {
3483 var node = this.parsePow();
3484 while(this.skipValue(lexer.TOKEN_OPERATOR, '%')) {
3485 var node2 = this.parsePow();
3486 node = new nodes.Mod(node.lineno,
3487 node.colno,
3488 node,
3489 node2);
3490 }
3491 return node;
3492 },
3493
3494 parsePow: function() {
3495 var node = this.parseUnary();
3496 while(this.skipValue(lexer.TOKEN_OPERATOR, '**')) {
3497 var node2 = this.parseUnary();
3498 node = new nodes.Pow(node.lineno,
3499 node.colno,
3500 node,
3501 node2);
3502 }
3503 return node;
3504 },
3505
3506 parseUnary: function(noFilters) {
3507 var tok = this.peekToken();
3508 var node;
3509
3510 if(this.skipValue(lexer.TOKEN_OPERATOR, '-')) {
3511 node = new nodes.Neg(tok.lineno,
3512 tok.colno,
3513 this.parseUnary(true));
3514 }
3515 else if(this.skipValue(lexer.TOKEN_OPERATOR, '+')) {
3516 node = new nodes.Pos(tok.lineno,
3517 tok.colno,
3518 this.parseUnary(true));
3519 }
3520 else {
3521 node = this.parsePrimary();
3522 }
3523
3524 if(!noFilters) {
3525 node = this.parseFilter(node);
3526 }
3527
3528 return node;
3529 },
3530
3531 parsePrimary: function (noPostfix) {
3532 var tok = this.nextToken();
3533 var val;
3534 var node = null;
3535
3536 if(!tok) {
3537 this.fail('expected expression, got end of file');
3538 }
3539 else if(tok.type === lexer.TOKEN_STRING) {
3540 val = tok.value;
3541 }
3542 else if(tok.type === lexer.TOKEN_INT) {
3543 val = parseInt(tok.value, 10);
3544 }
3545 else if(tok.type === lexer.TOKEN_FLOAT) {
3546 val = parseFloat(tok.value);
3547 }
3548 else if(tok.type === lexer.TOKEN_BOOLEAN) {
3549 if(tok.value === 'true') {
3550 val = true;
3551 }
3552 else if(tok.value === 'false') {
3553 val = false;
3554 }
3555 else {
3556 this.fail('invalid boolean: ' + tok.value,
3557 tok.lineno,
3558 tok.colno);
3559 }
3560 }
3561 else if(tok.type === lexer.TOKEN_NONE) {
3562 val = null;
3563 }
3564 else if (tok.type === lexer.TOKEN_REGEX) {
3565 val = new RegExp(tok.value.body, tok.value.flags);
3566 }
3567
3568 if(val !== undefined) {
3569 node = new nodes.Literal(tok.lineno, tok.colno, val);
3570 }
3571 else if(tok.type === lexer.TOKEN_SYMBOL) {
3572 node = new nodes.Symbol(tok.lineno, tok.colno, tok.value);
3573
3574 if(!noPostfix) {
3575 node = this.parsePostfix(node);
3576 }
3577 }
3578 else {
3579 // See if it's an aggregate type, we need to push the
3580 // current delimiter token back on
3581 this.pushToken(tok);
3582 node = this.parseAggregate();
3583 }
3584
3585 if(node) {
3586 return node;
3587 }
3588 else {
3589 this.fail('unexpected token: ' + tok.value,
3590 tok.lineno,
3591 tok.colno);
3592 }
3593 },
3594
3595 parseFilterName: function() {
3596 var tok = this.expect(lexer.TOKEN_SYMBOL);
3597 var name = tok.value;
3598
3599 while(this.skipValue(lexer.TOKEN_OPERATOR, '.')) {
3600 name += '.' + this.expect(lexer.TOKEN_SYMBOL).value;
3601 }
3602
3603 return new nodes.Symbol(tok.lineno, tok.colno, name);
3604 },
3605
3606 parseFilterArgs: function(node) {
3607 if(this.peekToken().type === lexer.TOKEN_LEFT_PAREN) {
3608 // Get a FunCall node and add the parameters to the
3609 // filter
3610 var call = this.parsePostfix(node);
3611 return call.args.children;
3612 }
3613 return [];
3614 },
3615
3616 parseFilter: function(node) {
3617 while(this.skip(lexer.TOKEN_PIPE)) {
3618 var name = this.parseFilterName();
3619
3620 node = new nodes.Filter(
3621 name.lineno,
3622 name.colno,
3623 name,
3624 new nodes.NodeList(
3625 name.lineno,
3626 name.colno,
3627 [node].concat(this.parseFilterArgs(node))
3628 )
3629 );
3630 }
3631
3632 return node;
3633 },
3634
3635 parseFilterStatement: function() {
3636 var filterTok = this.peekToken();
3637 if(!this.skipSymbol('filter')) {
3638 this.fail('parseFilterStatement: expected filter');
3639 }
3640
3641 var name = this.parseFilterName();
3642 var args = this.parseFilterArgs(name);
3643
3644 this.advanceAfterBlockEnd(filterTok.value);
3645 var body = new nodes.Capture(
3646 name.lineno,
3647 name.colno,
3648 this.parseUntilBlocks('endfilter')
3649 );
3650 this.advanceAfterBlockEnd();
3651
3652 var node = new nodes.Filter(
3653 name.lineno,
3654 name.colno,
3655 name,
3656 new nodes.NodeList(
3657 name.lineno,
3658 name.colno,
3659 [body].concat(args)
3660 )
3661 );
3662
3663 return new nodes.Output(
3664 name.lineno,
3665 name.colno,
3666 [node]
3667 );
3668 },
3669
3670 parseAggregate: function() {
3671 var tok = this.nextToken();
3672 var node;
3673
3674 switch(tok.type) {
3675 case lexer.TOKEN_LEFT_PAREN:
3676 node = new nodes.Group(tok.lineno, tok.colno); break;
3677 case lexer.TOKEN_LEFT_BRACKET:
3678 node = new nodes.Array(tok.lineno, tok.colno); break;
3679 case lexer.TOKEN_LEFT_CURLY:
3680 node = new nodes.Dict(tok.lineno, tok.colno); break;
3681 default:
3682 return null;
3683 }
3684
3685 while(1) {
3686 var type = this.peekToken().type;
3687 if(type === lexer.TOKEN_RIGHT_PAREN ||
3688 type === lexer.TOKEN_RIGHT_BRACKET ||
3689 type === lexer.TOKEN_RIGHT_CURLY) {
3690 this.nextToken();
3691 break;
3692 }
3693
3694 if(node.children.length > 0) {
3695 if(!this.skip(lexer.TOKEN_COMMA)) {
3696 this.fail('parseAggregate: expected comma after expression',
3697 tok.lineno,
3698 tok.colno);
3699 }
3700 }
3701
3702 if(node instanceof nodes.Dict) {
3703 // TODO: check for errors
3704 var key = this.parsePrimary();
3705
3706 // We expect a key/value pair for dicts, separated by a
3707 // colon
3708 if(!this.skip(lexer.TOKEN_COLON)) {
3709 this.fail('parseAggregate: expected colon after dict key',
3710 tok.lineno,
3711 tok.colno);
3712 }
3713
3714 // TODO: check for errors
3715 var value = this.parseExpression();
3716 node.addChild(new nodes.Pair(key.lineno,
3717 key.colno,
3718 key,
3719 value));
3720 }
3721 else {
3722 // TODO: check for errors
3723 var expr = this.parseExpression();
3724 node.addChild(expr);
3725 }
3726 }
3727
3728 return node;
3729 },
3730
3731 parseSignature: function(tolerant, noParens) {
3732 var tok = this.peekToken();
3733 if(!noParens && tok.type !== lexer.TOKEN_LEFT_PAREN) {
3734 if(tolerant) {
3735 return null;
3736 }
3737 else {
3738 this.fail('expected arguments', tok.lineno, tok.colno);
3739 }
3740 }
3741
3742 if(tok.type === lexer.TOKEN_LEFT_PAREN) {
3743 tok = this.nextToken();
3744 }
3745
3746 var args = new nodes.NodeList(tok.lineno, tok.colno);
3747 var kwargs = new nodes.KeywordArgs(tok.lineno, tok.colno);
3748 var checkComma = false;
3749
3750 while(1) {
3751 tok = this.peekToken();
3752 if(!noParens && tok.type === lexer.TOKEN_RIGHT_PAREN) {
3753 this.nextToken();
3754 break;
3755 }
3756 else if(noParens && tok.type === lexer.TOKEN_BLOCK_END) {
3757 break;
3758 }
3759
3760 if(checkComma && !this.skip(lexer.TOKEN_COMMA)) {
3761 this.fail('parseSignature: expected comma after expression',
3762 tok.lineno,
3763 tok.colno);
3764 }
3765 else {
3766 var arg = this.parseExpression();
3767
3768 if(this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
3769 kwargs.addChild(
3770 new nodes.Pair(arg.lineno,
3771 arg.colno,
3772 arg,
3773 this.parseExpression())
3774 );
3775 }
3776 else {
3777 args.addChild(arg);
3778 }
3779 }
3780
3781 checkComma = true;
3782 }
3783
3784 if(kwargs.children.length) {
3785 args.addChild(kwargs);
3786 }
3787
3788 return args;
3789 },
3790
3791 parseUntilBlocks: function(/* blockNames */) {
3792 var prev = this.breakOnBlocks;
3793 this.breakOnBlocks = lib.toArray(arguments);
3794
3795 var ret = this.parse();
3796
3797 this.breakOnBlocks = prev;
3798 return ret;
3799 },
3800
3801 parseNodes: function () {
3802 var tok;
3803 var buf = [];
3804
3805 while((tok = this.nextToken())) {
3806 if(tok.type === lexer.TOKEN_DATA) {
3807 var data = tok.value;
3808 var nextToken = this.peekToken();
3809 var nextVal = nextToken && nextToken.value;
3810
3811 // If the last token has "-" we need to trim the
3812 // leading whitespace of the data. This is marked with
3813 // the `dropLeadingWhitespace` variable.
3814 if(this.dropLeadingWhitespace) {
3815 // TODO: this could be optimized (don't use regex)
3816 data = data.replace(/^\s*/, '');
3817 this.dropLeadingWhitespace = false;
3818 }
3819
3820 // Same for the succeding block start token
3821 if(nextToken &&
3822 ((nextToken.type === lexer.TOKEN_BLOCK_START &&
3823 nextVal.charAt(nextVal.length - 1) === '-') ||
3824 (nextToken.type === lexer.TOKEN_COMMENT &&
3825 nextVal.charAt(this.tokens.tags.COMMENT_START.length)
3826 === '-'))) {
3827 // TODO: this could be optimized (don't use regex)
3828 data = data.replace(/\s*$/, '');
3829 }
3830
3831 buf.push(new nodes.Output(tok.lineno,
3832 tok.colno,
3833 [new nodes.TemplateData(tok.lineno,
3834 tok.colno,
3835 data)]));
3836 }
3837 else if(tok.type === lexer.TOKEN_BLOCK_START) {
3838 this.dropLeadingWhitespace = false;
3839 var n = this.parseStatement();
3840 if(!n) {
3841 break;
3842 }
3843 buf.push(n);
3844 }
3845 else if(tok.type === lexer.TOKEN_VARIABLE_START) {
3846 var e = this.parseExpression();
3847 this.advanceAfterVariableEnd();
3848 this.dropLeadingWhitespace = false;
3849 buf.push(new nodes.Output(tok.lineno, tok.colno, [e]));
3850 }
3851 else if(tok.type === lexer.TOKEN_COMMENT) {
3852 this.dropLeadingWhitespace = tok.value.charAt(
3853 tok.value.length - this.tokens.tags.COMMENT_END.length - 1
3854 ) === '-';
3855 } else {
3856 // Ignore comments, otherwise this should be an error
3857 this.fail('Unexpected token at top-level: ' +
3858 tok.type, tok.lineno, tok.colno);
3859
3860 }
3861 }
3862
3863 return buf;
3864 },
3865
3866 parse: function() {
3867 return new nodes.NodeList(0, 0, this.parseNodes());
3868 },
3869
3870 parseAsRoot: function() {
3871 return new nodes.Root(0, 0, this.parseNodes());
3872 }
3873 });
3874
3875 // var util = require('util');
3876
3877 // var l = lexer.lex('{%- if x -%}\n hello {% endif %}');
3878 // var t;
3879 // while((t = l.nextToken())) {
3880 // console.log(util.inspect(t));
3881 // }
3882
3883 // var p = new Parser(lexer.lex('hello {% filter title %}' +
3884 // 'Hello madam how are you' +
3885 // '{% endfilter %}'));
3886 // var n = p.parseAsRoot();
3887 // nodes.printNodes(n);
3888
3889 module.exports = {
3890 parse: function(src, extensions, opts) {
3891 var p = new Parser(lexer.lex(src, opts));
3892 if (extensions !== undefined) {
3893 p.extensions = extensions;
3894 }
3895 return p.parseAsRoot();
3896 }
3897 };
3898
3899
3900/***/ },
3901/* 9 */
3902/***/ function(module, exports, __webpack_require__) {
3903
3904 'use strict';
3905
3906 var lib = __webpack_require__(1);
3907
3908 var whitespaceChars = ' \n\t\r\u00A0';
3909 var delimChars = '()[]{}%*-+~/#,:|.<>=!';
3910 var intChars = '0123456789';
3911
3912 var BLOCK_START = '{%';
3913 var BLOCK_END = '%}';
3914 var VARIABLE_START = '{{';
3915 var VARIABLE_END = '}}';
3916 var COMMENT_START = '{#';
3917 var COMMENT_END = '#}';
3918
3919 var TOKEN_STRING = 'string';
3920 var TOKEN_WHITESPACE = 'whitespace';
3921 var TOKEN_DATA = 'data';
3922 var TOKEN_BLOCK_START = 'block-start';
3923 var TOKEN_BLOCK_END = 'block-end';
3924 var TOKEN_VARIABLE_START = 'variable-start';
3925 var TOKEN_VARIABLE_END = 'variable-end';
3926 var TOKEN_COMMENT = 'comment';
3927 var TOKEN_LEFT_PAREN = 'left-paren';
3928 var TOKEN_RIGHT_PAREN = 'right-paren';
3929 var TOKEN_LEFT_BRACKET = 'left-bracket';
3930 var TOKEN_RIGHT_BRACKET = 'right-bracket';
3931 var TOKEN_LEFT_CURLY = 'left-curly';
3932 var TOKEN_RIGHT_CURLY = 'right-curly';
3933 var TOKEN_OPERATOR = 'operator';
3934 var TOKEN_COMMA = 'comma';
3935 var TOKEN_COLON = 'colon';
3936 var TOKEN_TILDE = 'tilde';
3937 var TOKEN_PIPE = 'pipe';
3938 var TOKEN_INT = 'int';
3939 var TOKEN_FLOAT = 'float';
3940 var TOKEN_BOOLEAN = 'boolean';
3941 var TOKEN_NONE = 'none';
3942 var TOKEN_SYMBOL = 'symbol';
3943 var TOKEN_SPECIAL = 'special';
3944 var TOKEN_REGEX = 'regex';
3945
3946 function token(type, value, lineno, colno) {
3947 return {
3948 type: type,
3949 value: value,
3950 lineno: lineno,
3951 colno: colno
3952 };
3953 }
3954
3955 function Tokenizer(str, opts) {
3956 this.str = str;
3957 this.index = 0;
3958 this.len = str.length;
3959 this.lineno = 0;
3960 this.colno = 0;
3961
3962 this.in_code = false;
3963
3964 opts = opts || {};
3965
3966 var tags = opts.tags || {};
3967 this.tags = {
3968 BLOCK_START: tags.blockStart || BLOCK_START,
3969 BLOCK_END: tags.blockEnd || BLOCK_END,
3970 VARIABLE_START: tags.variableStart || VARIABLE_START,
3971 VARIABLE_END: tags.variableEnd || VARIABLE_END,
3972 COMMENT_START: tags.commentStart || COMMENT_START,
3973 COMMENT_END: tags.commentEnd || COMMENT_END
3974 };
3975
3976 this.trimBlocks = !!opts.trimBlocks;
3977 this.lstripBlocks = !!opts.lstripBlocks;
3978 }
3979
3980 Tokenizer.prototype.nextToken = function() {
3981 var lineno = this.lineno;
3982 var colno = this.colno;
3983 var tok;
3984
3985 if(this.in_code) {
3986 // Otherwise, if we are in a block parse it as code
3987 var cur = this.current();
3988
3989 if(this.is_finished()) {
3990 // We have nothing else to parse
3991 return null;
3992 }
3993 else if(cur === '"' || cur === '\'') {
3994 // We've hit a string
3995 return token(TOKEN_STRING, this.parseString(cur), lineno, colno);
3996 }
3997 else if((tok = this._extract(whitespaceChars))) {
3998 // We hit some whitespace
3999 return token(TOKEN_WHITESPACE, tok, lineno, colno);
4000 }
4001 else if((tok = this._extractString(this.tags.BLOCK_END)) ||
4002 (tok = this._extractString('-' + this.tags.BLOCK_END))) {
4003 // Special check for the block end tag
4004 //
4005 // It is a requirement that start and end tags are composed of
4006 // delimiter characters (%{}[] etc), and our code always
4007 // breaks on delimiters so we can assume the token parsing
4008 // doesn't consume these elsewhere
4009 this.in_code = false;
4010 if(this.trimBlocks) {
4011 cur = this.current();
4012 if(cur === '\n') {
4013 // Skip newline
4014 this.forward();
4015 }else if(cur === '\r'){
4016 // Skip CRLF newline
4017 this.forward();
4018 cur = this.current();
4019 if(cur === '\n'){
4020 this.forward();
4021 }else{
4022 // Was not a CRLF, so go back
4023 this.back();
4024 }
4025 }
4026 }
4027 return token(TOKEN_BLOCK_END, tok, lineno, colno);
4028 }
4029 else if((tok = this._extractString(this.tags.VARIABLE_END))) {
4030 // Special check for variable end tag (see above)
4031 this.in_code = false;
4032 return token(TOKEN_VARIABLE_END, tok, lineno, colno);
4033 }
4034 else if (cur === 'r' && this.str.charAt(this.index + 1) === '/') {
4035 // Skip past 'r/'.
4036 this.forwardN(2);
4037
4038 // Extract until the end of the regex -- / ends it, \/ does not.
4039 var regexBody = '';
4040 while (!this.is_finished()) {
4041 if (this.current() === '/' && this.previous() !== '\\') {
4042 this.forward();
4043 break;
4044 } else {
4045 regexBody += this.current();
4046 this.forward();
4047 }
4048 }
4049
4050 // Check for flags.
4051 // The possible flags are according to https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
4052 var POSSIBLE_FLAGS = ['g', 'i', 'm', 'y'];
4053 var regexFlags = '';
4054 while (!this.is_finished()) {
4055 var isCurrentAFlag = POSSIBLE_FLAGS.indexOf(this.current()) !== -1;
4056 if (isCurrentAFlag) {
4057 regexFlags += this.current();
4058 this.forward();
4059 } else {
4060 break;
4061 }
4062 }
4063
4064 return token(TOKEN_REGEX, {body: regexBody, flags: regexFlags}, lineno, colno);
4065 }
4066 else if(delimChars.indexOf(cur) !== -1) {
4067 // We've hit a delimiter (a special char like a bracket)
4068 this.forward();
4069 var complexOps = ['==', '===', '!=', '!==', '<=', '>=', '//', '**'];
4070 var curComplex = cur + this.current();
4071 var type;
4072
4073 if(lib.indexOf(complexOps, curComplex) !== -1) {
4074 this.forward();
4075 cur = curComplex;
4076
4077 // See if this is a strict equality/inequality comparator
4078 if(lib.indexOf(complexOps, curComplex + this.current()) !== -1) {
4079 cur = curComplex + this.current();
4080 this.forward();
4081 }
4082 }
4083
4084 switch(cur) {
4085 case '(': type = TOKEN_LEFT_PAREN; break;
4086 case ')': type = TOKEN_RIGHT_PAREN; break;
4087 case '[': type = TOKEN_LEFT_BRACKET; break;
4088 case ']': type = TOKEN_RIGHT_BRACKET; break;
4089 case '{': type = TOKEN_LEFT_CURLY; break;
4090 case '}': type = TOKEN_RIGHT_CURLY; break;
4091 case ',': type = TOKEN_COMMA; break;
4092 case ':': type = TOKEN_COLON; break;
4093 case '~': type = TOKEN_TILDE; break;
4094 case '|': type = TOKEN_PIPE; break;
4095 default: type = TOKEN_OPERATOR;
4096 }
4097
4098 return token(type, cur, lineno, colno);
4099 }
4100 else {
4101 // We are not at whitespace or a delimiter, so extract the
4102 // text and parse it
4103 tok = this._extractUntil(whitespaceChars + delimChars);
4104
4105 if(tok.match(/^[-+]?[0-9]+$/)) {
4106 if(this.current() === '.') {
4107 this.forward();
4108 var dec = this._extract(intChars);
4109 return token(TOKEN_FLOAT, tok + '.' + dec, lineno, colno);
4110 }
4111 else {
4112 return token(TOKEN_INT, tok, lineno, colno);
4113 }
4114 }
4115 else if(tok.match(/^(true|false)$/)) {
4116 return token(TOKEN_BOOLEAN, tok, lineno, colno);
4117 }
4118 else if(tok === 'none') {
4119 return token(TOKEN_NONE, tok, lineno, colno);
4120 }
4121 else if(tok) {
4122 return token(TOKEN_SYMBOL, tok, lineno, colno);
4123 }
4124 else {
4125 throw new Error('Unexpected value while parsing: ' + tok);
4126 }
4127 }
4128 }
4129 else {
4130 // Parse out the template text, breaking on tag
4131 // delimiters because we need to look for block/variable start
4132 // tags (don't use the full delimChars for optimization)
4133 var beginChars = (this.tags.BLOCK_START.charAt(0) +
4134 this.tags.VARIABLE_START.charAt(0) +
4135 this.tags.COMMENT_START.charAt(0) +
4136 this.tags.COMMENT_END.charAt(0));
4137
4138 if(this.is_finished()) {
4139 return null;
4140 }
4141 else if((tok = this._extractString(this.tags.BLOCK_START + '-')) ||
4142 (tok = this._extractString(this.tags.BLOCK_START))) {
4143 this.in_code = true;
4144 return token(TOKEN_BLOCK_START, tok, lineno, colno);
4145 }
4146 else if((tok = this._extractString(this.tags.VARIABLE_START))) {
4147 this.in_code = true;
4148 return token(TOKEN_VARIABLE_START, tok, lineno, colno);
4149 }
4150 else {
4151 tok = '';
4152 var data;
4153 var in_comment = false;
4154
4155 if(this._matches(this.tags.COMMENT_START)) {
4156 in_comment = true;
4157 tok = this._extractString(this.tags.COMMENT_START);
4158 }
4159
4160 // Continually consume text, breaking on the tag delimiter
4161 // characters and checking to see if it's a start tag.
4162 //
4163 // We could hit the end of the template in the middle of
4164 // our looping, so check for the null return value from
4165 // _extractUntil
4166 while((data = this._extractUntil(beginChars)) !== null) {
4167 tok += data;
4168
4169 if((this._matches(this.tags.BLOCK_START) ||
4170 this._matches(this.tags.VARIABLE_START) ||
4171 this._matches(this.tags.COMMENT_START)) &&
4172 !in_comment) {
4173 if(this.lstripBlocks &&
4174 this._matches(this.tags.BLOCK_START) &&
4175 this.colno > 0 &&
4176 this.colno <= tok.length) {
4177 var lastLine = tok.slice(-this.colno);
4178 if(/^\s+$/.test(lastLine)) {
4179 // Remove block leading whitespace from beginning of the string
4180 tok = tok.slice(0, -this.colno);
4181 if(!tok.length) {
4182 // All data removed, collapse to avoid unnecessary nodes
4183 // by returning next token (block start)
4184 return this.nextToken();
4185 }
4186 }
4187 }
4188 // If it is a start tag, stop looping
4189 break;
4190 }
4191 else if(this._matches(this.tags.COMMENT_END)) {
4192 if(!in_comment) {
4193 throw new Error('unexpected end of comment');
4194 }
4195 tok += this._extractString(this.tags.COMMENT_END);
4196 break;
4197 }
4198 else {
4199 // It does not match any tag, so add the character and
4200 // carry on
4201 tok += this.current();
4202 this.forward();
4203 }
4204 }
4205
4206 if(data === null && in_comment) {
4207 throw new Error('expected end of comment, got end of file');
4208 }
4209
4210 return token(in_comment ? TOKEN_COMMENT : TOKEN_DATA,
4211 tok,
4212 lineno,
4213 colno);
4214 }
4215 }
4216
4217 throw new Error('Could not parse text');
4218 };
4219
4220 Tokenizer.prototype.parseString = function(delimiter) {
4221 this.forward();
4222
4223 var str = '';
4224
4225 while(!this.is_finished() && this.current() !== delimiter) {
4226 var cur = this.current();
4227
4228 if(cur === '\\') {
4229 this.forward();
4230 switch(this.current()) {
4231 case 'n': str += '\n'; break;
4232 case 't': str += '\t'; break;
4233 case 'r': str += '\r'; break;
4234 default:
4235 str += this.current();
4236 }
4237 this.forward();
4238 }
4239 else {
4240 str += cur;
4241 this.forward();
4242 }
4243 }
4244
4245 this.forward();
4246 return str;
4247 };
4248
4249 Tokenizer.prototype._matches = function(str) {
4250 if(this.index + str.length > this.len) {
4251 return null;
4252 }
4253
4254 var m = this.str.slice(this.index, this.index + str.length);
4255 return m === str;
4256 };
4257
4258 Tokenizer.prototype._extractString = function(str) {
4259 if(this._matches(str)) {
4260 this.index += str.length;
4261 return str;
4262 }
4263 return null;
4264 };
4265
4266 Tokenizer.prototype._extractUntil = function(charString) {
4267 // Extract all non-matching chars, with the default matching set
4268 // to everything
4269 return this._extractMatching(true, charString || '');
4270 };
4271
4272 Tokenizer.prototype._extract = function(charString) {
4273 // Extract all matching chars (no default, so charString must be
4274 // explicit)
4275 return this._extractMatching(false, charString);
4276 };
4277
4278 Tokenizer.prototype._extractMatching = function (breakOnMatch, charString) {
4279 // Pull out characters until a breaking char is hit.
4280 // If breakOnMatch is false, a non-matching char stops it.
4281 // If breakOnMatch is true, a matching char stops it.
4282
4283 if(this.is_finished()) {
4284 return null;
4285 }
4286
4287 var first = charString.indexOf(this.current());
4288
4289 // Only proceed if the first character doesn't meet our condition
4290 if((breakOnMatch && first === -1) ||
4291 (!breakOnMatch && first !== -1)) {
4292 var t = this.current();
4293 this.forward();
4294
4295 // And pull out all the chars one at a time until we hit a
4296 // breaking char
4297 var idx = charString.indexOf(this.current());
4298
4299 while(((breakOnMatch && idx === -1) ||
4300 (!breakOnMatch && idx !== -1)) && !this.is_finished()) {
4301 t += this.current();
4302 this.forward();
4303
4304 idx = charString.indexOf(this.current());
4305 }
4306
4307 return t;
4308 }
4309
4310 return '';
4311 };
4312
4313 Tokenizer.prototype._extractRegex = function(regex) {
4314 var matches = this.currentStr().match(regex);
4315 if(!matches) {
4316 return null;
4317 }
4318
4319 // Move forward whatever was matched
4320 this.forwardN(matches[0].length);
4321
4322 return matches;
4323 };
4324
4325 Tokenizer.prototype.is_finished = function() {
4326 return this.index >= this.len;
4327 };
4328
4329 Tokenizer.prototype.forwardN = function(n) {
4330 for(var i=0; i<n; i++) {
4331 this.forward();
4332 }
4333 };
4334
4335 Tokenizer.prototype.forward = function() {
4336 this.index++;
4337
4338 if(this.previous() === '\n') {
4339 this.lineno++;
4340 this.colno = 0;
4341 }
4342 else {
4343 this.colno++;
4344 }
4345 };
4346
4347 Tokenizer.prototype.backN = function(n) {
4348 for(var i=0; i<n; i++) {
4349 this.back();
4350 }
4351 };
4352
4353 Tokenizer.prototype.back = function() {
4354 this.index--;
4355
4356 if(this.current() === '\n') {
4357 this.lineno--;
4358
4359 var idx = this.src.lastIndexOf('\n', this.index-1);
4360 if(idx === -1) {
4361 this.colno = this.index;
4362 }
4363 else {
4364 this.colno = this.index - idx;
4365 }
4366 }
4367 else {
4368 this.colno--;
4369 }
4370 };
4371
4372 // current returns current character
4373 Tokenizer.prototype.current = function() {
4374 if(!this.is_finished()) {
4375 return this.str.charAt(this.index);
4376 }
4377 return '';
4378 };
4379
4380 // currentStr returns what's left of the unparsed string
4381 Tokenizer.prototype.currentStr = function() {
4382 if(!this.is_finished()) {
4383 return this.str.substr(this.index);
4384 }
4385 return '';
4386 };
4387
4388 Tokenizer.prototype.previous = function() {
4389 return this.str.charAt(this.index-1);
4390 };
4391
4392 module.exports = {
4393 lex: function(src, opts) {
4394 return new Tokenizer(src, opts);
4395 },
4396
4397 TOKEN_STRING: TOKEN_STRING,
4398 TOKEN_WHITESPACE: TOKEN_WHITESPACE,
4399 TOKEN_DATA: TOKEN_DATA,
4400 TOKEN_BLOCK_START: TOKEN_BLOCK_START,
4401 TOKEN_BLOCK_END: TOKEN_BLOCK_END,
4402 TOKEN_VARIABLE_START: TOKEN_VARIABLE_START,
4403 TOKEN_VARIABLE_END: TOKEN_VARIABLE_END,
4404 TOKEN_COMMENT: TOKEN_COMMENT,
4405 TOKEN_LEFT_PAREN: TOKEN_LEFT_PAREN,
4406 TOKEN_RIGHT_PAREN: TOKEN_RIGHT_PAREN,
4407 TOKEN_LEFT_BRACKET: TOKEN_LEFT_BRACKET,
4408 TOKEN_RIGHT_BRACKET: TOKEN_RIGHT_BRACKET,
4409 TOKEN_LEFT_CURLY: TOKEN_LEFT_CURLY,
4410 TOKEN_RIGHT_CURLY: TOKEN_RIGHT_CURLY,
4411 TOKEN_OPERATOR: TOKEN_OPERATOR,
4412 TOKEN_COMMA: TOKEN_COMMA,
4413 TOKEN_COLON: TOKEN_COLON,
4414 TOKEN_TILDE: TOKEN_TILDE,
4415 TOKEN_PIPE: TOKEN_PIPE,
4416 TOKEN_INT: TOKEN_INT,
4417 TOKEN_FLOAT: TOKEN_FLOAT,
4418 TOKEN_BOOLEAN: TOKEN_BOOLEAN,
4419 TOKEN_NONE: TOKEN_NONE,
4420 TOKEN_SYMBOL: TOKEN_SYMBOL,
4421 TOKEN_SPECIAL: TOKEN_SPECIAL,
4422 TOKEN_REGEX: TOKEN_REGEX
4423 };
4424
4425
4426/***/ },
4427/* 10 */
4428/***/ function(module, exports, __webpack_require__) {
4429
4430 /* WEBPACK VAR INJECTION */(function(process) {'use strict';
4431
4432 var lib = __webpack_require__(1);
4433 // jshint -W079
4434 var Object = __webpack_require__(6);
4435
4436 function traverseAndCheck(obj, type, results) {
4437 if(obj instanceof type) {
4438 results.push(obj);
4439 }
4440
4441 if(obj instanceof Node) {
4442 obj.findAll(type, results);
4443 }
4444 }
4445
4446 var Node = Object.extend('Node', {
4447 init: function(lineno, colno) {
4448 this.lineno = lineno;
4449 this.colno = colno;
4450
4451 var fields = this.fields;
4452 for(var i = 0, l = fields.length; i < l; i++) {
4453 var field = fields[i];
4454
4455 // The first two args are line/col numbers, so offset by 2
4456 var val = arguments[i + 2];
4457
4458 // Fields should never be undefined, but null. It makes
4459 // testing easier to normalize values.
4460 if(val === undefined) {
4461 val = null;
4462 }
4463
4464 this[field] = val;
4465 }
4466 },
4467
4468 findAll: function(type, results) {
4469 results = results || [];
4470
4471 var i, l;
4472 if(this instanceof NodeList) {
4473 var children = this.children;
4474
4475 for(i = 0, l = children.length; i < l; i++) {
4476 traverseAndCheck(children[i], type, results);
4477 }
4478 }
4479 else {
4480 var fields = this.fields;
4481
4482 for(i = 0, l = fields.length; i < l; i++) {
4483 traverseAndCheck(this[fields[i]], type, results);
4484 }
4485 }
4486
4487 return results;
4488 },
4489
4490 iterFields: function(func) {
4491 lib.each(this.fields, function(field) {
4492 func(this[field], field);
4493 }, this);
4494 }
4495 });
4496
4497 // Abstract nodes
4498 var Value = Node.extend('Value', { fields: ['value'] });
4499
4500 // Concrete nodes
4501 var NodeList = Node.extend('NodeList', {
4502 fields: ['children'],
4503
4504 init: function(lineno, colno, nodes) {
4505 this.parent(lineno, colno, nodes || []);
4506 },
4507
4508 addChild: function(node) {
4509 this.children.push(node);
4510 }
4511 });
4512
4513 var Root = NodeList.extend('Root');
4514 var Literal = Value.extend('Literal');
4515 var Symbol = Value.extend('Symbol');
4516 var Group = NodeList.extend('Group');
4517 var Array = NodeList.extend('Array');
4518 var Pair = Node.extend('Pair', { fields: ['key', 'value'] });
4519 var Dict = NodeList.extend('Dict');
4520 var LookupVal = Node.extend('LookupVal', { fields: ['target', 'val'] });
4521 var If = Node.extend('If', { fields: ['cond', 'body', 'else_'] });
4522 var IfAsync = If.extend('IfAsync');
4523 var InlineIf = Node.extend('InlineIf', { fields: ['cond', 'body', 'else_'] });
4524 var For = Node.extend('For', { fields: ['arr', 'name', 'body', 'else_'] });
4525 var AsyncEach = For.extend('AsyncEach');
4526 var AsyncAll = For.extend('AsyncAll');
4527 var Macro = Node.extend('Macro', { fields: ['name', 'args', 'body'] });
4528 var Caller = Macro.extend('Caller');
4529 var Import = Node.extend('Import', { fields: ['template', 'target', 'withContext'] });
4530 var FromImport = Node.extend('FromImport', {
4531 fields: ['template', 'names', 'withContext'],
4532
4533 init: function(lineno, colno, template, names, withContext) {
4534 this.parent(lineno, colno,
4535 template,
4536 names || new NodeList(), withContext);
4537 }
4538 });
4539 var FunCall = Node.extend('FunCall', { fields: ['name', 'args'] });
4540 var Filter = FunCall.extend('Filter');
4541 var FilterAsync = Filter.extend('FilterAsync', {
4542 fields: ['name', 'args', 'symbol']
4543 });
4544 var KeywordArgs = Dict.extend('KeywordArgs');
4545 var Block = Node.extend('Block', { fields: ['name', 'body'] });
4546 var Super = Node.extend('Super', { fields: ['blockName', 'symbol'] });
4547 var TemplateRef = Node.extend('TemplateRef', { fields: ['template'] });
4548 var Extends = TemplateRef.extend('Extends');
4549 var Include = Node.extend('Include', { fields: ['template', 'ignoreMissing'] });
4550 var Set = Node.extend('Set', { fields: ['targets', 'value'] });
4551 var Output = NodeList.extend('Output');
4552 var Capture = Node.extend('Capture', { fields: ['body'] });
4553 var TemplateData = Literal.extend('TemplateData');
4554 var UnaryOp = Node.extend('UnaryOp', { fields: ['target'] });
4555 var BinOp = Node.extend('BinOp', { fields: ['left', 'right'] });
4556 var In = BinOp.extend('In');
4557 var Or = BinOp.extend('Or');
4558 var And = BinOp.extend('And');
4559 var Not = UnaryOp.extend('Not');
4560 var Add = BinOp.extend('Add');
4561 var Concat = BinOp.extend('Concat');
4562 var Sub = BinOp.extend('Sub');
4563 var Mul = BinOp.extend('Mul');
4564 var Div = BinOp.extend('Div');
4565 var FloorDiv = BinOp.extend('FloorDiv');
4566 var Mod = BinOp.extend('Mod');
4567 var Pow = BinOp.extend('Pow');
4568 var Neg = UnaryOp.extend('Neg');
4569 var Pos = UnaryOp.extend('Pos');
4570 var Compare = Node.extend('Compare', { fields: ['expr', 'ops'] });
4571 var CompareOperand = Node.extend('CompareOperand', {
4572 fields: ['expr', 'type']
4573 });
4574
4575 var CallExtension = Node.extend('CallExtension', {
4576 fields: ['extName', 'prop', 'args', 'contentArgs'],
4577
4578 init: function(ext, prop, args, contentArgs) {
4579 this.extName = ext._name || ext;
4580 this.prop = prop;
4581 this.args = args || new NodeList();
4582 this.contentArgs = contentArgs || [];
4583 this.autoescape = ext.autoescape;
4584 }
4585 });
4586
4587 var CallExtensionAsync = CallExtension.extend('CallExtensionAsync');
4588
4589 // Print the AST in a nicely formatted tree format for debuggin
4590 function printNodes(node, indent) {
4591 indent = indent || 0;
4592
4593 // This is hacky, but this is just a debugging function anyway
4594 function print(str, indent, inline) {
4595 var lines = str.split('\n');
4596
4597 for(var i=0; i<lines.length; i++) {
4598 if(lines[i]) {
4599 if((inline && i > 0) || !inline) {
4600 for(var j=0; j<indent; j++) {
4601 process.stdout.write(' ');
4602 }
4603 }
4604 }
4605
4606 if(i === lines.length-1) {
4607 process.stdout.write(lines[i]);
4608 }
4609 else {
4610 process.stdout.write(lines[i] + '\n');
4611 }
4612 }
4613 }
4614
4615 print(node.typename + ': ', indent);
4616
4617 if(node instanceof NodeList) {
4618 print('\n');
4619 lib.each(node.children, function(n) {
4620 printNodes(n, indent + 2);
4621 });
4622 }
4623 else if(node instanceof CallExtension) {
4624 print(node.extName + '.' + node.prop);
4625 print('\n');
4626
4627 if(node.args) {
4628 printNodes(node.args, indent + 2);
4629 }
4630
4631 if(node.contentArgs) {
4632 lib.each(node.contentArgs, function(n) {
4633 printNodes(n, indent + 2);
4634 });
4635 }
4636 }
4637 else {
4638 var nodes = null;
4639 var props = null;
4640
4641 node.iterFields(function(val, field) {
4642 if(val instanceof Node) {
4643 nodes = nodes || {};
4644 nodes[field] = val;
4645 }
4646 else {
4647 props = props || {};
4648 props[field] = val;
4649 }
4650 });
4651
4652 if(props) {
4653 print(JSON.stringify(props, null, 2) + '\n', null, true);
4654 }
4655 else {
4656 print('\n');
4657 }
4658
4659 if(nodes) {
4660 for(var k in nodes) {
4661 printNodes(nodes[k], indent + 2);
4662 }
4663 }
4664
4665 }
4666 }
4667
4668 // var t = new NodeList(0, 0,
4669 // [new Value(0, 0, 3),
4670 // new Value(0, 0, 10),
4671 // new Pair(0, 0,
4672 // new Value(0, 0, 'key'),
4673 // new Value(0, 0, 'value'))]);
4674 // printNodes(t);
4675
4676 module.exports = {
4677 Node: Node,
4678 Root: Root,
4679 NodeList: NodeList,
4680 Value: Value,
4681 Literal: Literal,
4682 Symbol: Symbol,
4683 Group: Group,
4684 Array: Array,
4685 Pair: Pair,
4686 Dict: Dict,
4687 Output: Output,
4688 Capture: Capture,
4689 TemplateData: TemplateData,
4690 If: If,
4691 IfAsync: IfAsync,
4692 InlineIf: InlineIf,
4693 For: For,
4694 AsyncEach: AsyncEach,
4695 AsyncAll: AsyncAll,
4696 Macro: Macro,
4697 Caller: Caller,
4698 Import: Import,
4699 FromImport: FromImport,
4700 FunCall: FunCall,
4701 Filter: Filter,
4702 FilterAsync: FilterAsync,
4703 KeywordArgs: KeywordArgs,
4704 Block: Block,
4705 Super: Super,
4706 Extends: Extends,
4707 Include: Include,
4708 Set: Set,
4709 LookupVal: LookupVal,
4710 BinOp: BinOp,
4711 In: In,
4712 Or: Or,
4713 And: And,
4714 Not: Not,
4715 Add: Add,
4716 Concat: Concat,
4717 Sub: Sub,
4718 Mul: Mul,
4719 Div: Div,
4720 FloorDiv: FloorDiv,
4721 Mod: Mod,
4722 Pow: Pow,
4723 Neg: Neg,
4724 Pos: Pos,
4725 Compare: Compare,
4726 CompareOperand: CompareOperand,
4727
4728 CallExtension: CallExtension,
4729 CallExtensionAsync: CallExtensionAsync,
4730
4731 printNodes: printNodes
4732 };
4733
4734 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3)))
4735
4736/***/ },
4737/* 11 */
4738/***/ function(module, exports, __webpack_require__) {
4739
4740 'use strict';
4741
4742 var nodes = __webpack_require__(10);
4743 var lib = __webpack_require__(1);
4744
4745 var sym = 0;
4746 function gensym() {
4747 return 'hole_' + sym++;
4748 }
4749
4750 // copy-on-write version of map
4751 function mapCOW(arr, func) {
4752 var res = null;
4753
4754 for(var i=0; i<arr.length; i++) {
4755 var item = func(arr[i]);
4756
4757 if(item !== arr[i]) {
4758 if(!res) {
4759 res = arr.slice();
4760 }
4761
4762 res[i] = item;
4763 }
4764 }
4765
4766 return res || arr;
4767 }
4768
4769 function walk(ast, func, depthFirst) {
4770 if(!(ast instanceof nodes.Node)) {
4771 return ast;
4772 }
4773
4774 if(!depthFirst) {
4775 var astT = func(ast);
4776
4777 if(astT && astT !== ast) {
4778 return astT;
4779 }
4780 }
4781
4782 if(ast instanceof nodes.NodeList) {
4783 var children = mapCOW(ast.children, function(node) {
4784 return walk(node, func, depthFirst);
4785 });
4786
4787 if(children !== ast.children) {
4788 ast = new nodes[ast.typename](ast.lineno, ast.colno, children);
4789 }
4790 }
4791 else if(ast instanceof nodes.CallExtension) {
4792 var args = walk(ast.args, func, depthFirst);
4793
4794 var contentArgs = mapCOW(ast.contentArgs, function(node) {
4795 return walk(node, func, depthFirst);
4796 });
4797
4798 if(args !== ast.args || contentArgs !== ast.contentArgs) {
4799 ast = new nodes[ast.typename](ast.extName,
4800 ast.prop,
4801 args,
4802 contentArgs);
4803 }
4804 }
4805 else {
4806 var props = ast.fields.map(function(field) {
4807 return ast[field];
4808 });
4809
4810 var propsT = mapCOW(props, function(prop) {
4811 return walk(prop, func, depthFirst);
4812 });
4813
4814 if(propsT !== props) {
4815 ast = new nodes[ast.typename](ast.lineno, ast.colno);
4816
4817 propsT.forEach(function(prop, i) {
4818 ast[ast.fields[i]] = prop;
4819 });
4820 }
4821 }
4822
4823 return depthFirst ? (func(ast) || ast) : ast;
4824 }
4825
4826 function depthWalk(ast, func) {
4827 return walk(ast, func, true);
4828 }
4829
4830 function _liftFilters(node, asyncFilters, prop) {
4831 var children = [];
4832
4833 var walked = depthWalk(prop ? node[prop] : node, function(node) {
4834 if(node instanceof nodes.Block) {
4835 return node;
4836 }
4837 else if((node instanceof nodes.Filter &&
4838 lib.indexOf(asyncFilters, node.name.value) !== -1) ||
4839 node instanceof nodes.CallExtensionAsync) {
4840 var symbol = new nodes.Symbol(node.lineno,
4841 node.colno,
4842 gensym());
4843
4844 children.push(new nodes.FilterAsync(node.lineno,
4845 node.colno,
4846 node.name,
4847 node.args,
4848 symbol));
4849 return symbol;
4850 }
4851 });
4852
4853 if(prop) {
4854 node[prop] = walked;
4855 }
4856 else {
4857 node = walked;
4858 }
4859
4860 if(children.length) {
4861 children.push(node);
4862
4863 return new nodes.NodeList(
4864 node.lineno,
4865 node.colno,
4866 children
4867 );
4868 }
4869 else {
4870 return node;
4871 }
4872 }
4873
4874 function liftFilters(ast, asyncFilters) {
4875 return depthWalk(ast, function(node) {
4876 if(node instanceof nodes.Output) {
4877 return _liftFilters(node, asyncFilters);
4878 }
4879 else if(node instanceof nodes.Set) {
4880 return _liftFilters(node, asyncFilters, 'value');
4881 }
4882 else if(node instanceof nodes.For) {
4883 return _liftFilters(node, asyncFilters, 'arr');
4884 }
4885 else if(node instanceof nodes.If) {
4886 return _liftFilters(node, asyncFilters, 'cond');
4887 }
4888 else if(node instanceof nodes.CallExtension) {
4889 return _liftFilters(node, asyncFilters, 'args');
4890 }
4891 });
4892 }
4893
4894 function liftSuper(ast) {
4895 return walk(ast, function(blockNode) {
4896 if(!(blockNode instanceof nodes.Block)) {
4897 return;
4898 }
4899
4900 var hasSuper = false;
4901 var symbol = gensym();
4902
4903 blockNode.body = walk(blockNode.body, function(node) {
4904 if(node instanceof nodes.FunCall &&
4905 node.name.value === 'super') {
4906 hasSuper = true;
4907 return new nodes.Symbol(node.lineno, node.colno, symbol);
4908 }
4909 });
4910
4911 if(hasSuper) {
4912 blockNode.body.children.unshift(new nodes.Super(
4913 0, 0, blockNode.name, new nodes.Symbol(0, 0, symbol)
4914 ));
4915 }
4916 });
4917 }
4918
4919 function convertStatements(ast) {
4920 return depthWalk(ast, function(node) {
4921 if(!(node instanceof nodes.If) &&
4922 !(node instanceof nodes.For)) {
4923 return;
4924 }
4925
4926 var async = false;
4927 walk(node, function(node) {
4928 if(node instanceof nodes.FilterAsync ||
4929 node instanceof nodes.IfAsync ||
4930 node instanceof nodes.AsyncEach ||
4931 node instanceof nodes.AsyncAll ||
4932 node instanceof nodes.CallExtensionAsync) {
4933 async = true;
4934 // Stop iterating by returning the node
4935 return node;
4936 }
4937 });
4938
4939 if(async) {
4940 if(node instanceof nodes.If) {
4941 return new nodes.IfAsync(
4942 node.lineno,
4943 node.colno,
4944 node.cond,
4945 node.body,
4946 node.else_
4947 );
4948 }
4949 else if(node instanceof nodes.For) {
4950 return new nodes.AsyncEach(
4951 node.lineno,
4952 node.colno,
4953 node.arr,
4954 node.name,
4955 node.body,
4956 node.else_
4957 );
4958 }
4959 }
4960 });
4961 }
4962
4963 function cps(ast, asyncFilters) {
4964 return convertStatements(liftSuper(liftFilters(ast, asyncFilters)));
4965 }
4966
4967 function transform(ast, asyncFilters) {
4968 return cps(ast, asyncFilters || []);
4969 }
4970
4971 // var parser = require('./parser');
4972 // var src = 'hello {% foo %}{% endfoo %} end';
4973 // var ast = transform(parser.parse(src, [new FooExtension()]), ['bar']);
4974 // nodes.printNodes(ast);
4975
4976 module.exports = {
4977 transform: transform
4978 };
4979
4980
4981/***/ },
4982/* 12 */
4983/***/ function(module, exports, __webpack_require__) {
4984
4985 'use strict';
4986
4987 var lib = __webpack_require__(1);
4988 var Obj = __webpack_require__(6);
4989
4990 // Frames keep track of scoping both at compile-time and run-time so
4991 // we know how to access variables. Block tags can introduce special
4992 // variables, for example.
4993 var Frame = Obj.extend({
4994 init: function(parent, isolateWrites) {
4995 this.variables = {};
4996 this.parent = parent;
4997 this.topLevel = false;
4998 // if this is true, writes (set) should never propagate upwards past
4999 // this frame to its parent (though reads may).
5000 this.isolateWrites = isolateWrites;
5001 },
5002
5003 set: function(name, val, resolveUp) {
5004 // Allow variables with dots by automatically creating the
5005 // nested structure
5006 var parts = name.split('.');
5007 var obj = this.variables;
5008 var frame = this;
5009
5010 if(resolveUp) {
5011 if((frame = this.resolve(parts[0], true))) {
5012 frame.set(name, val);
5013 return;
5014 }
5015 }
5016
5017 for(var i=0; i<parts.length - 1; i++) {
5018 var id = parts[i];
5019
5020 if(!obj[id]) {
5021 obj[id] = {};
5022 }
5023 obj = obj[id];
5024 }
5025
5026 obj[parts[parts.length - 1]] = val;
5027 },
5028
5029 get: function(name) {
5030 var val = this.variables[name];
5031 if(val !== undefined && val !== null) {
5032 return val;
5033 }
5034 return null;
5035 },
5036
5037 lookup: function(name) {
5038 var p = this.parent;
5039 var val = this.variables[name];
5040 if(val !== undefined && val !== null) {
5041 return val;
5042 }
5043 return p && p.lookup(name);
5044 },
5045
5046 resolve: function(name, forWrite) {
5047 var p = (forWrite && this.isolateWrites) ? undefined : this.parent;
5048 var val = this.variables[name];
5049 if(val !== undefined && val !== null) {
5050 return this;
5051 }
5052 return p && p.resolve(name);
5053 },
5054
5055 push: function(isolateWrites) {
5056 return new Frame(this, isolateWrites);
5057 },
5058
5059 pop: function() {
5060 return this.parent;
5061 }
5062 });
5063
5064 function makeMacro(argNames, kwargNames, func) {
5065 return function() {
5066 var argCount = numArgs(arguments);
5067 var args;
5068 var kwargs = getKeywordArgs(arguments);
5069 var i;
5070
5071 if(argCount > argNames.length) {
5072 args = Array.prototype.slice.call(arguments, 0, argNames.length);
5073
5074 // Positional arguments that should be passed in as
5075 // keyword arguments (essentially default values)
5076 var vals = Array.prototype.slice.call(arguments, args.length, argCount);
5077 for(i = 0; i < vals.length; i++) {
5078 if(i < kwargNames.length) {
5079 kwargs[kwargNames[i]] = vals[i];
5080 }
5081 }
5082
5083 args.push(kwargs);
5084 }
5085 else if(argCount < argNames.length) {
5086 args = Array.prototype.slice.call(arguments, 0, argCount);
5087
5088 for(i = argCount; i < argNames.length; i++) {
5089 var arg = argNames[i];
5090
5091 // Keyword arguments that should be passed as
5092 // positional arguments, i.e. the caller explicitly
5093 // used the name of a positional arg
5094 args.push(kwargs[arg]);
5095 delete kwargs[arg];
5096 }
5097
5098 args.push(kwargs);
5099 }
5100 else {
5101 args = arguments;
5102 }
5103
5104 return func.apply(this, args);
5105 };
5106 }
5107
5108 function makeKeywordArgs(obj) {
5109 obj.__keywords = true;
5110 return obj;
5111 }
5112
5113 function getKeywordArgs(args) {
5114 var len = args.length;
5115 if(len) {
5116 var lastArg = args[len - 1];
5117 if(lastArg && lastArg.hasOwnProperty('__keywords')) {
5118 return lastArg;
5119 }
5120 }
5121 return {};
5122 }
5123
5124 function numArgs(args) {
5125 var len = args.length;
5126 if(len === 0) {
5127 return 0;
5128 }
5129
5130 var lastArg = args[len - 1];
5131 if(lastArg && lastArg.hasOwnProperty('__keywords')) {
5132 return len - 1;
5133 }
5134 else {
5135 return len;
5136 }
5137 }
5138
5139 // A SafeString object indicates that the string should not be
5140 // autoescaped. This happens magically because autoescaping only
5141 // occurs on primitive string objects.
5142 function SafeString(val) {
5143 if(typeof val !== 'string') {
5144 return val;
5145 }
5146
5147 this.val = val;
5148 this.length = val.length;
5149 }
5150
5151 SafeString.prototype = Object.create(String.prototype, {
5152 length: { writable: true, configurable: true, value: 0 }
5153 });
5154 SafeString.prototype.valueOf = function() {
5155 return this.val;
5156 };
5157 SafeString.prototype.toString = function() {
5158 return this.val;
5159 };
5160
5161 function copySafeness(dest, target) {
5162 if(dest instanceof SafeString) {
5163 return new SafeString(target);
5164 }
5165 return target.toString();
5166 }
5167
5168 function markSafe(val) {
5169 var type = typeof val;
5170
5171 if(type === 'string') {
5172 return new SafeString(val);
5173 }
5174 else if(type !== 'function') {
5175 return val;
5176 }
5177 else {
5178 return function() {
5179 var ret = val.apply(this, arguments);
5180
5181 if(typeof ret === 'string') {
5182 return new SafeString(ret);
5183 }
5184
5185 return ret;
5186 };
5187 }
5188 }
5189
5190 function suppressValue(val, autoescape) {
5191 val = (val !== undefined && val !== null) ? val : '';
5192
5193 if(autoescape && !(val instanceof SafeString)) {
5194 val = lib.escape(val.toString());
5195 }
5196
5197 return val;
5198 }
5199
5200 function ensureDefined(val, lineno, colno) {
5201 if(val === null || val === undefined) {
5202 throw new lib.TemplateError(
5203 'attempted to output null or undefined value',
5204 lineno + 1,
5205 colno + 1
5206 );
5207 }
5208 return val;
5209 }
5210
5211 function memberLookup(obj, val) {
5212 obj = obj || {};
5213
5214 if(typeof obj[val] === 'function') {
5215 return function() {
5216 return obj[val].apply(obj, arguments);
5217 };
5218 }
5219
5220 return obj[val];
5221 }
5222
5223 function callWrap(obj, name, context, args) {
5224 if(!obj) {
5225 throw new Error('Unable to call `' + name + '`, which is undefined or falsey');
5226 }
5227 else if(typeof obj !== 'function') {
5228 throw new Error('Unable to call `' + name + '`, which is not a function');
5229 }
5230
5231 // jshint validthis: true
5232 return obj.apply(context, args);
5233 }
5234
5235 function contextOrFrameLookup(context, frame, name) {
5236 var val = frame.lookup(name);
5237 return (val !== undefined && val !== null) ?
5238 val :
5239 context.lookup(name);
5240 }
5241
5242 function handleError(error, lineno, colno) {
5243 if(error.lineno) {
5244 return error;
5245 }
5246 else {
5247 return new lib.TemplateError(error, lineno, colno);
5248 }
5249 }
5250
5251 function asyncEach(arr, dimen, iter, cb) {
5252 if(lib.isArray(arr)) {
5253 var len = arr.length;
5254
5255 lib.asyncIter(arr, function(item, i, next) {
5256 switch(dimen) {
5257 case 1: iter(item, i, len, next); break;
5258 case 2: iter(item[0], item[1], i, len, next); break;
5259 case 3: iter(item[0], item[1], item[2], i, len, next); break;
5260 default:
5261 item.push(i, next);
5262 iter.apply(this, item);
5263 }
5264 }, cb);
5265 }
5266 else {
5267 lib.asyncFor(arr, function(key, val, i, len, next) {
5268 iter(key, val, i, len, next);
5269 }, cb);
5270 }
5271 }
5272
5273 function asyncAll(arr, dimen, func, cb) {
5274 var finished = 0;
5275 var len, i;
5276 var outputArr;
5277
5278 function done(i, output) {
5279 finished++;
5280 outputArr[i] = output;
5281
5282 if(finished === len) {
5283 cb(null, outputArr.join(''));
5284 }
5285 }
5286
5287 if(lib.isArray(arr)) {
5288 len = arr.length;
5289 outputArr = new Array(len);
5290
5291 if(len === 0) {
5292 cb(null, '');
5293 }
5294 else {
5295 for(i = 0; i < arr.length; i++) {
5296 var item = arr[i];
5297
5298 switch(dimen) {
5299 case 1: func(item, i, len, done); break;
5300 case 2: func(item[0], item[1], i, len, done); break;
5301 case 3: func(item[0], item[1], item[2], i, len, done); break;
5302 default:
5303 item.push(i, done);
5304 // jshint validthis: true
5305 func.apply(this, item);
5306 }
5307 }
5308 }
5309 }
5310 else {
5311 var keys = lib.keys(arr);
5312 len = keys.length;
5313 outputArr = new Array(len);
5314
5315 if(len === 0) {
5316 cb(null, '');
5317 }
5318 else {
5319 for(i = 0; i < keys.length; i++) {
5320 var k = keys[i];
5321 func(k, arr[k], i, len, done);
5322 }
5323 }
5324 }
5325 }
5326
5327 module.exports = {
5328 Frame: Frame,
5329 makeMacro: makeMacro,
5330 makeKeywordArgs: makeKeywordArgs,
5331 numArgs: numArgs,
5332 suppressValue: suppressValue,
5333 ensureDefined: ensureDefined,
5334 memberLookup: memberLookup,
5335 contextOrFrameLookup: contextOrFrameLookup,
5336 callWrap: callWrap,
5337 handleError: handleError,
5338 isArray: lib.isArray,
5339 keys: lib.keys,
5340 SafeString: SafeString,
5341 copySafeness: copySafeness,
5342 markSafe: markSafe,
5343 asyncEach: asyncEach,
5344 asyncAll: asyncAll,
5345 inOperator: lib.inOperator
5346 };
5347
5348
5349/***/ },
5350/* 13 */
5351/***/ function(module, exports, __webpack_require__) {
5352
5353 'use strict';
5354
5355 var lib = __webpack_require__(1);
5356 var r = __webpack_require__(12);
5357
5358 function normalize(value, defaultValue) {
5359 if(value === null || value === undefined || value === false) {
5360 return defaultValue;
5361 }
5362 return value;
5363 }
5364
5365 var filters = {
5366 abs: function(n) {
5367 return Math.abs(n);
5368 },
5369
5370 batch: function(arr, linecount, fill_with) {
5371 var i;
5372 var res = [];
5373 var tmp = [];
5374
5375 for(i = 0; i < arr.length; i++) {
5376 if(i % linecount === 0 && tmp.length) {
5377 res.push(tmp);
5378 tmp = [];
5379 }
5380
5381 tmp.push(arr[i]);
5382 }
5383
5384 if(tmp.length) {
5385 if(fill_with) {
5386 for(i = tmp.length; i < linecount; i++) {
5387 tmp.push(fill_with);
5388 }
5389 }
5390
5391 res.push(tmp);
5392 }
5393
5394 return res;
5395 },
5396
5397 capitalize: function(str) {
5398 str = normalize(str, '');
5399 var ret = str.toLowerCase();
5400 return r.copySafeness(str, ret.charAt(0).toUpperCase() + ret.slice(1));
5401 },
5402
5403 center: function(str, width) {
5404 str = normalize(str, '');
5405 width = width || 80;
5406
5407 if(str.length >= width) {
5408 return str;
5409 }
5410
5411 var spaces = width - str.length;
5412 var pre = lib.repeat(' ', spaces/2 - spaces % 2);
5413 var post = lib.repeat(' ', spaces/2);
5414 return r.copySafeness(str, pre + str + post);
5415 },
5416
5417 'default': function(val, def, bool) {
5418 if(bool) {
5419 return val ? val : def;
5420 }
5421 else {
5422 return (val !== undefined) ? val : def;
5423 }
5424 },
5425
5426 dictsort: function(val, case_sensitive, by) {
5427 if (!lib.isObject(val)) {
5428 throw new lib.TemplateError('dictsort filter: val must be an object');
5429 }
5430
5431 var array = [];
5432 for (var k in val) {
5433 // deliberately include properties from the object's prototype
5434 array.push([k,val[k]]);
5435 }
5436
5437 var si;
5438 if (by === undefined || by === 'key') {
5439 si = 0;
5440 } else if (by === 'value') {
5441 si = 1;
5442 } else {
5443 throw new lib.TemplateError(
5444 'dictsort filter: You can only sort by either key or value');
5445 }
5446
5447 array.sort(function(t1, t2) {
5448 var a = t1[si];
5449 var b = t2[si];
5450
5451 if (!case_sensitive) {
5452 if (lib.isString(a)) {
5453 a = a.toUpperCase();
5454 }
5455 if (lib.isString(b)) {
5456 b = b.toUpperCase();
5457 }
5458 }
5459
5460 return a > b ? 1 : (a === b ? 0 : -1);
5461 });
5462
5463 return array;
5464 },
5465
5466 dump: function(obj) {
5467 return JSON.stringify(obj);
5468 },
5469
5470 escape: function(str) {
5471 if(str instanceof r.SafeString) {
5472 return str;
5473 }
5474 str = (str === null || str === undefined) ? '' : str;
5475 return r.markSafe(lib.escape(str.toString()));
5476 },
5477
5478 safe: function(str) {
5479 if (str instanceof r.SafeString) {
5480 return str;
5481 }
5482 str = (str === null || str === undefined) ? '' : str;
5483 return r.markSafe(str.toString());
5484 },
5485
5486 first: function(arr) {
5487 return arr[0];
5488 },
5489
5490 groupby: function(arr, attr) {
5491 return lib.groupBy(arr, attr);
5492 },
5493
5494 indent: function(str, width, indentfirst) {
5495 str = normalize(str, '');
5496
5497 if (str === '') return '';
5498
5499 width = width || 4;
5500 var res = '';
5501 var lines = str.split('\n');
5502 var sp = lib.repeat(' ', width);
5503
5504 for(var i=0; i<lines.length; i++) {
5505 if(i === 0 && !indentfirst) {
5506 res += lines[i] + '\n';
5507 }
5508 else {
5509 res += sp + lines[i] + '\n';
5510 }
5511 }
5512
5513 return r.copySafeness(str, res);
5514 },
5515
5516 join: function(arr, del, attr) {
5517 del = del || '';
5518
5519 if(attr) {
5520 arr = lib.map(arr, function(v) {
5521 return v[attr];
5522 });
5523 }
5524
5525 return arr.join(del);
5526 },
5527
5528 last: function(arr) {
5529 return arr[arr.length-1];
5530 },
5531
5532 length: function(val) {
5533 var value = normalize(val, '');
5534
5535 if(value !== undefined) {
5536 if(
5537 (typeof Map === 'function' && value instanceof Map) ||
5538 (typeof Set === 'function' && value instanceof Set)
5539 ) {
5540 // ECMAScript 2015 Maps and Sets
5541 return value.size;
5542 }
5543 if(lib.isObject(value) && !(value instanceof r.SafeString)) {
5544 // Objects (besides SafeStrings), non-primative Arrays
5545 return Object.keys(value).length;
5546 }
5547 return value.length;
5548 }
5549 return 0;
5550 },
5551
5552 list: function(val) {
5553 if(lib.isString(val)) {
5554 return val.split('');
5555 }
5556 else if(lib.isObject(val)) {
5557 var keys = [];
5558
5559 if(Object.keys) {
5560 keys = Object.keys(val);
5561 }
5562 else {
5563 for(var k in val) {
5564 keys.push(k);
5565 }
5566 }
5567
5568 return lib.map(keys, function(k) {
5569 return { key: k,
5570 value: val[k] };
5571 });
5572 }
5573 else if(lib.isArray(val)) {
5574 return val;
5575 }
5576 else {
5577 throw new lib.TemplateError('list filter: type not iterable');
5578 }
5579 },
5580
5581 lower: function(str) {
5582 str = normalize(str, '');
5583 return str.toLowerCase();
5584 },
5585
5586 random: function(arr) {
5587 return arr[Math.floor(Math.random() * arr.length)];
5588 },
5589
5590 rejectattr: function(arr, attr) {
5591 return arr.filter(function (item) {
5592 return !item[attr];
5593 });
5594 },
5595
5596 selectattr: function(arr, attr) {
5597 return arr.filter(function (item) {
5598 return !!item[attr];
5599 });
5600 },
5601
5602 replace: function(str, old, new_, maxCount) {
5603 var originalStr = str;
5604
5605 if (old instanceof RegExp) {
5606 return str.replace(old, new_);
5607 }
5608
5609 if(typeof maxCount === 'undefined'){
5610 maxCount = -1;
5611 }
5612
5613 var res = ''; // Output
5614
5615 // Cast Numbers in the search term to string
5616 if(typeof old === 'number'){
5617 old = old + '';
5618 }
5619 else if(typeof old !== 'string') {
5620 // If it is something other than number or string,
5621 // return the original string
5622 return str;
5623 }
5624
5625 // Cast numbers in the replacement to string
5626 if(typeof str === 'number'){
5627 str = str + '';
5628 }
5629
5630 // If by now, we don't have a string, throw it back
5631 if(typeof str !== 'string' && !(str instanceof r.SafeString)){
5632 return str;
5633 }
5634
5635 // ShortCircuits
5636 if(old === ''){
5637 // Mimic the python behaviour: empty string is replaced
5638 // by replacement e.g. "abc"|replace("", ".") -> .a.b.c.
5639 res = new_ + str.split('').join(new_) + new_;
5640 return r.copySafeness(str, res);
5641 }
5642
5643 var nextIndex = str.indexOf(old);
5644 // if # of replacements to perform is 0, or the string to does
5645 // not contain the old value, return the string
5646 if(maxCount === 0 || nextIndex === -1){
5647 return str;
5648 }
5649
5650 var pos = 0;
5651 var count = 0; // # of replacements made
5652
5653 while(nextIndex > -1 && (maxCount === -1 || count < maxCount)){
5654 // Grab the next chunk of src string and add it with the
5655 // replacement, to the result
5656 res += str.substring(pos, nextIndex) + new_;
5657 // Increment our pointer in the src string
5658 pos = nextIndex + old.length;
5659 count++;
5660 // See if there are any more replacements to be made
5661 nextIndex = str.indexOf(old, pos);
5662 }
5663
5664 // We've either reached the end, or done the max # of
5665 // replacements, tack on any remaining string
5666 if(pos < str.length) {
5667 res += str.substring(pos);
5668 }
5669
5670 return r.copySafeness(originalStr, res);
5671 },
5672
5673 reverse: function(val) {
5674 var arr;
5675 if(lib.isString(val)) {
5676 arr = filters.list(val);
5677 }
5678 else {
5679 // Copy it
5680 arr = lib.map(val, function(v) { return v; });
5681 }
5682
5683 arr.reverse();
5684
5685 if(lib.isString(val)) {
5686 return r.copySafeness(val, arr.join(''));
5687 }
5688 return arr;
5689 },
5690
5691 round: function(val, precision, method) {
5692 precision = precision || 0;
5693 var factor = Math.pow(10, precision);
5694 var rounder;
5695
5696 if(method === 'ceil') {
5697 rounder = Math.ceil;
5698 }
5699 else if(method === 'floor') {
5700 rounder = Math.floor;
5701 }
5702 else {
5703 rounder = Math.round;
5704 }
5705
5706 return rounder(val * factor) / factor;
5707 },
5708
5709 slice: function(arr, slices, fillWith) {
5710 var sliceLength = Math.floor(arr.length / slices);
5711 var extra = arr.length % slices;
5712 var offset = 0;
5713 var res = [];
5714
5715 for(var i=0; i<slices; i++) {
5716 var start = offset + i * sliceLength;
5717 if(i < extra) {
5718 offset++;
5719 }
5720 var end = offset + (i + 1) * sliceLength;
5721
5722 var slice = arr.slice(start, end);
5723 if(fillWith && i >= extra) {
5724 slice.push(fillWith);
5725 }
5726 res.push(slice);
5727 }
5728
5729 return res;
5730 },
5731
5732 sum: function(arr, attr, start) {
5733 var sum = 0;
5734
5735 if(typeof start === 'number'){
5736 sum += start;
5737 }
5738
5739 if(attr) {
5740 arr = lib.map(arr, function(v) {
5741 return v[attr];
5742 });
5743 }
5744
5745 for(var i = 0; i < arr.length; i++) {
5746 sum += arr[i];
5747 }
5748
5749 return sum;
5750 },
5751
5752 sort: r.makeMacro(['value', 'reverse', 'case_sensitive', 'attribute'], [], function(arr, reverse, caseSens, attr) {
5753 // Copy it
5754 arr = lib.map(arr, function(v) { return v; });
5755
5756 arr.sort(function(a, b) {
5757 var x, y;
5758
5759 if(attr) {
5760 x = a[attr];
5761 y = b[attr];
5762 }
5763 else {
5764 x = a;
5765 y = b;
5766 }
5767
5768 if(!caseSens && lib.isString(x) && lib.isString(y)) {
5769 x = x.toLowerCase();
5770 y = y.toLowerCase();
5771 }
5772
5773 if(x < y) {
5774 return reverse ? 1 : -1;
5775 }
5776 else if(x > y) {
5777 return reverse ? -1: 1;
5778 }
5779 else {
5780 return 0;
5781 }
5782 });
5783
5784 return arr;
5785 }),
5786
5787 string: function(obj) {
5788 return r.copySafeness(obj, obj);
5789 },
5790
5791 striptags: function(input, preserve_linebreaks) {
5792 input = normalize(input, '');
5793 preserve_linebreaks = preserve_linebreaks || false;
5794 var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi;
5795 var trimmedInput = filters.trim(input.replace(tags, ''));
5796 var res = '';
5797 if (preserve_linebreaks) {
5798 res = trimmedInput
5799 .replace(/^ +| +$/gm, '') // remove leading and trailing spaces
5800 .replace(/ +/g, ' ') // squash adjacent spaces
5801 .replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
5802 .replace(/\n\n\n+/g, '\n\n'); // squash abnormal adjacent linebreaks
5803 } else {
5804 res = trimmedInput.replace(/\s+/gi, ' ');
5805 }
5806 return r.copySafeness(input, res);
5807 },
5808
5809 title: function(str) {
5810 str = normalize(str, '');
5811 var words = str.split(' ');
5812 for(var i = 0; i < words.length; i++) {
5813 words[i] = filters.capitalize(words[i]);
5814 }
5815 return r.copySafeness(str, words.join(' '));
5816 },
5817
5818 trim: function(str) {
5819 return r.copySafeness(str, str.replace(/^\s*|\s*$/g, ''));
5820 },
5821
5822 truncate: function(input, length, killwords, end) {
5823 var orig = input;
5824 input = normalize(input, '');
5825 length = length || 255;
5826
5827 if (input.length <= length)
5828 return input;
5829
5830 if (killwords) {
5831 input = input.substring(0, length);
5832 } else {
5833 var idx = input.lastIndexOf(' ', length);
5834 if(idx === -1) {
5835 idx = length;
5836 }
5837
5838 input = input.substring(0, idx);
5839 }
5840
5841 input += (end !== undefined && end !== null) ? end : '...';
5842 return r.copySafeness(orig, input);
5843 },
5844
5845 upper: function(str) {
5846 str = normalize(str, '');
5847 return str.toUpperCase();
5848 },
5849
5850 urlencode: function(obj) {
5851 var enc = encodeURIComponent;
5852 if (lib.isString(obj)) {
5853 return enc(obj);
5854 } else {
5855 var parts;
5856 if (lib.isArray(obj)) {
5857 parts = obj.map(function(item) {
5858 return enc(item[0]) + '=' + enc(item[1]);
5859 });
5860 } else {
5861 parts = [];
5862 for (var k in obj) {
5863 if (obj.hasOwnProperty(k)) {
5864 parts.push(enc(k) + '=' + enc(obj[k]));
5865 }
5866 }
5867 }
5868 return parts.join('&');
5869 }
5870 },
5871
5872 urlize: function(str, length, nofollow) {
5873 if (isNaN(length)) length = Infinity;
5874
5875 var noFollowAttr = (nofollow === true ? ' rel="nofollow"' : '');
5876
5877 // For the jinja regexp, see
5878 // https://github.com/mitsuhiko/jinja2/blob/f15b814dcba6aa12bc74d1f7d0c881d55f7126be/jinja2/utils.py#L20-L23
5879 var puncRE = /^(?:\(|<|&lt;)?(.*?)(?:\.|,|\)|\n|&gt;)?$/;
5880 // from http://blog.gerv.net/2011/05/html5_email_address_regexp/
5881 var emailRE = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i;
5882 var httpHttpsRE = /^https?:\/\/.*$/;
5883 var wwwRE = /^www\./;
5884 var tldRE = /\.(?:org|net|com)(?:\:|\/|$)/;
5885
5886 var words = str.split(/(\s+)/).filter(function(word) {
5887 // If the word has no length, bail. This can happen for str with
5888 // trailing whitespace.
5889 return word && word.length;
5890 }).map(function(word) {
5891 var matches = word.match(puncRE);
5892 var possibleUrl = matches && matches[1] || word;
5893
5894 // url that starts with http or https
5895 if (httpHttpsRE.test(possibleUrl))
5896 return '<a href="' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
5897
5898 // url that starts with www.
5899 if (wwwRE.test(possibleUrl))
5900 return '<a href="http://' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
5901
5902 // an email address of the form username@domain.tld
5903 if (emailRE.test(possibleUrl))
5904 return '<a href="mailto:' + possibleUrl + '">' + possibleUrl + '</a>';
5905
5906 // url that ends in .com, .org or .net that is not an email address
5907 if (tldRE.test(possibleUrl))
5908 return '<a href="http://' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
5909
5910 return word;
5911
5912 });
5913
5914 return words.join('');
5915 },
5916
5917 wordcount: function(str) {
5918 str = normalize(str, '');
5919 var words = (str) ? str.match(/\w+/g) : null;
5920 return (words) ? words.length : null;
5921 },
5922
5923 'float': function(val, def) {
5924 var res = parseFloat(val);
5925 return isNaN(res) ? def : res;
5926 },
5927
5928 'int': function(val, def) {
5929 var res = parseInt(val, 10);
5930 return isNaN(res) ? def : res;
5931 }
5932 };
5933
5934 // Aliases
5935 filters.d = filters['default'];
5936 filters.e = filters.escape;
5937
5938 module.exports = filters;
5939
5940
5941/***/ },
5942/* 14 */
5943/***/ function(module, exports, __webpack_require__) {
5944
5945 'use strict';
5946
5947 var Loader = __webpack_require__(15);
5948 var PrecompiledLoader = __webpack_require__(16);
5949
5950 var WebLoader = Loader.extend({
5951 init: function(baseURL, opts) {
5952 this.baseURL = baseURL || '.';
5953 opts = opts || {};
5954
5955 // By default, the cache is turned off because there's no way
5956 // to "watch" templates over HTTP, so they are re-downloaded
5957 // and compiled each time. (Remember, PRECOMPILE YOUR
5958 // TEMPLATES in production!)
5959 this.useCache = !!opts.useCache;
5960
5961 // We default `async` to false so that the simple synchronous
5962 // API can be used when you aren't doing anything async in
5963 // your templates (which is most of the time). This performs a
5964 // sync ajax request, but that's ok because it should *only*
5965 // happen in development. PRECOMPILE YOUR TEMPLATES.
5966 this.async = !!opts.async;
5967 },
5968
5969 resolve: function(from, to) { // jshint ignore:line
5970 throw new Error('relative templates not support in the browser yet');
5971 },
5972
5973 getSource: function(name, cb) {
5974 var useCache = this.useCache;
5975 var result;
5976 this.fetch(this.baseURL + '/' + name, function(err, src) {
5977 if(err) {
5978 if(cb) {
5979 cb(err.content);
5980 } else {
5981 if (err.status === 404) {
5982 result = null;
5983 } else {
5984 throw err.content;
5985 }
5986 }
5987 }
5988 else {
5989 result = { src: src,
5990 path: name,
5991 noCache: !useCache };
5992 if(cb) {
5993 cb(null, result);
5994 }
5995 }
5996 });
5997
5998 // if this WebLoader isn't running asynchronously, the
5999 // fetch above would actually run sync and we'll have a
6000 // result here
6001 return result;
6002 },
6003
6004 fetch: function(url, cb) {
6005 // Only in the browser please
6006 var ajax;
6007 var loading = true;
6008
6009 if(window.XMLHttpRequest) { // Mozilla, Safari, ...
6010 ajax = new XMLHttpRequest();
6011 }
6012 else if(window.ActiveXObject) { // IE 8 and older
6013 /* global ActiveXObject */
6014 ajax = new ActiveXObject('Microsoft.XMLHTTP');
6015 }
6016
6017 ajax.onreadystatechange = function() {
6018 if(ajax.readyState === 4 && loading) {
6019 loading = false;
6020 if(ajax.status === 0 || ajax.status === 200) {
6021 cb(null, ajax.responseText);
6022 }
6023 else {
6024 cb({ status: ajax.status, content: ajax.responseText });
6025 }
6026 }
6027 };
6028
6029 url += (url.indexOf('?') === -1 ? '?' : '&') + 's=' +
6030 (new Date().getTime());
6031
6032 ajax.open('GET', url, this.async);
6033 ajax.send();
6034 }
6035 });
6036
6037 module.exports = {
6038 WebLoader: WebLoader,
6039 PrecompiledLoader: PrecompiledLoader
6040 };
6041
6042
6043/***/ },
6044/* 15 */
6045/***/ function(module, exports, __webpack_require__) {
6046
6047 'use strict';
6048
6049 var path = __webpack_require__(3);
6050 var Obj = __webpack_require__(6);
6051 var lib = __webpack_require__(1);
6052
6053 var Loader = Obj.extend({
6054 on: function(name, func) {
6055 this.listeners = this.listeners || {};
6056 this.listeners[name] = this.listeners[name] || [];
6057 this.listeners[name].push(func);
6058 },
6059
6060 emit: function(name /*, arg1, arg2, ...*/) {
6061 var args = Array.prototype.slice.call(arguments, 1);
6062
6063 if(this.listeners && this.listeners[name]) {
6064 lib.each(this.listeners[name], function(listener) {
6065 listener.apply(null, args);
6066 });
6067 }
6068 },
6069
6070 resolve: function(from, to) {
6071 return path.resolve(path.dirname(from), to);
6072 },
6073
6074 isRelative: function(filename) {
6075 return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0);
6076 }
6077 });
6078
6079 module.exports = Loader;
6080
6081
6082/***/ },
6083/* 16 */
6084/***/ function(module, exports, __webpack_require__) {
6085
6086 'use strict';
6087
6088 var Loader = __webpack_require__(15);
6089
6090 var PrecompiledLoader = Loader.extend({
6091 init: function(compiledTemplates) {
6092 this.precompiled = compiledTemplates || {};
6093 },
6094
6095 getSource: function(name) {
6096 if (this.precompiled[name]) {
6097 return {
6098 src: { type: 'code',
6099 obj: this.precompiled[name] },
6100 path: name
6101 };
6102 }
6103 return null;
6104 }
6105 });
6106
6107 module.exports = PrecompiledLoader;
6108
6109
6110/***/ },
6111/* 17 */
6112/***/ function(module, exports) {
6113
6114 'use strict';
6115
6116 function cycler(items) {
6117 var index = -1;
6118
6119 return {
6120 current: null,
6121 reset: function() {
6122 index = -1;
6123 this.current = null;
6124 },
6125
6126 next: function() {
6127 index++;
6128 if(index >= items.length) {
6129 index = 0;
6130 }
6131
6132 this.current = items[index];
6133 return this.current;
6134 },
6135 };
6136
6137 }
6138
6139 function joiner(sep) {
6140 sep = sep || ',';
6141 var first = true;
6142
6143 return function() {
6144 var val = first ? '' : sep;
6145 first = false;
6146 return val;
6147 };
6148 }
6149
6150 // Making this a function instead so it returns a new object
6151 // each time it's called. That way, if something like an environment
6152 // uses it, they will each have their own copy.
6153 function globals() {
6154 return {
6155 range: function(start, stop, step) {
6156 if(typeof stop === 'undefined') {
6157 stop = start;
6158 start = 0;
6159 step = 1;
6160 }
6161 else if(!step) {
6162 step = 1;
6163 }
6164
6165 var arr = [];
6166 var i;
6167 if (step > 0) {
6168 for (i=start; i<stop; i+=step) {
6169 arr.push(i);
6170 }
6171 } else {
6172 for (i=start; i>stop; i+=step) {
6173 arr.push(i);
6174 }
6175 }
6176 return arr;
6177 },
6178
6179 // lipsum: function(n, html, min, max) {
6180 // },
6181
6182 cycler: function() {
6183 return cycler(Array.prototype.slice.call(arguments));
6184 },
6185
6186 joiner: function(sep) {
6187 return joiner(sep);
6188 }
6189 };
6190 }
6191
6192 module.exports = globals;
6193
6194
6195/***/ },
6196/* 18 */
6197/***/ function(module, exports) {
6198
6199 function installCompat() {
6200 'use strict';
6201
6202 // This must be called like `nunjucks.installCompat` so that `this`
6203 // references the nunjucks instance
6204 var runtime = this.runtime; // jshint ignore:line
6205 var lib = this.lib; // jshint ignore:line
6206
6207 var orig_contextOrFrameLookup = runtime.contextOrFrameLookup;
6208 runtime.contextOrFrameLookup = function(context, frame, key) {
6209 var val = orig_contextOrFrameLookup.apply(this, arguments);
6210 if (val === undefined) {
6211 switch (key) {
6212 case 'True':
6213 return true;
6214 case 'False':
6215 return false;
6216 case 'None':
6217 return null;
6218 }
6219 }
6220
6221 return val;
6222 };
6223
6224 var orig_memberLookup = runtime.memberLookup;
6225 var ARRAY_MEMBERS = {
6226 pop: function(index) {
6227 if (index === undefined) {
6228 return this.pop();
6229 }
6230 if (index >= this.length || index < 0) {
6231 throw new Error('KeyError');
6232 }
6233 return this.splice(index, 1);
6234 },
6235 remove: function(element) {
6236 for (var i = 0; i < this.length; i++) {
6237 if (this[i] === element) {
6238 return this.splice(i, 1);
6239 }
6240 }
6241 throw new Error('ValueError');
6242 },
6243 count: function(element) {
6244 var count = 0;
6245 for (var i = 0; i < this.length; i++) {
6246 if (this[i] === element) {
6247 count++;
6248 }
6249 }
6250 return count;
6251 },
6252 index: function(element) {
6253 var i;
6254 if ((i = this.indexOf(element)) === -1) {
6255 throw new Error('ValueError');
6256 }
6257 return i;
6258 },
6259 find: function(element) {
6260 return this.indexOf(element);
6261 },
6262 insert: function(index, elem) {
6263 return this.splice(index, 0, elem);
6264 }
6265 };
6266 var OBJECT_MEMBERS = {
6267 items: function() {
6268 var ret = [];
6269 for(var k in this) {
6270 ret.push([k, this[k]]);
6271 }
6272 return ret;
6273 },
6274 values: function() {
6275 var ret = [];
6276 for(var k in this) {
6277 ret.push(this[k]);
6278 }
6279 return ret;
6280 },
6281 keys: function() {
6282 var ret = [];
6283 for(var k in this) {
6284 ret.push(k);
6285 }
6286 return ret;
6287 },
6288 get: function(key, def) {
6289 var output = this[key];
6290 if (output === undefined) {
6291 output = def;
6292 }
6293 return output;
6294 },
6295 has_key: function(key) {
6296 return this.hasOwnProperty(key);
6297 },
6298 pop: function(key, def) {
6299 var output = this[key];
6300 if (output === undefined && def !== undefined) {
6301 output = def;
6302 } else if (output === undefined) {
6303 throw new Error('KeyError');
6304 } else {
6305 delete this[key];
6306 }
6307 return output;
6308 },
6309 popitem: function() {
6310 for (var k in this) {
6311 // Return the first object pair.
6312 var val = this[k];
6313 delete this[k];
6314 return [k, val];
6315 }
6316 throw new Error('KeyError');
6317 },
6318 setdefault: function(key, def) {
6319 if (key in this) {
6320 return this[key];
6321 }
6322 if (def === undefined) {
6323 def = null;
6324 }
6325 return this[key] = def;
6326 },
6327 update: function(kwargs) {
6328 for (var k in kwargs) {
6329 this[k] = kwargs[k];
6330 }
6331 return null; // Always returns None
6332 }
6333 };
6334 OBJECT_MEMBERS.iteritems = OBJECT_MEMBERS.items;
6335 OBJECT_MEMBERS.itervalues = OBJECT_MEMBERS.values;
6336 OBJECT_MEMBERS.iterkeys = OBJECT_MEMBERS.keys;
6337 runtime.memberLookup = function(obj, val, autoescape) { // jshint ignore:line
6338 obj = obj || {};
6339
6340 // If the object is an object, return any of the methods that Python would
6341 // otherwise provide.
6342 if (lib.isArray(obj) && ARRAY_MEMBERS.hasOwnProperty(val)) {
6343 return function() {return ARRAY_MEMBERS[val].apply(obj, arguments);};
6344 }
6345
6346 if (lib.isObject(obj) && OBJECT_MEMBERS.hasOwnProperty(val)) {
6347 return function() {return OBJECT_MEMBERS[val].apply(obj, arguments);};
6348 }
6349
6350 return orig_memberLookup.apply(this, arguments);
6351 };
6352 }
6353
6354 module.exports = installCompat;
6355
6356
6357/***/ }
6358/******/ ])
6359});
6360;
\No newline at end of file