diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f2a19f2..b4aa2dbe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,103 +1,3 @@
-Changelog for versions previous to v0.1.9 are located at http://nunjucks.tumblr.com/.
+Changelog is available at https://github.com/mozilla/nunjucks/releases
-# v1.0.6 (August 15, 2014)
-
-* Added the `addGlobal` method to the Environment object
-* import/extends/include now can take an arbitrary expression
-* fix bugs in `set`
-* improve express integration (allows rendering templates without an extension)
-
-# v1.0.5 (May 1, 2014)
-
-* Added support for browserify
-* Added option to specify template output path when precompiling templates
-* Keep version comment in browser minified files
-* Speed up SafeString implementation
-* Handle null and non-matching cases for word count filter
-* Added support for node-webkit
-* Other various minor bugfixes
-
-# v1.0.4 (April 4, 2014)
-
-* Use v0.8.2 of chokidar from NPM rather than Github
-
-# v1.0.2 (March 25, 2014)
-
-* Use chokidar for watching file changes. This should fix a lot of problems on OS X machines.
-* Always use `/` in paths when precompiling templates
-* Fix bug where async filters hang indefinitely inside `if` statements
-* Extensions now can override autoescaping with an `autoescape` property
-* Other various minor bugfixes
-
-# v1.0.1 (December 16, 2013)
-
-This is mostly bugfix and code cleanup release. The only added things are:
-
-* New `nunjucks.compile` function which takes a string and returns a `Template` object
-* The `urlize` filter has been added
-
-# v1.0.0 (October 24, 2013)
-
-We've hit 1.0! Thanks for helping nunjucks stabilize and become
-awesome. I've added many good features recently and several people
-have been using them, and everything seems stable. I think it's time
-to cut 1.0.
-
-## Big changes:
-
-* An asynchronous API is now available, and async filters, extensions, and
- loaders is supported. The async API is optional and if you don't do
- anything async (the default), nothing changes for you. You can read
- more about this
- [here](http://jlongster.github.io/nunjucks/api.html#asynchronous-support). (fixes #41)
-* Much simpler higher-level API for initiating/configuring nunjucks is
- available. Read more
- [here](http://jlongster.github.io/nunjucks/api.html#simple-api).
-* An official grunt plugin is available for precompiling templates: [grunt-nunjucks](https://github.com/jlongster/grunt-nunjucks)
-* **The browser files have been renamed**. nunjucks.js is now the full
- library with compiler, and nunjucks-slim.js is the small version
- that only works with precompiled templates
-
-## Smaller changes:
-
-* urlencode filter has been added
-* The express integration has been refactored and isn't a kludge
- anymore. Should avoid some bugs and be more future-proof;
-* The order in which variables are lookup up in the context and frame
- lookup has been reversed. It will now look in the frame first, and
- then the context. This means that if a `for` loop introduces a new
- var, like `{% for name in names %}`, and if you have `name` in the
- context as well, it will properly reference `name` from the for loop
- inside the loop. (fixes #122 and #119)
-
-# v0.1.10 (August 9, 2013)
-
-This is a minor version update that includes several bugfixes.
-
-* fix hang when parsing an unclosed string that hits the end of file (fixes #85)
-* Adds IE8 support
-* `super()` calls are marked safe by default if using autoescaping
-* exposed a `markSafe` function in the runtime module for marking strings as safe inside filters
-* iterating over any "falsey" values will output nothing
-* make "this" be the context object in filters (fixes #109)
-
-# v0.1.9 (May 31, 2013)
-
-* autoescaping ([docs](http://nunjucks.jlongster.com/api#Autoescaping))
-* support for custom tags ([docs](http://nunjucks.jlongster.com/api#Custom-Tags-%2526-Extensions))
-* the API for the `Environment` object changed slightly ([docs](http://nunjucks.jlongster.com/api#new-Environment%28%255Bloaders%255D%252C-%255Boptions%255D%29))
-* tests now use expect.js instead of should.js, can be run [in the browser](http://jlongster.github.io/nunjucks/tests/browser/)!
-* ternary conditional operator added (foo if bar else baz)
-* various optimizations, compilation is now 1.4x faster
-* fix too aggressive caching of templates from HTTP loader
-* truncate filter has been added
-* improve title filter
-* many improvements to error messages
-* add AMD support for precompiled templates
-* fix multiple levels of super (issue #61)
-* support passing a single file to nunjucks-precompile
-* fix usage of `set` in an `if` block
-* fixed passing a false-y value as the last argument to a macro
-* `range`, `cycler`, and `joiner` globals have been added
-* fix nested blocks
-* add bower.json so the client-side lib can be installed through bower
+For versions previous to v0.1.9 are located at http://nunjucks.tumblr.com/.
diff --git a/MAINTENANCE.md b/MAINTENANCE.md
index 3a3cd49a..4f8c6d79 100644
--- a/MAINTENANCE.md
+++ b/MAINTENANCE.md
@@ -5,15 +5,15 @@ Nunjucks attempts to adhere to semantic versioning. The API is very stable, so f
1. Do a `pull` from github to make sure you have all the latest updates
-2. View all the changes and update CHANGELOG.md:
+2. View all the changes since the last version:
```
$ git log --oneline v1.2.3..master
```
-Replace `v1.2.3` with whatever the last version was, and you'll see all the changes going out in this version. Add a new version to CHANGELOG.md and write some notes about what's going out.
+Replace `v1.2.3` with whatever the last version was, and you'll see all the changes going out in this version.
-3. Update the version in `package.json`
+3. Draft a new release and write the changelog in the description, describing the changes in see from #2. The title should be the version.
4. Run the command to make sure the ready-made files for the browser are up-to-date.
@@ -21,17 +21,17 @@ Replace `v1.2.3` with whatever the last version was, and you'll see all the chan
$ make browserfiles
```
-5. Commit above changes and push to `master`
+5. Update the version in `package.json`
-6. Publish to npm:
+6. Commit above changes and push to `master`
+
+7. Publish to npm:
```
npm publish
```
-7. Go to https://github.com/mozilla/nunjucks/releases and click "Draft a new release". Fill out title and copy what you entered in CHANGELOG.md in the description. (CHANGELOG.md could go away I guess with github's release stuff)
-
-8. Make sure docs are up-to-date. If anything, you need to copy all the nunjucks*.js files in `browser/` to the [nunjucks-docs repo](https://github.com/mozilla/nunjucks-docs) in the `files` directory. This is where the "download" link points to in the docs. In `nunjucks-docs`, build the docs:
+8. Make sure docs are up-to-date. You need to at least copy all the nunjucks*.js files in `browser/` to the [nunjucks-docs repo](https://github.com/mozilla/nunjucks-docs) in the `files` directory. This is where the "download" link points to in the docs. In `nunjucks-docs`, build the docs:
```
cd path/to/nunjucks-docs && make prod
diff --git a/browser/nunjucks-slim.js b/browser/nunjucks-slim.js
index 2fc23912..964ceac7 100644
--- a/browser/nunjucks-slim.js
+++ b/browser/nunjucks-slim.js
@@ -1,4 +1,4 @@
-// Browser bundle of nunjucks 1.0.7 (slim, only works with precompiled templates)
+// Browser bundle of nunjucks 1.1.0 (slim, only works with precompiled templates)
(function() {
var modules = {};
@@ -198,7 +198,7 @@ exports.without = function(array) {
contains = exports.toArray(arguments).slice(1);
while(++index < length) {
- if(contains.indexOf(array[index]) === -1) {
+ if(exports.indexOf(contains, array[index]) === -1) {
result.push(array[index]);
}
}
@@ -293,37 +293,35 @@ exports.asyncFor = function(obj, iter, cb) {
next();
};
-if(!Array.prototype.indexOf) {
- Array.prototype.indexOf = function(array, searchElement /*, fromIndex */) {
- if (array == null) {
- throw new TypeError();
- }
- var t = Object(array);
- var len = t.length >>> 0;
- if (len === 0) {
- return -1;
- }
- var n = 0;
- if (arguments.length > 2) {
- n = Number(arguments[2]);
- if (n != n) { // shortcut for verifying if it's NaN
- n = 0;
- } else if (n != 0 && n != Infinity && n != -Infinity) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
- }
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
+exports.indexOf = Array.prototype.indexOf ?
+ function (arr, searchElement, fromIndex) {
+ return Array.prototype.indexOf.call(arr, searchElement, fromIndex);
+ } :
+ function (arr, searchElement, fromIndex) {
+ var length = this.length >>> 0; // Hack to convert object.length to a UInt32
+
+ fromIndex = +fromIndex || 0;
+
+ if(Math.abs(fromIndex) === Infinity) {
+ fromIndex = 0;
}
- if (n >= len) {
- return -1;
+
+ if(fromIndex < 0) {
+ fromIndex += length;
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
}
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
- for (; k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
+
+ for(;fromIndex < length; fromIndex++) {
+ if (arr[fromIndex] === searchElement) {
+ return fromIndex;
}
}
+
return -1;
};
-}
if(!Array.prototype.map) {
Array.prototype.map = function() {
@@ -978,6 +976,10 @@ var filters = {
},
replace: function(str, old, new_, maxCount) {
+ if (old instanceof RegExp) {
+ return str.replace(old, new_);
+ }
+
var res = str;
var last = res;
var count = 1;
@@ -1175,8 +1177,10 @@ var filters = {
}).map(function(word) {
var matches = word.match(puncRE);
+
var possibleUrl = matches && matches[1] || word;
+
// url that starts with http or https
if (httpHttpsRE.test(possibleUrl))
return '' + possibleUrl.substr(0, length) + '';
@@ -1193,7 +1197,7 @@ var filters = {
if (tldRE.test(possibleUrl))
return '' + possibleUrl.substr(0, length) + '';
- return possibleUrl;
+ return word;
});
@@ -1226,12 +1230,12 @@ modules['filters'] = filters;
function cycler(items) {
var index = -1;
- var current = null;
+ this.current = null;
return {
reset: function() {
index = -1;
- current = null;
+ this.current = null;
},
next: function() {
@@ -1240,9 +1244,9 @@ function cycler(items) {
index = 0;
}
- current = items[index];
- return current;
- }
+ this.current = items[index];
+ return this.current;
+ },
};
}
@@ -1312,6 +1316,7 @@ var Environment = Obj.extend({
// defaults to false
opts = opts || {};
this.dev = !!opts.dev;
+ this.lexerTags = opts.tags;
// The autoescape flag sets global autoescaping. If true,
// every string variable will be escaped by default.
@@ -1338,10 +1343,6 @@ var Environment = Obj.extend({
this.extensions = {};
this.extensionsList = [];
- if(opts.tags) {
- lexer.setTags(opts.tags);
- }
-
for(var name in builtin_filters) {
this.addFilter(name, builtin_filters[name]);
}
@@ -1572,7 +1573,7 @@ var Context = Obj.extend({
},
getSuper: function(env, name, block, frame, runtime, cb) {
- var idx = (this.blocks[name] || []).indexOf(block);
+ var idx = lib.indexOf(this.blocks[name] || [], block);
var blk = this.blocks[name][idx + 1];
var context = this;
@@ -1686,7 +1687,8 @@ var Template = Obj.extend({
var source = compiler.compile(this.tmplStr,
this.env.asyncFilters,
this.env.extensionsList,
- this.path);
+ this.path,
+ this.env.lexerTags);
var func = new Function(source);
props = func();
}
diff --git a/browser/nunjucks-slim.min.js b/browser/nunjucks-slim.min.js
index f2fda10e..2ebe580a 100644
--- a/browser/nunjucks-slim.min.js
+++ b/browser/nunjucks-slim.min.js
@@ -1,3 +1,3 @@
-// Browser bundle of nunjucks 1.0.7 (slim, only works with precompiled templates)
+// Browser bundle of nunjucks 1.1.0 (slim, only works with precompiled templates)
-(function(){var modules={};(function(){function extend(cls,name,props){var F=function(){};F.prototype=cls.prototype;var prototype=new F;var fnTest=/xyz/.test(function(){xyz})?/\bparent\b/:/.*/;props=props||{};for(var k in props){var src=props[k];var parent=prototype[k];if(typeof parent=="function"&&typeof src=="function"&&fnTest.test(src)){prototype[k]=function(src,parent){return function(){var tmp=this.parent;this.parent=parent;var res=src.apply(this,arguments);this.parent=tmp;return res}}(src,parent)}else{prototype[k]=src}}prototype.typename=name;var new_cls=function(){if(prototype.init){prototype.init.apply(this,arguments)}};new_cls.prototype=prototype;new_cls.prototype.constructor=new_cls;new_cls.extend=function(name,props){if(typeof name=="object"){props=name;name="anonymous"}return extend(new_cls,name,props)};return new_cls}modules["object"]=extend(Object,"Object",{})})();(function(){var ArrayProto=Array.prototype;var ObjProto=Object.prototype;var escapeMap={"&":"&",'"':""","'":"'","<":"<",">":">"};var escapeRegex=/[&"'<>]/g;var lookupEscape=function(ch){return escapeMap[ch]};var exports=modules["lib"]={};exports.withPrettyErrors=function(path,withInternals,func){try{return func()}catch(e){if(!e.Update){e=new exports.TemplateError(e)}e.Update(path);if(!withInternals){var old=e;e=new Error(old.message);e.name=old.name}throw e}};exports.TemplateError=function(message,lineno,colno){var err=this;if(message instanceof Error){err=message;message=message.name+": "+message.message}else{if(Error.captureStackTrace){Error.captureStackTrace(err)}}err.name="Template render error";err.message=message;err.lineno=lineno;err.colno=colno;err.firstUpdate=true;err.Update=function(path){var message="("+(path||"unknown path")+")";if(this.firstUpdate){if(this.lineno&&this.colno){message+=" [Line "+this.lineno+", Column "+this.colno+"]"}else if(this.lineno){message+=" [Line "+this.lineno+"]"}}message+="\n ";if(this.firstUpdate){message+=" "}this.message=message+(this.message||"");this.firstUpdate=false;return this};return err};exports.TemplateError.prototype=Error.prototype;exports.escape=function(val){return val.replace(escapeRegex,lookupEscape)};exports.isFunction=function(obj){return ObjProto.toString.call(obj)=="[object Function]"};exports.isArray=Array.isArray||function(obj){return ObjProto.toString.call(obj)=="[object Array]"};exports.isString=function(obj){return ObjProto.toString.call(obj)=="[object String]"};exports.isObject=function(obj){return ObjProto.toString.call(obj)=="[object Object]"};exports.groupBy=function(obj,val){var result={};var iterator=exports.isFunction(val)?val:function(obj){return obj[val]};for(var i=0;i>>0;if(len===0){return-1}var n=0;if(arguments.length>2){n=Number(arguments[2]);if(n!=n){n=0}else if(n!=0&&n!=Infinity&&n!=-Infinity){n=(n>0||-1)*Math.floor(Math.abs(n))}}if(n>=len){return-1}var k=n>=0?n:Math.max(len-Math.abs(n),0);for(;kargNames.length){args=Array.prototype.slice.call(arguments,0,argNames.length);var vals=Array.prototype.slice.call(arguments,args.length,argCount);for(var i=0;i=width){return str}var spaces=width-str.length;var pre=lib.repeat(" ",spaces/2-spaces%2);var post=lib.repeat(" ",spaces/2);return r.copySafeness(str,pre+str+post)},"default":function(val,def){return val?val:def},dictsort:function(val,case_sensitive,by){if(!lib.isObject(val)){throw new lib.TemplateError("dictsort filter: val must be an object")}var array=[];for(var k in val){array.push([k,val[k]])}var si;if(by===undefined||by==="key"){si=0}else if(by==="value"){si=1}else{throw new lib.TemplateError("dictsort filter: You can only sort by either key or value")}array.sort(function(t1,t2){var a=t1[si];var b=t2[si];if(!case_sensitive){if(lib.isString(a)){a=a.toUpperCase()}if(lib.isString(b)){b=b.toUpperCase()}}return a>b?1:a==b?0:-1});return array},escape:function(str){if(typeof str=="string"||str instanceof r.SafeString){return lib.escape(str)}return str},safe:function(str){return r.markSafe(str)},first:function(arr){return arr[0]},groupby:function(arr,attr){return lib.groupBy(arr,attr)},indent:function(str,width,indentfirst){width=width||4;var res="";var lines=str.split("\n");var sp=lib.repeat(" ",width);for(var i=0;i=maxCount){break}last=res;res=res.replace(old,new_);count++}return r.copySafeness(str,res)},reverse:function(val){var arr;if(lib.isString(val)){arr=filters.list(val)}else{arr=lib.map(val,function(v){return v})}arr.reverse();if(lib.isString(val)){return r.copySafeness(val,arr.join(""))}return arr},round:function(val,precision,method){precision=precision||0;var factor=Math.pow(10,precision);var rounder;if(method=="ceil"){rounder=Math.ceil}else if(method=="floor"){rounder=Math.floor}else{rounder=Math.round}return rounder(val*factor)/factor},slice:function(arr,slices,fillWith){var sliceLength=Math.floor(arr.length/slices);var extra=arr.length%slices;var offset=0;var res=[];for(var i=0;i=extra){slice.push(fillWith)}res.push(slice)}return res},sort:function(arr,reverse,caseSens,attr){arr=lib.map(arr,function(v){return v});arr.sort(function(a,b){var x,y;if(attr){x=a[attr];y=b[attr]}else{x=a;y=b}if(!caseSens&&lib.isString(x)&&lib.isString(y)){x=x.toLowerCase();y=y.toLowerCase()}if(xy){return reverse?-1:1}else{return 0}});return arr},string:function(obj){return r.copySafeness(obj,obj)},title:function(str){var words=str.split(" ");for(var i=0;i"+possibleUrl.substr(0,length)+"";if(wwwRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";if(emailRE.test(possibleUrl))return''+possibleUrl+"";if(tldRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";return possibleUrl});return words.join(" ")},wordcount:function(str){var words=str?str.match(/\w+/g):null;return words?words.length:null},"float":function(val,def){var res=parseFloat(val);return isNaN(res)?def:res},"int":function(val,def){var res=parseInt(val,10);return isNaN(res)?def:res}};filters.d=filters["default"];filters.e=filters.escape;modules["filters"]=filters})();(function(){function cycler(items){var index=-1;var current=null;return{reset:function(){index=-1;current=null},next:function(){index++;if(index>=items.length){index=0}current=items[index];return current}}}function joiner(sep){sep=sep||",";var first=true;return function(){var val=first?"":sep;first=false;return val}}var globals={range:function(start,stop,step){if(!stop){stop=start;start=0;step=1}else if(!step){step=1}var arr=[];for(var i=start;i":">"};var escapeRegex=/[&"'<>]/g;var lookupEscape=function(ch){return escapeMap[ch]};var exports=modules["lib"]={};exports.withPrettyErrors=function(path,withInternals,func){try{return func()}catch(e){if(!e.Update){e=new exports.TemplateError(e)}e.Update(path);if(!withInternals){var old=e;e=new Error(old.message);e.name=old.name}throw e}};exports.TemplateError=function(message,lineno,colno){var err=this;if(message instanceof Error){err=message;message=message.name+": "+message.message}else{if(Error.captureStackTrace){Error.captureStackTrace(err)}}err.name="Template render error";err.message=message;err.lineno=lineno;err.colno=colno;err.firstUpdate=true;err.Update=function(path){var message="("+(path||"unknown path")+")";if(this.firstUpdate){if(this.lineno&&this.colno){message+=" [Line "+this.lineno+", Column "+this.colno+"]"}else if(this.lineno){message+=" [Line "+this.lineno+"]"}}message+="\n ";if(this.firstUpdate){message+=" "}this.message=message+(this.message||"");this.firstUpdate=false;return this};return err};exports.TemplateError.prototype=Error.prototype;exports.escape=function(val){return val.replace(escapeRegex,lookupEscape)};exports.isFunction=function(obj){return ObjProto.toString.call(obj)=="[object Function]"};exports.isArray=Array.isArray||function(obj){return ObjProto.toString.call(obj)=="[object Array]"};exports.isString=function(obj){return ObjProto.toString.call(obj)=="[object String]"};exports.isObject=function(obj){return ObjProto.toString.call(obj)=="[object Object]"};exports.groupBy=function(obj,val){var result={};var iterator=exports.isFunction(val)?val:function(obj){return obj[val]};for(var i=0;i>>0;fromIndex=+fromIndex||0;if(Math.abs(fromIndex)===Infinity){fromIndex=0}if(fromIndex<0){fromIndex+=length;if(fromIndex<0){fromIndex=0}}for(;fromIndexargNames.length){args=Array.prototype.slice.call(arguments,0,argNames.length);var vals=Array.prototype.slice.call(arguments,args.length,argCount);for(var i=0;i=width){return str}var spaces=width-str.length;var pre=lib.repeat(" ",spaces/2-spaces%2);var post=lib.repeat(" ",spaces/2);return r.copySafeness(str,pre+str+post)},"default":function(val,def){return val?val:def},dictsort:function(val,case_sensitive,by){if(!lib.isObject(val)){throw new lib.TemplateError("dictsort filter: val must be an object")}var array=[];for(var k in val){array.push([k,val[k]])}var si;if(by===undefined||by==="key"){si=0}else if(by==="value"){si=1}else{throw new lib.TemplateError("dictsort filter: You can only sort by either key or value")}array.sort(function(t1,t2){var a=t1[si];var b=t2[si];if(!case_sensitive){if(lib.isString(a)){a=a.toUpperCase()}if(lib.isString(b)){b=b.toUpperCase()}}return a>b?1:a==b?0:-1});return array},escape:function(str){if(typeof str=="string"||str instanceof r.SafeString){return lib.escape(str)}return str},safe:function(str){return r.markSafe(str)},first:function(arr){return arr[0]},groupby:function(arr,attr){return lib.groupBy(arr,attr)},indent:function(str,width,indentfirst){width=width||4;var res="";var lines=str.split("\n");var sp=lib.repeat(" ",width);for(var i=0;i=maxCount){break}last=res;res=res.replace(old,new_);count++}return r.copySafeness(str,res)},reverse:function(val){var arr;if(lib.isString(val)){arr=filters.list(val)}else{arr=lib.map(val,function(v){return v})}arr.reverse();if(lib.isString(val)){return r.copySafeness(val,arr.join(""))}return arr},round:function(val,precision,method){precision=precision||0;var factor=Math.pow(10,precision);var rounder;if(method=="ceil"){rounder=Math.ceil}else if(method=="floor"){rounder=Math.floor}else{rounder=Math.round}return rounder(val*factor)/factor},slice:function(arr,slices,fillWith){var sliceLength=Math.floor(arr.length/slices);var extra=arr.length%slices;var offset=0;var res=[];for(var i=0;i=extra){slice.push(fillWith)}res.push(slice)}return res},sort:function(arr,reverse,caseSens,attr){arr=lib.map(arr,function(v){return v});arr.sort(function(a,b){var x,y;if(attr){x=a[attr];y=b[attr]}else{x=a;y=b}if(!caseSens&&lib.isString(x)&&lib.isString(y)){x=x.toLowerCase();y=y.toLowerCase()}if(xy){return reverse?-1:1}else{return 0}});return arr},string:function(obj){return r.copySafeness(obj,obj)},title:function(str){var words=str.split(" ");for(var i=0;i"+possibleUrl.substr(0,length)+"";if(wwwRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";if(emailRE.test(possibleUrl))return''+possibleUrl+"";if(tldRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";return word});return words.join(" ")},wordcount:function(str){var words=str?str.match(/\w+/g):null;return words?words.length:null},"float":function(val,def){var res=parseFloat(val);return isNaN(res)?def:res},"int":function(val,def){var res=parseInt(val,10);return isNaN(res)?def:res}};filters.d=filters["default"];filters.e=filters.escape;modules["filters"]=filters})();(function(){function cycler(items){var index=-1;this.current=null;return{reset:function(){index=-1;this.current=null},next:function(){index++;if(index>=items.length){index=0}this.current=items[index];return this.current}}}function joiner(sep){sep=sep||",";var first=true;return function(){var val=first?"":sep;first=false;return val}}var globals={range:function(start,stop,step){if(!stop){stop=start;start=0;step=1}else if(!step){step=1}var arr=[];for(var i=start;i>> 0;
- if (len === 0) {
- return -1;
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
+exports.indexOf = Array.prototype.indexOf ?
+ function (arr, searchElement, fromIndex) {
+ return Array.prototype.indexOf.call(arr, searchElement, fromIndex);
+ } :
+ function (arr, searchElement, fromIndex) {
+ var length = this.length >>> 0; // Hack to convert object.length to a UInt32
+
+ fromIndex = +fromIndex || 0;
+
+ if(Math.abs(fromIndex) === Infinity) {
+ fromIndex = 0;
}
- var n = 0;
- if (arguments.length > 2) {
- n = Number(arguments[2]);
- if (n != n) { // shortcut for verifying if it's NaN
- n = 0;
- } else if (n != 0 && n != Infinity && n != -Infinity) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
+
+ if(fromIndex < 0) {
+ fromIndex += length;
+ if (fromIndex < 0) {
+ fromIndex = 0;
}
}
- if (n >= len) {
- return -1;
- }
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
- for (; k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
+
+ for(;fromIndex < length; fromIndex++) {
+ if (arr[fromIndex] === searchElement) {
+ return fromIndex;
}
}
+
return -1;
};
-}
if(!Array.prototype.map) {
Array.prototype.map = function() {
@@ -438,10 +436,11 @@ var LookupVal = Node.extend("LookupVal", { fields: ['target', 'val'] });
var If = Node.extend("If", { fields: ['cond', 'body', 'else_'] });
var IfAsync = If.extend("IfAsync");
var InlineIf = Node.extend("InlineIf", { fields: ['cond', 'body', 'else_'] });
-var For = Node.extend("For", { fields: ['arr', 'name', 'body'] });
+var For = Node.extend("For", { fields: ['arr', 'name', 'body', 'else_'] });
var AsyncEach = For.extend("AsyncEach");
var AsyncAll = For.extend("AsyncAll");
var Macro = Node.extend("Macro", { fields: ['name', 'args', 'body'] });
+var Caller = Macro.extend("Caller");
var Import = Node.extend("Import", { fields: ['template', 'target'] });
var FromImport = Node.extend("FromImport", {
fields: ['template', 'names'],
@@ -468,6 +467,7 @@ var Output = NodeList.extend("Output");
var TemplateData = Literal.extend("TemplateData");
var UnaryOp = Node.extend("UnaryOp", { fields: ['target'] });
var BinOp = Node.extend("BinOp", { fields: ['left', 'right'] });
+var In = BinOp.extend("In");
var Or = BinOp.extend("Or");
var And = BinOp.extend("And");
var Not = UnaryOp.extend("Not");
@@ -614,6 +614,7 @@ modules['nodes'] = {
AsyncEach: AsyncEach,
AsyncAll: AsyncAll,
Macro: Macro,
+ Caller: Caller,
Import: Import,
FromImport: FromImport,
FunCall: FunCall,
@@ -627,6 +628,7 @@ modules['nodes'] = {
Set: Set,
LookupVal: LookupVal,
BinOp: BinOp,
+ In: In,
Or: Or,
And: And,
Not: Not,
@@ -1025,6 +1027,7 @@ var TOKEN_FLOAT = "float";
var TOKEN_BOOLEAN = "boolean";
var TOKEN_SYMBOL = "symbol";
var TOKEN_SPECIAL = "special";
+var TOKEN_REGEX = "regex";
function token(type, value, lineno, colno) {
return {
@@ -1035,7 +1038,7 @@ function token(type, value, lineno, colno) {
};
}
-function Tokenizer(str) {
+function Tokenizer(str, tags) {
this.str = str;
this.index = 0;
this.len = str.length;
@@ -1043,6 +1046,16 @@ function Tokenizer(str) {
this.colno = 0;
this.in_code = false;
+
+ tags = tags || {};
+ this.tags = {
+ BLOCK_START: tags.blockStart || BLOCK_START,
+ BLOCK_END: tags.blockEnd || BLOCK_END,
+ VARIABLE_START: tags.variableStart || VARIABLE_START,
+ VARIABLE_END: tags.variableEnd || VARIABLE_END,
+ COMMENT_START: tags.commentStart || COMMENT_START,
+ COMMENT_END: tags.commentEnd || COMMENT_END
+ };
}
Tokenizer.prototype.nextToken = function() {
@@ -1066,8 +1079,8 @@ Tokenizer.prototype.nextToken = function() {
// We hit some whitespace
return token(TOKEN_WHITESPACE, tok, lineno, colno);
}
- else if((tok = this._extractString(BLOCK_END)) ||
- (tok = this._extractString('-' + BLOCK_END))) {
+ else if((tok = this._extractString(this.tags.BLOCK_END)) ||
+ (tok = this._extractString('-' + this.tags.BLOCK_END))) {
// Special check for the block end tag
//
// It is a requirement that start and end tags are composed of
@@ -1077,11 +1090,43 @@ Tokenizer.prototype.nextToken = function() {
this.in_code = false;
return token(TOKEN_BLOCK_END, tok, lineno, colno);
}
- else if((tok = this._extractString(VARIABLE_END))) {
+ else if((tok = this._extractString(this.tags.VARIABLE_END))) {
// Special check for variable end tag (see above)
this.in_code = false;
return token(TOKEN_VARIABLE_END, tok, lineno, colno);
}
+ else if (cur === 'r' && this.str.charAt(this.index + 1) === '/') {
+ // Skip past 'r/'.
+ this.forwardN(2);
+
+ // Extract until the end of the regex -- / ends it, \/ does not.
+ var regexBody = '';
+ while (!this.is_finished()) {
+ if (this.current() === '/' && this.previous() !== '\\') {
+ this.forward();
+ break;
+ } else {
+ regexBody += this.current();
+ this.forward();
+ }
+ }
+
+ // Check for flags.
+ // The possible flags are according to https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
+ var POSSIBLE_FLAGS = ['g', 'i', 'm', 'y'];
+ var regexFlags = '';
+ while (!this.is_finished()) {
+ var isCurrentAFlag = POSSIBLE_FLAGS.indexOf(this.current()) !== -1;
+ if (isCurrentAFlag) {
+ regexFlags += this.current();
+ this.forward();
+ } else {
+ break;
+ }
+ }
+
+ return token(TOKEN_REGEX, {body: regexBody, flags: regexFlags}, lineno, colno);
+ }
else if(delimChars.indexOf(cur) != -1) {
// We've hit a delimiter (a special char like a bracket)
this.forward();
@@ -1089,7 +1134,7 @@ Tokenizer.prototype.nextToken = function() {
var curComplex = cur + this.current();
var type;
- if(complexOps.indexOf(curComplex) !== -1) {
+ if(lib.indexOf(complexOps, curComplex) !== -1) {
this.forward();
cur = curComplex;
}
@@ -1139,21 +1184,21 @@ Tokenizer.prototype.nextToken = function() {
// Parse out the template text, breaking on tag
// delimiters because we need to look for block/variable start
// tags (don't use the full delimChars for optimization)
- var beginChars = (BLOCK_START.charAt(0) +
- VARIABLE_START.charAt(0) +
- COMMENT_START.charAt(0) +
- COMMENT_END.charAt(0));
+ var beginChars = (this.tags.BLOCK_START.charAt(0) +
+ this.tags.VARIABLE_START.charAt(0) +
+ this.tags.COMMENT_START.charAt(0) +
+ this.tags.COMMENT_END.charAt(0));
var tok;
if(this.is_finished()) {
return null;
}
- else if((tok = this._extractString(BLOCK_START + '-')) ||
- (tok = this._extractString(BLOCK_START))) {
+ else if((tok = this._extractString(this.tags.BLOCK_START + '-')) ||
+ (tok = this._extractString(this.tags.BLOCK_START))) {
this.in_code = true;
return token(TOKEN_BLOCK_START, tok, lineno, colno);
}
- else if((tok = this._extractString(VARIABLE_START))) {
+ else if((tok = this._extractString(this.tags.VARIABLE_START))) {
this.in_code = true;
return token(TOKEN_VARIABLE_START, tok, lineno, colno);
}
@@ -1162,9 +1207,9 @@ Tokenizer.prototype.nextToken = function() {
var data;
var in_comment = false;
- if(this._matches(COMMENT_START)) {
+ if(this._matches(this.tags.COMMENT_START)) {
in_comment = true;
- tok = this._extractString(COMMENT_START);
+ tok = this._extractString(this.tags.COMMENT_START);
}
// Continually consume text, breaking on the tag delimiter
@@ -1176,18 +1221,18 @@ Tokenizer.prototype.nextToken = function() {
while((data = this._extractUntil(beginChars)) !== null) {
tok += data;
- if((this._matches(BLOCK_START) ||
- this._matches(VARIABLE_START) ||
- this._matches(COMMENT_START)) &&
+ if((this._matches(this.tags.BLOCK_START) ||
+ this._matches(this.tags.VARIABLE_START) ||
+ this._matches(this.tags.COMMENT_START)) &&
!in_comment) {
// If it is a start tag, stop looping
break;
}
- else if(this._matches(COMMENT_END)) {
+ else if(this._matches(this.tags.COMMENT_END)) {
if(!in_comment) {
throw new Error("unexpected end of comment");
}
- tok += this._extractString(COMMENT_END);
+ tok += this._extractString(this.tags.COMMENT_END);
break;
}
else {
@@ -1360,17 +1405,8 @@ Tokenizer.prototype.previous = function() {
};
modules['lexer'] = {
- lex: function(src) {
- return new Tokenizer(src);
- },
-
- setTags: function(tags) {
- BLOCK_START = tags.blockStart || BLOCK_START;
- BLOCK_END = tags.blockEnd || BLOCK_END;
- VARIABLE_START = tags.variableStart || VARIABLE_START;
- VARIABLE_END = tags.variableEnd || VARIABLE_END;
- COMMENT_START = tags.commentStart || COMMENT_START;
- COMMENT_END = tags.commentEnd || COMMENT_END;
+ lex: function(src, tags) {
+ return new Tokenizer(src, tags);
},
TOKEN_STRING: TOKEN_STRING,
@@ -1395,7 +1431,8 @@ modules['lexer'] = {
TOKEN_FLOAT: TOKEN_FLOAT,
TOKEN_BOOLEAN: TOKEN_BOOLEAN,
TOKEN_SYMBOL: TOKEN_SYMBOL,
- TOKEN_SPECIAL: TOKEN_SPECIAL
+ TOKEN_SPECIAL: TOKEN_SPECIAL,
+ TOKEN_REGEX: TOKEN_REGEX
};
})();
(function() {
@@ -1582,7 +1619,13 @@ var Parser = Object.extend({
node.arr = this.parseExpression();
this.advanceAfterBlockEnd(forTok.value);
- node.body = this.parseUntilBlocks(endBlock);
+ node.body = this.parseUntilBlocks(endBlock, 'else');
+
+ if(this.skipSymbol('else')) {
+ this.advanceAfterBlockEnd('else');
+ node.else_ = this.parseUntilBlocks(endBlock);
+ }
+
this.advanceAfterBlockEnd();
return node;
@@ -1608,6 +1651,46 @@ var Parser = Object.extend({
return node;
},
+ parseCall: function() {
+ // a call block is parsed as a normal FunCall, but with an added
+ // 'caller' kwarg which is a Caller node.
+ var callTok = this.peekToken();
+ if(!this.skipSymbol('call')) {
+ this.fail("expected call");
+ }
+
+ var callerArgs = this.parseSignature(true) || new nodes.NodeList();
+ var macroCall = this.parsePrimary();
+
+ this.advanceAfterBlockEnd(callTok.value);
+ var body = this.parseUntilBlocks('endcall');
+ this.advanceAfterBlockEnd();
+
+ var callerName = new nodes.Symbol(callTok.lineno,
+ callTok.colno,
+ 'caller');
+ var callerNode = new nodes.Caller(callTok.lineno,
+ callTok.colno,
+ callerName,
+ callerArgs,
+ body);
+
+ // add the additional caller kwarg, adding kwargs if necessary
+ var args = macroCall.args.children;
+ if (!(args[args.length-1] instanceof nodes.KeywordArgs)) {
+ args.push(new nodes.KeywordArgs());
+ }
+ var kwargs = args[args.length - 1];
+ kwargs.addChild(new nodes.Pair(callTok.lineno,
+ callTok.colno,
+ callerName,
+ callerNode));
+
+ return new nodes.Output(callTok.lineno,
+ callTok.colno,
+ [macroCall]);
+ },
+
parseImport: function() {
var importTok = this.peekToken();
if(!this.skipSymbol('import')) {
@@ -1833,7 +1916,7 @@ var Parser = Object.extend({
}
if(this.breakOnBlocks &&
- this.breakOnBlocks.indexOf(tok.value) !== -1) {
+ lib.indexOf(this.breakOnBlocks, tok.value) !== -1) {
return null;
}
@@ -1851,13 +1934,14 @@ var Parser = Object.extend({
case 'include': return this.parseInclude();
case 'set': return this.parseSet();
case 'macro': return this.parseMacro();
+ case 'call': return this.parseCall();
case 'import': return this.parseImport();
case 'from': return this.parseFrom();
default:
if (this.extensions.length) {
for (var i = 0; i < this.extensions.length; i++) {
var ext = this.extensions[i];
- if ((ext.tags || []).indexOf(tok.value) !== -1) {
+ if (lib.indexOf(ext.tags || [], tok.value) !== -1) {
return ext.parse(this, nodes, lexer);
}
}
@@ -1986,15 +2070,15 @@ var Parser = Object.extend({
},
parseInlineIf: function() {
- var node = this.parseOr();
+ var node = this.parseIn();
if(this.skipSymbol('if')) {
- var cond_node = this.parseOr();
+ var cond_node = this.parseIn();
var body_node = node;
node = new nodes.InlineIf(node.lineno, node.colno);
node.body = body_node;
node.cond = cond_node;
if(this.skipSymbol('else')) {
- node.else_ = this.parseOr();
+ node.else_ = this.parseIn();
} else {
node.else_ = null;
}
@@ -2003,6 +2087,36 @@ var Parser = Object.extend({
return node;
},
+ parseIn: function() {
+ var node = this.parseOr();
+ while(1) {
+ // check if the next token is 'not'
+ var tok = this.nextToken();
+ if (!tok) { break; }
+ var invert = tok.type == lexer.TOKEN_SYMBOL && tok.value == 'not';
+ // if it wasn't 'not', put it back
+ if (!invert) { this.pushToken(tok); }
+ if (this.skipSymbol('in')) {
+ var node2 = this.parseOr();
+ node = new nodes.In(node.lineno,
+ node.colno,
+ node,
+ node2);
+ if (invert) {
+ node = new nodes.Not(node.lineno,
+ node.colno,
+ node);
+ }
+ }
+ else {
+ // if we'd found a 'not' but this wasn't an 'in', put back the 'not'
+ if (invert) { this.pushToken(tok); }
+ break;
+ }
+ }
+ return node;
+ },
+
parseOr: function() {
var node = this.parseAnd();
while(this.skipSymbol('or')) {
@@ -2048,27 +2162,12 @@ var Parser = Object.extend({
if(!tok) {
break;
}
- else if(compareOps.indexOf(tok.value) !== -1) {
+ else if(lib.indexOf(compareOps, tok.value) !== -1) {
ops.push(new nodes.CompareOperand(tok.lineno,
tok.colno,
this.parseAdd(),
tok.value));
}
- else if(tok.type == lexer.TOKEN_SYMBOL &&
- tok.value == 'in') {
- ops.push(new nodes.CompareOperand(tok.lineno,
- tok.colno,
- this.parseAdd(),
- 'in'));
- }
- else if(tok.type == lexer.TOKEN_SYMBOL &&
- tok.value == 'not' &&
- this.skipSymbol('in')) {
- ops.push(new nodes.CompareOperand(tok.lineno,
- tok.colno,
- this.parseAdd(),
- 'notin'));
- }
else {
this.pushToken(tok);
break;
@@ -2225,6 +2324,9 @@ var Parser = Object.extend({
tok.colno);
}
}
+ else if (tok.type == lexer.TOKEN_REGEX) {
+ val = new RegExp(tok.value.body, tok.value.flags);
+ }
if(val !== null) {
node = new nodes.Literal(tok.lineno, tok.colno, val);
@@ -2489,14 +2591,13 @@ var Parser = Object.extend({
// console.log(util.inspect(t));
// }
-// var p = new Parser(lexer.lex('hello {% foo %} {{ "hi" | bar }} {% endfoo %} end'));
-// p.extensions = [new FooExtension()];
+// var p = new Parser(lexer.lex('{% if not x %}foo{% endif %}'));
// var n = p.parseAsRoot();
// nodes.printNodes(n);
modules['parser'] = {
- parse: function(src, extensions) {
- var p = new Parser(lexer.lex(src));
+ parse: function(src, extensions, lexerTags) {
+ var p = new Parser(lexer.lex(src, lexerTags));
if (extensions !== undefined) {
p.extensions = extensions;
}
@@ -2506,6 +2607,7 @@ modules['parser'] = {
})();
(function() {
var nodes = modules["nodes"];
+var lib = modules["lib"];
var sym = 0;
function gensym() {
@@ -2600,7 +2702,7 @@ function _liftFilters(node, asyncFilters, prop) {
return node;
}
else if((node instanceof nodes.Filter &&
- asyncFilters.indexOf(node.name.value) !== -1) ||
+ lib.indexOf(asyncFilters, node.name.value) !== -1) ||
node instanceof nodes.CallExtensionAsync) {
var symbol = new nodes.Symbol(node.lineno,
node.colno,
@@ -2714,7 +2816,8 @@ function convertStatements(ast) {
node.colno,
node.arr,
node.name,
- node.body
+ node.body,
+ node.else_
);
}
}
@@ -2910,10 +3013,12 @@ var Compiler = Object.extend({
nodes.Array,
nodes.Dict,
nodes.FunCall,
+ nodes.Caller,
nodes.Filter,
nodes.LookupVal,
nodes.Compare,
nodes.InlineIf,
+ nodes.In,
nodes.And,
nodes.Or,
nodes.Not,
@@ -3104,6 +3209,14 @@ var Compiler = Object.extend({
this.emit(')');
},
+ compileIn: function(node, frame) {
+ this.emit('(');
+ this.compile(node.right, frame);
+ this.emit('.indexOf(');
+ this.compile(node.left, frame);
+ this.emit(') !== -1)');
+ },
+
compileOr: binOpEmitter(' || '),
compileAnd: binOpEmitter(' && '),
compileAdd: binOpEmitter(' + '),
@@ -3312,27 +3425,7 @@ var Compiler = Object.extend({
this.addScopeLevel();
},
- scanLoop: function(node) {
- var loopUses = {};
-
- node.iterFields(function(field) {
- var lookups = field.findAll(nodes.LookupVal);
-
- lib.each(lookups, function(lookup) {
- if (lookup.target instanceof nodes.Symbol &&
- lookup.target.value == 'loop' &&
- lookup.val instanceof nodes.Literal) {
- loopUses[lookup.val.value] = true;
- }
- });
- });
-
- return loopUses;
- },
-
- emitLoopBindings: function(node, loopUses, arr, i, len) {
- len = len || arr + '.length';
-
+ emitLoopBindings: function(node, arr, i, len) {
var bindings = {
index: i + ' + 1',
index0: i,
@@ -3343,10 +3436,8 @@ var Compiler = Object.extend({
length: len
};
- for(var name in bindings) {
- if(name in loopUses) {
- this.emitLine('frame.set("loop.' + name + '", ' + bindings[name] + ');');
- }
+ for (var name in bindings) {
+ this.emitLine('frame.set("loop.' + name + '", ' + bindings[name] + ');');
}
},
@@ -3358,7 +3449,6 @@ var Compiler = Object.extend({
var i = this.tmpid();
var len = this.tmpid();
var arr = this.tmpid();
- var loopUses = this.scanLoop(node);
frame = frame.push();
this.emitLine('frame = frame.push();');
@@ -3378,6 +3468,7 @@ var Compiler = Object.extend({
// body of the loop is duplicated for each condition, but
// we are optimizing for speed over size.
this.emitLine('if(runtime.isArray(' + arr + ')) {'); {
+ this.emitLine('var ' + len + ' = ' + arr + '.length;');
this.emitLine('for(' + i + '=0; ' + i + ' < ' + arr + '.length; '
+ i + '++) {');
@@ -3390,7 +3481,7 @@ var Compiler = Object.extend({
frame.set(node.name.children[u].value, tid);
}
- this.emitLoopBindings(node, loopUses, arr, i);
+ this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
this.compile(node.body, frame);
});
@@ -3407,19 +3498,14 @@ var Compiler = Object.extend({
frame.set(val.value, v);
this.emitLine(i + ' = -1;');
-
- if(loopUses['revindex'] || loopUses['revindex0'] ||
- loopUses['last'] || loopUses['length']) {
- this.emitLine('var ' + len + ' = runtime.keys(' + arr + ').length;');
- }
-
+ this.emitLine('var ' + len + ' = runtime.keys(' + arr + ').length;');
this.emitLine('for(var ' + k + ' in ' + arr + ') {');
this.emitLine(i + '++;');
this.emitLine('var ' + v + ' = ' + arr + '[' + k + '];');
this.emitLine('frame.set("' + key.value + '", ' + k + ');');
this.emitLine('frame.set("' + val.value + '", ' + v + ');');
- this.emitLoopBindings(node, loopUses, arr, i, len);
+ this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
this.compile(node.body, frame);
});
@@ -3433,12 +3519,13 @@ var Compiler = Object.extend({
var v = this.tmpid();
frame.set(node.name.value, v);
+ this.emitLine('var ' + len + ' = ' + arr + '.length;');
this.emitLine('for(var ' + i + '=0; ' + i + ' < ' + arr + '.length; ' +
i + '++) {');
this.emitLine('var ' + v + ' = ' + arr + '[' + i + '];');
this.emitLine('frame.set("' + node.name.value + '", ' + v + ');');
- this.emitLoopBindings(node, loopUses, arr, i);
+ this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
this.compile(node.body, frame);
@@ -3448,6 +3535,12 @@ var Compiler = Object.extend({
}
this.emitLine('}');
+ if (node.else_) {
+ this.emitLine('if (!' + len + ') {');
+ this.compile(node.else_, frame);
+ this.emitLine('}');
+ }
+
this.emitLine('frame = frame.pop();');
},
@@ -3459,7 +3552,6 @@ var Compiler = Object.extend({
var i = this.tmpid();
var len = this.tmpid();
var arr = this.tmpid();
- var loopUses = this.scanLoop(node);
var asyncMethod = parallel ? 'asyncAll' : 'asyncEach';
frame = frame.push();
@@ -3492,7 +3584,7 @@ var Compiler = Object.extend({
frame.set(id, id);
}
- this.emitLoopBindings(node, loopUses, arr, i, len);
+ this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
var buf;
@@ -3517,6 +3609,12 @@ var Compiler = Object.extend({
this.emitLine(this.buffer + ' += ' + output + ';');
}
+ if (node.else_) {
+ this.emitLine('if (!' + arr + '.length) {');
+ this.compile(node.else_, frame);
+ this.emitLine('}');
+ }
+
this.emitLine('frame = frame.pop();');
},
@@ -3528,7 +3626,7 @@ var Compiler = Object.extend({
this._compileAsyncLoop(node, frame, true);
},
- _emitMacroBegin: function(node, frame) {
+ _compileMacro: function(node, frame) {
var args = [];
var kwargs = null;
var funcId = 'macro_' + this.tmpid();
@@ -3557,13 +3655,16 @@ var Compiler = Object.extend({
// arguments so support setting positional args with keywords
// args and passing keyword args as positional args
// (essentially default values). See runtime.js.
+ frame = frame.push();
this.emitLines(
'var ' + funcId + ' = runtime.makeMacro(',
'[' + argNames.join(', ') + '], ',
'[' + kwargNames.join(', ') + '], ',
'function (' + realNames.join(', ') + ') {',
'frame = frame.push();',
- 'kwargs = kwargs || {};'
+ 'kwargs = kwargs || {};',
+ 'if (kwargs.hasOwnProperty("caller")) {',
+ 'frame.set("caller", kwargs.caller); }'
);
// Expose the arguments to the template. Don't need to use
@@ -3587,31 +3688,27 @@ var Compiler = Object.extend({
}, this);
}
- return funcId;
- },
+ var bufferId = this.tmpid();
+ this.pushBufferId(bufferId);
- _emitMacroEnd: function(bufferId) {
+ this.withScopedSyntax(function () {
+ this.compile(node.body, frame);
+ });
+
+ frame = frame.pop();
this.emitLine('frame = frame.pop();');
this.emitLine('return new runtime.SafeString(' + bufferId + ');');
this.emitLine('});');
+ this.popBufferId();
+
+ return funcId;
},
compileMacro: function(node, frame) {
- frame = frame.push();
- var funcId = this._emitMacroBegin(node, frame);
- var id = this.tmpid();
- this.pushBufferId(id);
-
- this.withScopedSyntax(function() {
- this.compile(node.body, frame);
- });
-
- this._emitMacroEnd(id);
- this.popBufferId();
+ var funcId = this._compileMacro(node, frame);
// Expose the macro to the templates
var name = node.name.value;
- frame = frame.pop();
frame.set(name, funcId);
if(frame.parent) {
@@ -3625,6 +3722,13 @@ var Compiler = Object.extend({
}
},
+ compileCaller: function(node, frame) {
+ // basically an anonymous "macro expression"
+ this.emit('(function (){');
+ var funcId = this._compileMacro(node, frame);
+ this.emit('return ' + funcId + ';})()');
+ },
+
compileImport: function(node, frame) {
var id = this.tmpid();
var target = node.target.value;
@@ -3831,15 +3935,15 @@ var Compiler = Object.extend({
});
// var c = new Compiler();
-// var src = '{% macro foo() %}{% include "include.html" %}{% endmacro %} This is my template {{ foo() }}';
+// var src = '{% asyncEach i in arr %}{{ i }}{% else %}empty{% endeach %}';
// var ast = transformer.transform(parser.parse(src));
-//nodes.printNodes(ast);
+// nodes.printNodes(ast);
// c.compile(ast);
// var tmpl = c.getCode();
// console.log(tmpl);
modules['compiler'] = {
- compile: function(src, asyncFilters, extensions, name) {
+ compile: function(src, asyncFilters, extensions, name, lexerTags) {
var c = new Compiler();
// Run the extension preprocessors against the source.
@@ -3851,7 +3955,7 @@ modules['compiler'] = {
}
}
- c.compile(transformer.transform(parser.parse(src, extensions),
+ c.compile(transformer.transform(parser.parse(src, extensions, lexerTags),
asyncFilters,
name));
return c.getCode();
@@ -4050,6 +4154,10 @@ var filters = {
},
replace: function(str, old, new_, maxCount) {
+ if (old instanceof RegExp) {
+ return str.replace(old, new_);
+ }
+
var res = str;
var last = res;
var count = 1;
@@ -4247,8 +4355,10 @@ var filters = {
}).map(function(word) {
var matches = word.match(puncRE);
+
var possibleUrl = matches && matches[1] || word;
+
// url that starts with http or https
if (httpHttpsRE.test(possibleUrl))
return '' + possibleUrl.substr(0, length) + '';
@@ -4265,7 +4375,7 @@ var filters = {
if (tldRE.test(possibleUrl))
return '' + possibleUrl.substr(0, length) + '';
- return possibleUrl;
+ return word;
});
@@ -4298,12 +4408,12 @@ modules['filters'] = filters;
function cycler(items) {
var index = -1;
- var current = null;
+ this.current = null;
return {
reset: function() {
index = -1;
- current = null;
+ this.current = null;
},
next: function() {
@@ -4312,9 +4422,9 @@ function cycler(items) {
index = 0;
}
- current = items[index];
- return current;
- }
+ this.current = items[index];
+ return this.current;
+ },
};
}
@@ -4486,6 +4596,7 @@ var Environment = Obj.extend({
// defaults to false
opts = opts || {};
this.dev = !!opts.dev;
+ this.lexerTags = opts.tags;
// The autoescape flag sets global autoescaping. If true,
// every string variable will be escaped by default.
@@ -4512,10 +4623,6 @@ var Environment = Obj.extend({
this.extensions = {};
this.extensionsList = [];
- if(opts.tags) {
- lexer.setTags(opts.tags);
- }
-
for(var name in builtin_filters) {
this.addFilter(name, builtin_filters[name]);
}
@@ -4746,7 +4853,7 @@ var Context = Obj.extend({
},
getSuper: function(env, name, block, frame, runtime, cb) {
- var idx = (this.blocks[name] || []).indexOf(block);
+ var idx = lib.indexOf(this.blocks[name] || [], block);
var blk = this.blocks[name][idx + 1];
var context = this;
@@ -4860,7 +4967,8 @@ var Template = Obj.extend({
var source = compiler.compile(this.tmplStr,
this.env.asyncFilters,
this.env.extensionsList,
- this.path);
+ this.path,
+ this.env.lexerTags);
var func = new Function(source);
props = func();
}
diff --git a/browser/nunjucks.min.js b/browser/nunjucks.min.js
index 7774ddc4..b976bc3d 100644
--- a/browser/nunjucks.min.js
+++ b/browser/nunjucks.min.js
@@ -1,5 +1,5 @@
-// Browser bundle of nunjucks 1.0.7
+// Browser bundle of nunjucks 1.1.0
-(function(){var modules={};(function(){function extend(cls,name,props){var F=function(){};F.prototype=cls.prototype;var prototype=new F;var fnTest=/xyz/.test(function(){xyz})?/\bparent\b/:/.*/;props=props||{};for(var k in props){var src=props[k];var parent=prototype[k];if(typeof parent=="function"&&typeof src=="function"&&fnTest.test(src)){prototype[k]=function(src,parent){return function(){var tmp=this.parent;this.parent=parent;var res=src.apply(this,arguments);this.parent=tmp;return res}}(src,parent)}else{prototype[k]=src}}prototype.typename=name;var new_cls=function(){if(prototype.init){prototype.init.apply(this,arguments)}};new_cls.prototype=prototype;new_cls.prototype.constructor=new_cls;new_cls.extend=function(name,props){if(typeof name=="object"){props=name;name="anonymous"}return extend(new_cls,name,props)};return new_cls}modules["object"]=extend(Object,"Object",{})})();(function(){var ArrayProto=Array.prototype;var ObjProto=Object.prototype;var escapeMap={"&":"&",'"':""","'":"'","<":"<",">":">"};var escapeRegex=/[&"'<>]/g;var lookupEscape=function(ch){return escapeMap[ch]};var exports=modules["lib"]={};exports.withPrettyErrors=function(path,withInternals,func){try{return func()}catch(e){if(!e.Update){e=new exports.TemplateError(e)}e.Update(path);if(!withInternals){var old=e;e=new Error(old.message);e.name=old.name}throw e}};exports.TemplateError=function(message,lineno,colno){var err=this;if(message instanceof Error){err=message;message=message.name+": "+message.message}else{if(Error.captureStackTrace){Error.captureStackTrace(err)}}err.name="Template render error";err.message=message;err.lineno=lineno;err.colno=colno;err.firstUpdate=true;err.Update=function(path){var message="("+(path||"unknown path")+")";if(this.firstUpdate){if(this.lineno&&this.colno){message+=" [Line "+this.lineno+", Column "+this.colno+"]"}else if(this.lineno){message+=" [Line "+this.lineno+"]"}}message+="\n ";if(this.firstUpdate){message+=" "}this.message=message+(this.message||"");this.firstUpdate=false;return this};return err};exports.TemplateError.prototype=Error.prototype;exports.escape=function(val){return val.replace(escapeRegex,lookupEscape)};exports.isFunction=function(obj){return ObjProto.toString.call(obj)=="[object Function]"};exports.isArray=Array.isArray||function(obj){return ObjProto.toString.call(obj)=="[object Array]"};exports.isString=function(obj){return ObjProto.toString.call(obj)=="[object String]"};exports.isObject=function(obj){return ObjProto.toString.call(obj)=="[object Object]"};exports.groupBy=function(obj,val){var result={};var iterator=exports.isFunction(val)?val:function(obj){return obj[val]};for(var i=0;i>>0;if(len===0){return-1}var n=0;if(arguments.length>2){n=Number(arguments[2]);if(n!=n){n=0}else if(n!=0&&n!=Infinity&&n!=-Infinity){n=(n>0||-1)*Math.floor(Math.abs(n))}}if(n>=len){return-1}var k=n>=0?n:Math.max(len-Math.abs(n),0);for(;k0||!inline){for(var j=0;jargNames.length){args=Array.prototype.slice.call(arguments,0,argNames.length);var vals=Array.prototype.slice.call(arguments,args.length,argCount);for(var i=0;i=","//","**"];var curComplex=cur+this.current();var type;if(complexOps.indexOf(curComplex)!==-1){this.forward();cur=curComplex}switch(cur){case"(":type=TOKEN_LEFT_PAREN;break;case")":type=TOKEN_RIGHT_PAREN;break;case"[":type=TOKEN_LEFT_BRACKET;break;case"]":type=TOKEN_RIGHT_BRACKET;break;case"{":type=TOKEN_LEFT_CURLY;break;case"}":type=TOKEN_RIGHT_CURLY;break;case",":type=TOKEN_COMMA;break;case":":type=TOKEN_COLON;break;case"|":type=TOKEN_PIPE;break;default:type=TOKEN_OPERATOR}return token(type,cur,lineno,colno)}else{tok=this._extractUntil(whitespaceChars+delimChars);if(tok.match(/^[-+]?[0-9]+$/)){if(this.current()=="."){this.forward();var dec=this._extract(intChars);return token(TOKEN_FLOAT,tok+"."+dec,lineno,colno)}else{return token(TOKEN_INT,tok,lineno,colno)}}else if(tok.match(/^(true|false)$/)){return token(TOKEN_BOOLEAN,tok,lineno,colno)}else if(tok){return token(TOKEN_SYMBOL,tok,lineno,colno)}else{throw new Error("Unexpected value while parsing: "+tok)}}}else{var beginChars=BLOCK_START.charAt(0)+VARIABLE_START.charAt(0)+COMMENT_START.charAt(0)+COMMENT_END.charAt(0);var tok;if(this.is_finished()){return null}else if((tok=this._extractString(BLOCK_START+"-"))||(tok=this._extractString(BLOCK_START))){this.in_code=true;return token(TOKEN_BLOCK_START,tok,lineno,colno)}else if(tok=this._extractString(VARIABLE_START)){this.in_code=true;return token(TOKEN_VARIABLE_START,tok,lineno,colno)}else{tok="";var data;var in_comment=false;if(this._matches(COMMENT_START)){in_comment=true;tok=this._extractString(COMMENT_START)}while((data=this._extractUntil(beginChars))!==null){tok+=data;if((this._matches(BLOCK_START)||this._matches(VARIABLE_START)||this._matches(COMMENT_START))&&!in_comment){break}else if(this._matches(COMMENT_END)){if(!in_comment){throw new Error("unexpected end of comment")}tok+=this._extractString(COMMENT_END);break}else{tok+=this.current();this.forward()}}if(data===null&&in_comment){throw new Error("expected end of comment, got end of file")}return token(in_comment?TOKEN_COMMENT:TOKEN_DATA,tok,lineno,colno)}}throw new Error("Could not parse text")};Tokenizer.prototype.parseString=function(delimiter){this.forward();var lineno=this.lineno;var colno=this.colno;var str="";while(!this.is_finished()&&this.current()!=delimiter){var cur=this.current();if(cur=="\\"){this.forward();switch(this.current()){case"n":str+="\n";break;case"t":str+=" ";break;case"r":str+="\r";break;default:str+=this.current()}this.forward()}else{str+=cur;this.forward()}}this.forward();return str};Tokenizer.prototype._matches=function(str){if(this.index+str.length>this.length){return null}var m=this.str.slice(this.index,this.index+str.length);return m==str};Tokenizer.prototype._extractString=function(str){if(this._matches(str)){this.index+=str.length;return str}return null};Tokenizer.prototype._extractUntil=function(charString){return this._extractMatching(true,charString||"")};Tokenizer.prototype._extract=function(charString){return this._extractMatching(false,charString)};Tokenizer.prototype._extractMatching=function(breakOnMatch,charString){if(this.is_finished()){return null}var first=charString.indexOf(this.current());if(breakOnMatch&&first==-1||!breakOnMatch&&first!=-1){var t=this.current();this.forward();var idx=charString.indexOf(this.current());while((breakOnMatch&&idx==-1||!breakOnMatch&&idx!=-1)&&!this.is_finished()){t+=this.current();this.forward();idx=charString.indexOf(this.current())}return t}return""};Tokenizer.prototype.is_finished=function(){return this.index>=this.len};Tokenizer.prototype.forwardN=function(n){for(var i=0;i0&&!this.skip(lexer.TOKEN_COMMA)){this.fail("parseFrom: expected comma",fromTok.lineno,fromTok.colno)}var name=this.parsePrimary();if(name.value.charAt(0)=="_"){this.fail("parseFrom: names starting with an underscore "+"cannot be imported",name.lineno,name.colno)}if(this.skipSymbol("as")){var alias=this.parsePrimary();names.addChild(new nodes.Pair(name.lineno,name.colno,name,alias))}else{names.addChild(name)}}return node},parseBlock:function(){var tag=this.peekToken();if(!this.skipSymbol("block")){this.fail("parseBlock: expected block",tag.lineno,tag.colno)}var node=new nodes.Block(tag.lineno,tag.colno);node.name=this.parsePrimary();if(!(node.name instanceof nodes.Symbol)){this.fail("parseBlock: variable name expected",tag.lineno,tag.colno)}this.advanceAfterBlockEnd(tag.value);node.body=this.parseUntilBlocks("endblock");if(!this.peekToken()){this.fail("parseBlock: expected endblock, got end of file")}this.advanceAfterBlockEnd();return node},parseTemplateRef:function(tagName,nodeType){var tag=this.peekToken();if(!this.skipSymbol(tagName)){this.fail("parseTemplateRef: expected "+tagName)}var node=new nodeType(tag.lineno,tag.colno);node.template=this.parseExpression();this.advanceAfterBlockEnd(tag.value);return node},parseExtends:function(){return this.parseTemplateRef("extends",nodes.Extends)},parseInclude:function(){return this.parseTemplateRef("include",nodes.Include)},parseIf:function(){var tag=this.peekToken();var node;if(this.skipSymbol("if")||this.skipSymbol("elif")){node=new nodes.If(tag.lineno,tag.colno)}else if(this.skipSymbol("ifAsync")){node=new nodes.IfAsync(tag.lineno,tag.colno)}else{this.fail("parseIf: expected if or elif",tag.lineno,tag.colno)}node.cond=this.parseExpression();this.advanceAfterBlockEnd(tag.value);node.body=this.parseUntilBlocks("elif","else","endif");var tok=this.peekToken();switch(tok&&tok.value){case"elif":node.else_=this.parseIf();break;case"else":this.advanceAfterBlockEnd();node.else_=this.parseUntilBlocks("endif");this.advanceAfterBlockEnd();break;case"endif":node.else_=null;this.advanceAfterBlockEnd();break;default:this.fail("parseIf: expected endif, else, or endif, "+"got end of file")}return node},parseSet:function(){var tag=this.peekToken();if(!this.skipSymbol("set")){this.fail("parseSet: expected set",tag.lineno,tag.colno)}var node=new nodes.Set(tag.lineno,tag.colno,[]);var target;while(target=this.parsePrimary()){node.targets.push(target);if(!this.skip(lexer.TOKEN_COMMA)){break}}if(!this.skipValue(lexer.TOKEN_OPERATOR,"=")){this.fail("parseSet: expected = in set tag",tag.lineno,tag.colno)}node.value=this.parseExpression();this.advanceAfterBlockEnd(tag.value);return node},parseStatement:function(){var tok=this.peekToken();var node;if(tok.type!=lexer.TOKEN_SYMBOL){this.fail("tag name expected",tok.lineno,tok.colno)}if(this.breakOnBlocks&&this.breakOnBlocks.indexOf(tok.value)!==-1){return null}switch(tok.value){case"raw":return this.parseRaw();case"if":case"ifAsync":return this.parseIf();case"for":case"asyncEach":case"asyncAll":return this.parseFor();case"block":return this.parseBlock();case"extends":return this.parseExtends();case"include":return this.parseInclude();case"set":return this.parseSet();case"macro":return this.parseMacro();case"import":return this.parseImport();case"from":return this.parseFrom();default:if(this.extensions.length){for(var i=0;i1){this.fail("invalid index")}node=new nodes.LookupVal(tok.lineno,tok.colno,node,lookup.children[0])}else if(tok.type==lexer.TOKEN_OPERATOR&&tok.value=="."){this.nextToken();var val=this.nextToken();if(val.type!=lexer.TOKEN_SYMBOL){this.fail("expected name as lookup value, got "+val.value,val.lineno,val.colno)}var lookup=new nodes.Literal(val.lineno,val.colno,val.value);node=new nodes.LookupVal(tok.lineno,tok.colno,node,lookup)}else{break}tok=this.peekToken()}return node},parseExpression:function(){var node=this.parseInlineIf();return node},parseInlineIf:function(){var node=this.parseOr();
-if(this.skipSymbol("if")){var cond_node=this.parseOr();var body_node=node;node=new nodes.InlineIf(node.lineno,node.colno);node.body=body_node;node.cond=cond_node;if(this.skipSymbol("else")){node.else_=this.parseOr()}else{node.else_=null}}return node},parseOr:function(){var node=this.parseAnd();while(this.skipSymbol("or")){var node2=this.parseAnd();node=new nodes.Or(node.lineno,node.colno,node,node2)}return node},parseAnd:function(){var node=this.parseNot();while(this.skipSymbol("and")){var node2=this.parseNot();node=new nodes.And(node.lineno,node.colno,node,node2)}return node},parseNot:function(){var tok=this.peekToken();if(this.skipSymbol("not")){return new nodes.Not(tok.lineno,tok.colno,this.parseNot())}return this.parseCompare()},parseCompare:function(){var compareOps=["==","!=","<",">","<=",">="];var expr=this.parseAdd();var ops=[];while(1){var tok=this.nextToken();if(!tok){break}else if(compareOps.indexOf(tok.value)!==-1){ops.push(new nodes.CompareOperand(tok.lineno,tok.colno,this.parseAdd(),tok.value))}else if(tok.type==lexer.TOKEN_SYMBOL&&tok.value=="in"){ops.push(new nodes.CompareOperand(tok.lineno,tok.colno,this.parseAdd(),"in"))}else if(tok.type==lexer.TOKEN_SYMBOL&&tok.value=="not"&&this.skipSymbol("in")){ops.push(new nodes.CompareOperand(tok.lineno,tok.colno,this.parseAdd(),"notin"))}else{this.pushToken(tok);break}}if(ops.length){return new nodes.Compare(ops[0].lineno,ops[0].colno,expr,ops)}else{return expr}},parseAdd:function(){var node=this.parseSub();while(this.skipValue(lexer.TOKEN_OPERATOR,"+")){var node2=this.parseSub();node=new nodes.Add(node.lineno,node.colno,node,node2)}return node},parseSub:function(){var node=this.parseMul();while(this.skipValue(lexer.TOKEN_OPERATOR,"-")){var node2=this.parseMul();node=new nodes.Sub(node.lineno,node.colno,node,node2)}return node},parseMul:function(){var node=this.parseDiv();while(this.skipValue(lexer.TOKEN_OPERATOR,"*")){var node2=this.parseDiv();node=new nodes.Mul(node.lineno,node.colno,node,node2)}return node},parseDiv:function(){var node=this.parseFloorDiv();while(this.skipValue(lexer.TOKEN_OPERATOR,"/")){var node2=this.parseFloorDiv();node=new nodes.Div(node.lineno,node.colno,node,node2)}return node},parseFloorDiv:function(){var node=this.parseMod();while(this.skipValue(lexer.TOKEN_OPERATOR,"//")){var node2=this.parseMod();node=new nodes.FloorDiv(node.lineno,node.colno,node,node2)}return node},parseMod:function(){var node=this.parsePow();while(this.skipValue(lexer.TOKEN_OPERATOR,"%")){var node2=this.parsePow();node=new nodes.Mod(node.lineno,node.colno,node,node2)}return node},parsePow:function(){var node=this.parseUnary();while(this.skipValue(lexer.TOKEN_OPERATOR,"**")){var node2=this.parseUnary();node=new nodes.Pow(node.lineno,node.colno,node,node2)}return node},parseUnary:function(noFilters){var tok=this.peekToken();var node;if(this.skipValue(lexer.TOKEN_OPERATOR,"-")){node=new nodes.Neg(tok.lineno,tok.colno,this.parseUnary(true))}else if(this.skipValue(lexer.TOKEN_OPERATOR,"+")){node=new nodes.Pos(tok.lineno,tok.colno,this.parseUnary(true))}else{node=this.parsePrimary()}if(!noFilters){node=this.parseFilter(node)}return node},parsePrimary:function(noPostfix){var tok=this.nextToken();var val=null;var node=null;if(!tok){this.fail("expected expression, got end of file")}else if(tok.type==lexer.TOKEN_STRING){val=tok.value}else if(tok.type==lexer.TOKEN_INT){val=parseInt(tok.value,10)}else if(tok.type==lexer.TOKEN_FLOAT){val=parseFloat(tok.value)}else if(tok.type==lexer.TOKEN_BOOLEAN){if(tok.value=="true"){val=true}else if(tok.value=="false"){val=false}else{this.fail("invalid boolean: "+tok.val,tok.lineno,tok.colno)}}if(val!==null){node=new nodes.Literal(tok.lineno,tok.colno,val)}else if(tok.type==lexer.TOKEN_SYMBOL){node=new nodes.Symbol(tok.lineno,tok.colno,tok.value);if(!noPostfix){node=this.parsePostfix(node)}}else{this.pushToken(tok);node=this.parseAggregate()}if(node){return node}else{this.fail("unexpected token: "+tok.value,tok.lineno,tok.colno)}},parseFilter:function(node){while(this.skip(lexer.TOKEN_PIPE)){var tok=this.expect(lexer.TOKEN_SYMBOL);var name=tok.value;while(this.skipValue(lexer.TOKEN_OPERATOR,".")){name+="."+this.expect(lexer.TOKEN_SYMBOL).value}node=new nodes.Filter(tok.lineno,tok.colno,new nodes.Symbol(tok.lineno,tok.colno,name),new nodes.NodeList(tok.lineno,tok.colno,[node]));if(this.peekToken().type==lexer.TOKEN_LEFT_PAREN){var call=this.parsePostfix(node);node.args.children=node.args.children.concat(call.args.children)}}return node},parseAggregate:function(){var tok=this.nextToken();var node;switch(tok.type){case lexer.TOKEN_LEFT_PAREN:node=new nodes.Group(tok.lineno,tok.colno);break;case lexer.TOKEN_LEFT_BRACKET:node=new nodes.Array(tok.lineno,tok.colno);break;case lexer.TOKEN_LEFT_CURLY:node=new nodes.Dict(tok.lineno,tok.colno);break;default:return null}while(1){var type=this.peekToken().type;if(type==lexer.TOKEN_RIGHT_PAREN||type==lexer.TOKEN_RIGHT_BRACKET||type==lexer.TOKEN_RIGHT_CURLY){this.nextToken();break}if(node.children.length>0){if(!this.skip(lexer.TOKEN_COMMA)){this.fail("parseAggregate: expected comma after expression",tok.lineno,tok.colno)}}if(node instanceof nodes.Dict){var key=this.parsePrimary();if(!this.skip(lexer.TOKEN_COLON)){this.fail("parseAggregate: expected colon after dict key",tok.lineno,tok.colno)}var value=this.parseExpression();node.addChild(new nodes.Pair(key.lineno,key.colno,key,value))}else{var expr=this.parseExpression();node.addChild(expr)}}return node},parseSignature:function(tolerant,noParens){var tok=this.peekToken();if(!noParens&&tok.type!=lexer.TOKEN_LEFT_PAREN){if(tolerant){return null}else{this.fail("expected arguments",tok.lineno,tok.colno)}}if(tok.type==lexer.TOKEN_LEFT_PAREN){tok=this.nextToken()}var args=new nodes.NodeList(tok.lineno,tok.colno);var kwargs=new nodes.KeywordArgs(tok.lineno,tok.colno);var kwnames=[];var checkComma=false;while(1){tok=this.peekToken();if(!noParens&&tok.type==lexer.TOKEN_RIGHT_PAREN){this.nextToken();break}else if(noParens&&tok.type==lexer.TOKEN_BLOCK_END){break}if(checkComma&&!this.skip(lexer.TOKEN_COMMA)){this.fail("parseSignature: expected comma after expression",tok.lineno,tok.colno)}else{var arg=this.parseExpression();if(this.skipValue(lexer.TOKEN_OPERATOR,"=")){kwargs.addChild(new nodes.Pair(arg.lineno,arg.colno,arg,this.parseExpression()))}else{args.addChild(arg)}}checkComma=true}if(kwargs.children.length){args.addChild(kwargs)}return args},parseUntilBlocks:function(){var prev=this.breakOnBlocks;this.breakOnBlocks=lib.toArray(arguments);var ret=this.parse();this.breakOnBlocks=prev;return ret},parseNodes:function(){var tok;var buf=[];while(tok=this.nextToken()){if(tok.type==lexer.TOKEN_DATA){var data=tok.value;var nextToken=this.peekToken();var nextVal=nextToken&&nextToken.value;if(this.dropLeadingWhitespace){data=data.replace(/^\s*/,"");this.dropLeadingWhitespace=false}if(nextToken&&nextToken.type==lexer.TOKEN_BLOCK_START&&nextVal.charAt(nextVal.length-1)=="-"){data=data.replace(/\s*$/,"")}buf.push(new nodes.Output(tok.lineno,tok.colno,[new nodes.TemplateData(tok.lineno,tok.colno,data)]))}else if(tok.type==lexer.TOKEN_BLOCK_START){var n=this.parseStatement();if(!n){break}buf.push(n)}else if(tok.type==lexer.TOKEN_VARIABLE_START){var e=this.parseExpression();this.advanceAfterVariableEnd();buf.push(new nodes.Output(tok.lineno,tok.colno,[e]))}else if(tok.type!=lexer.TOKEN_COMMENT){this.fail("Unexpected token at top-level: "+tok.type,tok.lineno,tok.colno)}}return buf},parse:function(){return new nodes.NodeList(0,0,this.parseNodes())},parseAsRoot:function(){return new nodes.Root(0,0,this.parseNodes())}});modules["parser"]={parse:function(src,extensions){var p=new Parser(lexer.lex(src));if(extensions!==undefined){p.extensions=extensions}return p.parseAsRoot()}}})();(function(){var nodes=modules["nodes"];var sym=0;function gensym(){return"hole_"+sym++}function mapCOW(arr,func){var res=null;for(var i=0;i":">","<=":"<=",">=":">="};function binOpEmitter(str){return function(node,frame){this.compile(node.left,frame);this.emit(str);this.compile(node.right,frame)}}function quotedArray(arr){return"["+lib.map(arr,function(x){return'"'+x+'"'})+"]"}var Compiler=Object.extend({init:function(){this.codebuf=[];this.lastId=0;this.buffer=null;this.bufferStack=[];this.isChild=false;this.scopeClosers=""},fail:function(msg,lineno,colno){if(lineno!==undefined)lineno+=1;if(colno!==undefined)colno+=1;throw new lib.TemplateError(msg,lineno,colno)},pushBufferId:function(id){this.bufferStack.push(this.buffer);this.buffer=id;this.emit("var "+this.buffer+' = "";')},popBufferId:function(){this.buffer=this.bufferStack.pop()},emit:function(code){this.codebuf.push(code)},emitLine:function(code){this.emit(code+"\n")},emitLines:function(){lib.each(lib.toArray(arguments),function(line){this.emitLine(line)},this)},emitFuncBegin:function(name){this.buffer="output";this.scopeClosers="";this.emitLine("function "+name+"(env, context, frame, runtime, cb) {");this.emitLine("var lineno = null;");this.emitLine("var colno = null;");this.emitLine("var "+this.buffer+' = "";');this.emitLine("try {")},emitFuncEnd:function(noReturn){if(!noReturn){this.emitLine("cb(null, "+this.buffer+");")}this.closeScopeLevels();this.emitLine("} catch (e) {");this.emitLine(" cb(runtime.handleError(e, lineno, colno));");this.emitLine("}");this.emitLine("}");this.buffer=null},addScopeLevel:function(){this.scopeClosers+="})"},closeScopeLevels:function(){this.emitLine(this.scopeClosers+";");this.scopeClosers=""},withScopedSyntax:function(func){var scopeClosers=this.scopeClosers;this.scopeClosers="";func.call(this);this.closeScopeLevels();this.scopeClosers=scopeClosers},makeCallback:function(res){var err=this.tmpid();return"function("+err+(res?","+res:"")+") {\n"+"if("+err+") { cb("+err+"); return; }"},tmpid:function(){this.lastId++;return"t_"+this.lastId},_bufferAppend:function(func){this.emit(this.buffer+" += runtime.suppressValue(");func.call(this);this.emit(", env.autoesc);\n")},_compileChildren:function(node,frame){var children=node.children;for(var i=0,l=children.length;i0){this.emit(",")}this.compile(node.children[i],frame)}if(endChar){this.emit(endChar)}},_compileExpression:function(node,frame){this.assertType(node,nodes.Literal,nodes.Symbol,nodes.Group,nodes.Array,nodes.Dict,nodes.FunCall,nodes.Filter,nodes.LookupVal,nodes.Compare,nodes.InlineIf,nodes.And,nodes.Or,nodes.Not,nodes.Add,nodes.Sub,nodes.Mul,nodes.Div,nodes.FloorDiv,nodes.Mod,nodes.Pow,nodes.Neg,nodes.Pos,nodes.Compare,nodes.NodeList);this.compile(node,frame)},assertType:function(node){var types=lib.toArray(arguments).slice(1);var success=false;for(var i=0;i0){this.emit(",")}if(arg){var id=this.tmpid();this.emitLine("function(cb) {");this.emitLine("if(!cb) { cb = function(err) { if(err) { throw err; }}}");this.pushBufferId(id);this.withScopedSyntax(function(){this.compile(arg,frame);this.emitLine("cb(null, "+id+");")});this.popBufferId();this.emitLine("return "+id+";");this.emitLine("}")}else{this.emit("null")}},this)}if(async){var res=this.tmpid();this.emitLine(", "+this.makeCallback(res));this.emitLine(this.buffer+" += runtime.suppressValue("+res+", "+autoescape+" && env.autoesc);");this.addScopeLevel()}else{this.emit(")");this.emit(", "+autoescape+" && env.autoesc);\n")}},compileCallExtensionAsync:function(node,frame){this.compileCallExtension(node,frame,true)},compileNodeList:function(node,frame){this._compileChildren(node,frame)},compileLiteral:function(node,frame){if(typeof node.value=="string"){var val=node.value.replace(/\\/g,"\\\\");val=val.replace(/"/g,'\\"');val=val.replace(/\n/g,"\\n");val=val.replace(/\r/g,"\\r");val=val.replace(/\t/g,"\\t");this.emit('"'+val+'"')}else{this.emit(node.value.toString())}},compileSymbol:function(node,frame){var name=node.value;var v;if(v=frame.lookup(name)){this.emit(v)}else{this.emit("runtime.contextOrFrameLookup("+'context, frame, "'+name+'")')}},compileGroup:function(node,frame){this._compileAggregate(node,frame,"(",")")},compileArray:function(node,frame){this._compileAggregate(node,frame,"[","]")},compileDict:function(node,frame){this._compileAggregate(node,frame,"{","}")},compilePair:function(node,frame){var key=node.key;var val=node.value;if(key instanceof nodes.Symbol){key=new nodes.Literal(key.lineno,key.colno,key.value)}else if(!(key instanceof nodes.Literal&&typeof key.value=="string")){this.fail("compilePair: Dict keys must be strings or names",key.lineno,key.colno)}this.compile(key,frame);this.emit(": ");this._compileExpression(val,frame)},compileInlineIf:function(node,frame){this.emit("(");this.compile(node.cond,frame);this.emit("?");this.compile(node.body,frame);this.emit(":");if(node.else_!==null)this.compile(node.else_,frame);else this.emit('""');this.emit(")")},compileOr:binOpEmitter(" || "),compileAnd:binOpEmitter(" && "),compileAdd:binOpEmitter(" + "),compileSub:binOpEmitter(" - "),compileMul:binOpEmitter(" * "),compileDiv:binOpEmitter(" / "),compileMod:binOpEmitter(" % "),compileNot:function(node,frame){this.emit("!");this.compile(node.target,frame)},compileFloorDiv:function(node,frame){this.emit("Math.floor(");this.compile(node.left,frame);this.emit(" / ");this.compile(node.right,frame);this.emit(")")},compilePow:function(node,frame){this.emit("Math.pow(");this.compile(node.left,frame);this.emit(", ");this.compile(node.right,frame);this.emit(")")},compileNeg:function(node,frame){this.emit("-");this.compile(node.target,frame)},compilePos:function(node,frame){this.emit("+");this.compile(node.target,frame)},compileCompare:function(node,frame){this.compile(node.expr,frame);for(var i=0;i=width){return str}var spaces=width-str.length;var pre=lib.repeat(" ",spaces/2-spaces%2);var post=lib.repeat(" ",spaces/2);return r.copySafeness(str,pre+str+post)},"default":function(val,def){return val?val:def},dictsort:function(val,case_sensitive,by){if(!lib.isObject(val)){throw new lib.TemplateError("dictsort filter: val must be an object")}var array=[];for(var k in val){array.push([k,val[k]])}var si;if(by===undefined||by==="key"){si=0}else if(by==="value"){si=1}else{throw new lib.TemplateError("dictsort filter: You can only sort by either key or value")}array.sort(function(t1,t2){var a=t1[si];var b=t2[si];if(!case_sensitive){if(lib.isString(a)){a=a.toUpperCase()}if(lib.isString(b)){b=b.toUpperCase()}}return a>b?1:a==b?0:-1});return array},escape:function(str){if(typeof str=="string"||str instanceof r.SafeString){return lib.escape(str)}return str},safe:function(str){return r.markSafe(str)},first:function(arr){return arr[0]},groupby:function(arr,attr){return lib.groupBy(arr,attr)},indent:function(str,width,indentfirst){width=width||4;var res="";var lines=str.split("\n");var sp=lib.repeat(" ",width);for(var i=0;i=maxCount){break}last=res;res=res.replace(old,new_);count++}return r.copySafeness(str,res)},reverse:function(val){var arr;if(lib.isString(val)){arr=filters.list(val)}else{arr=lib.map(val,function(v){return v})}arr.reverse();if(lib.isString(val)){return r.copySafeness(val,arr.join(""))}return arr},round:function(val,precision,method){precision=precision||0;var factor=Math.pow(10,precision);var rounder;if(method=="ceil"){rounder=Math.ceil}else if(method=="floor"){rounder=Math.floor}else{rounder=Math.round}return rounder(val*factor)/factor},slice:function(arr,slices,fillWith){var sliceLength=Math.floor(arr.length/slices);var extra=arr.length%slices;var offset=0;var res=[];for(var i=0;i=extra){slice.push(fillWith)}res.push(slice)}return res},sort:function(arr,reverse,caseSens,attr){arr=lib.map(arr,function(v){return v});arr.sort(function(a,b){var x,y;if(attr){x=a[attr];y=b[attr]}else{x=a;y=b}if(!caseSens&&lib.isString(x)&&lib.isString(y)){x=x.toLowerCase();y=y.toLowerCase()}if(xy){return reverse?-1:1}else{return 0}});return arr},string:function(obj){return r.copySafeness(obj,obj)},title:function(str){var words=str.split(" ");for(var i=0;i"+possibleUrl.substr(0,length)+"";if(wwwRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";if(emailRE.test(possibleUrl))return''+possibleUrl+"";if(tldRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";return possibleUrl});return words.join(" ")},wordcount:function(str){var words=str?str.match(/\w+/g):null;return words?words.length:null},"float":function(val,def){var res=parseFloat(val);return isNaN(res)?def:res},"int":function(val,def){var res=parseInt(val,10);return isNaN(res)?def:res}};filters.d=filters["default"];filters.e=filters.escape;modules["filters"]=filters})();(function(){function cycler(items){var index=-1;var current=null;return{reset:function(){index=-1;current=null},next:function(){index++;if(index>=items.length){index=0}current=items[index];return current}}}function joiner(sep){sep=sep||",";var first=true;return function(){var val=first?"":sep;first=false;return val}}var globals={range:function(start,stop,step){if(!stop){stop=start;start=0;step=1}else if(!step){step=1}var arr=[];for(var i=start;i":">"};var escapeRegex=/[&"'<>]/g;var lookupEscape=function(ch){return escapeMap[ch]};var exports=modules["lib"]={};exports.withPrettyErrors=function(path,withInternals,func){try{return func()}catch(e){if(!e.Update){e=new exports.TemplateError(e)}e.Update(path);if(!withInternals){var old=e;e=new Error(old.message);e.name=old.name}throw e}};exports.TemplateError=function(message,lineno,colno){var err=this;if(message instanceof Error){err=message;message=message.name+": "+message.message}else{if(Error.captureStackTrace){Error.captureStackTrace(err)}}err.name="Template render error";err.message=message;err.lineno=lineno;err.colno=colno;err.firstUpdate=true;err.Update=function(path){var message="("+(path||"unknown path")+")";if(this.firstUpdate){if(this.lineno&&this.colno){message+=" [Line "+this.lineno+", Column "+this.colno+"]"}else if(this.lineno){message+=" [Line "+this.lineno+"]"}}message+="\n ";if(this.firstUpdate){message+=" "}this.message=message+(this.message||"");this.firstUpdate=false;return this};return err};exports.TemplateError.prototype=Error.prototype;exports.escape=function(val){return val.replace(escapeRegex,lookupEscape)};exports.isFunction=function(obj){return ObjProto.toString.call(obj)=="[object Function]"};exports.isArray=Array.isArray||function(obj){return ObjProto.toString.call(obj)=="[object Array]"};exports.isString=function(obj){return ObjProto.toString.call(obj)=="[object String]"};exports.isObject=function(obj){return ObjProto.toString.call(obj)=="[object Object]"};exports.groupBy=function(obj,val){var result={};var iterator=exports.isFunction(val)?val:function(obj){return obj[val]};for(var i=0;i>>0;fromIndex=+fromIndex||0;if(Math.abs(fromIndex)===Infinity){fromIndex=0}if(fromIndex<0){fromIndex+=length;if(fromIndex<0){fromIndex=0}}for(;fromIndex0||!inline){for(var j=0;jargNames.length){args=Array.prototype.slice.call(arguments,0,argNames.length);var vals=Array.prototype.slice.call(arguments,args.length,argCount);for(var i=0;i=","//","**"];var curComplex=cur+this.current();var type;if(lib.indexOf(complexOps,curComplex)!==-1){this.forward();cur=curComplex}switch(cur){case"(":type=TOKEN_LEFT_PAREN;break;case")":type=TOKEN_RIGHT_PAREN;break;case"[":type=TOKEN_LEFT_BRACKET;break;case"]":type=TOKEN_RIGHT_BRACKET;break;case"{":type=TOKEN_LEFT_CURLY;break;case"}":type=TOKEN_RIGHT_CURLY;break;case",":type=TOKEN_COMMA;break;case":":type=TOKEN_COLON;break;case"|":type=TOKEN_PIPE;break;default:type=TOKEN_OPERATOR}return token(type,cur,lineno,colno)}else{tok=this._extractUntil(whitespaceChars+delimChars);if(tok.match(/^[-+]?[0-9]+$/)){if(this.current()=="."){this.forward();var dec=this._extract(intChars);return token(TOKEN_FLOAT,tok+"."+dec,lineno,colno)}else{return token(TOKEN_INT,tok,lineno,colno)}}else if(tok.match(/^(true|false)$/)){return token(TOKEN_BOOLEAN,tok,lineno,colno)}else if(tok){return token(TOKEN_SYMBOL,tok,lineno,colno)}else{throw new Error("Unexpected value while parsing: "+tok)}}}else{var beginChars=this.tags.BLOCK_START.charAt(0)+this.tags.VARIABLE_START.charAt(0)+this.tags.COMMENT_START.charAt(0)+this.tags.COMMENT_END.charAt(0);var tok;if(this.is_finished()){return null}else if((tok=this._extractString(this.tags.BLOCK_START+"-"))||(tok=this._extractString(this.tags.BLOCK_START))){this.in_code=true;return token(TOKEN_BLOCK_START,tok,lineno,colno)}else if(tok=this._extractString(this.tags.VARIABLE_START)){this.in_code=true;return token(TOKEN_VARIABLE_START,tok,lineno,colno)}else{tok="";var data;var in_comment=false;if(this._matches(this.tags.COMMENT_START)){in_comment=true;tok=this._extractString(this.tags.COMMENT_START)}while((data=this._extractUntil(beginChars))!==null){tok+=data;if((this._matches(this.tags.BLOCK_START)||this._matches(this.tags.VARIABLE_START)||this._matches(this.tags.COMMENT_START))&&!in_comment){break}else if(this._matches(this.tags.COMMENT_END)){if(!in_comment){throw new Error("unexpected end of comment")}tok+=this._extractString(this.tags.COMMENT_END);break}else{tok+=this.current();this.forward()}}if(data===null&&in_comment){throw new Error("expected end of comment, got end of file")}return token(in_comment?TOKEN_COMMENT:TOKEN_DATA,tok,lineno,colno)}}throw new Error("Could not parse text")};Tokenizer.prototype.parseString=function(delimiter){this.forward();var lineno=this.lineno;var colno=this.colno;var str="";while(!this.is_finished()&&this.current()!=delimiter){var cur=this.current();if(cur=="\\"){this.forward();switch(this.current()){case"n":str+="\n";break;case"t":str+=" ";break;case"r":str+="\r";break;default:str+=this.current()}this.forward()}else{str+=cur;this.forward()}}this.forward();return str};Tokenizer.prototype._matches=function(str){if(this.index+str.length>this.length){return null}var m=this.str.slice(this.index,this.index+str.length);return m==str};Tokenizer.prototype._extractString=function(str){if(this._matches(str)){this.index+=str.length;return str}return null};Tokenizer.prototype._extractUntil=function(charString){return this._extractMatching(true,charString||"")};Tokenizer.prototype._extract=function(charString){return this._extractMatching(false,charString)};Tokenizer.prototype._extractMatching=function(breakOnMatch,charString){if(this.is_finished()){return null}var first=charString.indexOf(this.current());if(breakOnMatch&&first==-1||!breakOnMatch&&first!=-1){var t=this.current();this.forward();var idx=charString.indexOf(this.current());while((breakOnMatch&&idx==-1||!breakOnMatch&&idx!=-1)&&!this.is_finished()){t+=this.current();this.forward();idx=charString.indexOf(this.current())}return t}return""};Tokenizer.prototype.is_finished=function(){return this.index>=this.len};Tokenizer.prototype.forwardN=function(n){for(var i=0;i0&&!this.skip(lexer.TOKEN_COMMA)){this.fail("parseFrom: expected comma",fromTok.lineno,fromTok.colno)}var name=this.parsePrimary();if(name.value.charAt(0)=="_"){this.fail("parseFrom: names starting with an underscore "+"cannot be imported",name.lineno,name.colno)}if(this.skipSymbol("as")){var alias=this.parsePrimary();names.addChild(new nodes.Pair(name.lineno,name.colno,name,alias))}else{names.addChild(name)}}return node},parseBlock:function(){var tag=this.peekToken();if(!this.skipSymbol("block")){this.fail("parseBlock: expected block",tag.lineno,tag.colno)}var node=new nodes.Block(tag.lineno,tag.colno);node.name=this.parsePrimary();if(!(node.name instanceof nodes.Symbol)){this.fail("parseBlock: variable name expected",tag.lineno,tag.colno)}this.advanceAfterBlockEnd(tag.value);node.body=this.parseUntilBlocks("endblock");if(!this.peekToken()){this.fail("parseBlock: expected endblock, got end of file")}this.advanceAfterBlockEnd();return node},parseTemplateRef:function(tagName,nodeType){var tag=this.peekToken();if(!this.skipSymbol(tagName)){this.fail("parseTemplateRef: expected "+tagName)}var node=new nodeType(tag.lineno,tag.colno);node.template=this.parseExpression();this.advanceAfterBlockEnd(tag.value);return node},parseExtends:function(){return this.parseTemplateRef("extends",nodes.Extends)},parseInclude:function(){return this.parseTemplateRef("include",nodes.Include)},parseIf:function(){var tag=this.peekToken();var node;if(this.skipSymbol("if")||this.skipSymbol("elif")){node=new nodes.If(tag.lineno,tag.colno)}else if(this.skipSymbol("ifAsync")){node=new nodes.IfAsync(tag.lineno,tag.colno)}else{this.fail("parseIf: expected if or elif",tag.lineno,tag.colno)}node.cond=this.parseExpression();this.advanceAfterBlockEnd(tag.value);node.body=this.parseUntilBlocks("elif","else","endif");var tok=this.peekToken();switch(tok&&tok.value){case"elif":node.else_=this.parseIf();break;case"else":this.advanceAfterBlockEnd();node.else_=this.parseUntilBlocks("endif");this.advanceAfterBlockEnd();break;case"endif":node.else_=null;this.advanceAfterBlockEnd();break;default:this.fail("parseIf: expected endif, else, or endif, "+"got end of file")}return node},parseSet:function(){var tag=this.peekToken();if(!this.skipSymbol("set")){this.fail("parseSet: expected set",tag.lineno,tag.colno)}var node=new nodes.Set(tag.lineno,tag.colno,[]);var target;while(target=this.parsePrimary()){node.targets.push(target);if(!this.skip(lexer.TOKEN_COMMA)){break}}if(!this.skipValue(lexer.TOKEN_OPERATOR,"=")){this.fail("parseSet: expected = in set tag",tag.lineno,tag.colno)}node.value=this.parseExpression();this.advanceAfterBlockEnd(tag.value);return node},parseStatement:function(){var tok=this.peekToken();var node;if(tok.type!=lexer.TOKEN_SYMBOL){this.fail("tag name expected",tok.lineno,tok.colno)}if(this.breakOnBlocks&&lib.indexOf(this.breakOnBlocks,tok.value)!==-1){return null}switch(tok.value){case"raw":return this.parseRaw();case"if":case"ifAsync":return this.parseIf();case"for":case"asyncEach":case"asyncAll":return this.parseFor();case"block":return this.parseBlock();case"extends":return this.parseExtends();case"include":return this.parseInclude();case"set":return this.parseSet();case"macro":return this.parseMacro();case"call":return this.parseCall();case"import":return this.parseImport();case"from":return this.parseFrom();default:if(this.extensions.length){for(var i=0;i1){this.fail("invalid index")}node=new nodes.LookupVal(tok.lineno,tok.colno,node,lookup.children[0])}else if(tok.type==lexer.TOKEN_OPERATOR&&tok.value=="."){this.nextToken();var val=this.nextToken();if(val.type!=lexer.TOKEN_SYMBOL){this.fail("expected name as lookup value, got "+val.value,val.lineno,val.colno)}var lookup=new nodes.Literal(val.lineno,val.colno,val.value);node=new nodes.LookupVal(tok.lineno,tok.colno,node,lookup)}else{break}tok=this.peekToken()}return node},parseExpression:function(){var node=this.parseInlineIf();return node},parseInlineIf:function(){var node=this.parseIn();if(this.skipSymbol("if")){var cond_node=this.parseIn();var body_node=node;node=new nodes.InlineIf(node.lineno,node.colno);node.body=body_node;node.cond=cond_node;if(this.skipSymbol("else")){node.else_=this.parseIn()}else{node.else_=null}}return node},parseIn:function(){var node=this.parseOr();while(1){var tok=this.nextToken();if(!tok){break}var invert=tok.type==lexer.TOKEN_SYMBOL&&tok.value=="not";if(!invert){this.pushToken(tok)}if(this.skipSymbol("in")){var node2=this.parseOr();node=new nodes.In(node.lineno,node.colno,node,node2);if(invert){node=new nodes.Not(node.lineno,node.colno,node)}}else{if(invert){this.pushToken(tok)}break}}return node},parseOr:function(){var node=this.parseAnd();while(this.skipSymbol("or")){var node2=this.parseAnd();node=new nodes.Or(node.lineno,node.colno,node,node2)}return node},parseAnd:function(){var node=this.parseNot();while(this.skipSymbol("and")){var node2=this.parseNot();node=new nodes.And(node.lineno,node.colno,node,node2)}return node},parseNot:function(){var tok=this.peekToken();if(this.skipSymbol("not")){return new nodes.Not(tok.lineno,tok.colno,this.parseNot())}return this.parseCompare()},parseCompare:function(){var compareOps=["==","!=","<",">","<=",">="];var expr=this.parseAdd();var ops=[];while(1){var tok=this.nextToken();if(!tok){break}else if(lib.indexOf(compareOps,tok.value)!==-1){ops.push(new nodes.CompareOperand(tok.lineno,tok.colno,this.parseAdd(),tok.value))}else{this.pushToken(tok);break}}if(ops.length){return new nodes.Compare(ops[0].lineno,ops[0].colno,expr,ops)}else{return expr}},parseAdd:function(){var node=this.parseSub();while(this.skipValue(lexer.TOKEN_OPERATOR,"+")){var node2=this.parseSub();node=new nodes.Add(node.lineno,node.colno,node,node2)}return node},parseSub:function(){var node=this.parseMul();while(this.skipValue(lexer.TOKEN_OPERATOR,"-")){var node2=this.parseMul();node=new nodes.Sub(node.lineno,node.colno,node,node2)}return node},parseMul:function(){var node=this.parseDiv();while(this.skipValue(lexer.TOKEN_OPERATOR,"*")){var node2=this.parseDiv();node=new nodes.Mul(node.lineno,node.colno,node,node2)}return node},parseDiv:function(){var node=this.parseFloorDiv();while(this.skipValue(lexer.TOKEN_OPERATOR,"/")){var node2=this.parseFloorDiv();node=new nodes.Div(node.lineno,node.colno,node,node2)}return node},parseFloorDiv:function(){var node=this.parseMod();while(this.skipValue(lexer.TOKEN_OPERATOR,"//")){var node2=this.parseMod();node=new nodes.FloorDiv(node.lineno,node.colno,node,node2)}return node},parseMod:function(){var node=this.parsePow();while(this.skipValue(lexer.TOKEN_OPERATOR,"%")){var node2=this.parsePow();node=new nodes.Mod(node.lineno,node.colno,node,node2)}return node},parsePow:function(){var node=this.parseUnary();while(this.skipValue(lexer.TOKEN_OPERATOR,"**")){var node2=this.parseUnary();node=new nodes.Pow(node.lineno,node.colno,node,node2)}return node},parseUnary:function(noFilters){var tok=this.peekToken();var node;if(this.skipValue(lexer.TOKEN_OPERATOR,"-")){node=new nodes.Neg(tok.lineno,tok.colno,this.parseUnary(true))}else if(this.skipValue(lexer.TOKEN_OPERATOR,"+")){node=new nodes.Pos(tok.lineno,tok.colno,this.parseUnary(true))}else{node=this.parsePrimary()}if(!noFilters){node=this.parseFilter(node)}return node},parsePrimary:function(noPostfix){var tok=this.nextToken();var val=null;var node=null;if(!tok){this.fail("expected expression, got end of file")}else if(tok.type==lexer.TOKEN_STRING){val=tok.value}else if(tok.type==lexer.TOKEN_INT){val=parseInt(tok.value,10)}else if(tok.type==lexer.TOKEN_FLOAT){val=parseFloat(tok.value)}else if(tok.type==lexer.TOKEN_BOOLEAN){if(tok.value=="true"){val=true}else if(tok.value=="false"){val=false}else{this.fail("invalid boolean: "+tok.val,tok.lineno,tok.colno)}}else if(tok.type==lexer.TOKEN_REGEX){val=new RegExp(tok.value.body,tok.value.flags)}if(val!==null){node=new nodes.Literal(tok.lineno,tok.colno,val)}else if(tok.type==lexer.TOKEN_SYMBOL){node=new nodes.Symbol(tok.lineno,tok.colno,tok.value);if(!noPostfix){node=this.parsePostfix(node)}}else{this.pushToken(tok);node=this.parseAggregate()}if(node){return node}else{this.fail("unexpected token: "+tok.value,tok.lineno,tok.colno)}},parseFilter:function(node){while(this.skip(lexer.TOKEN_PIPE)){var tok=this.expect(lexer.TOKEN_SYMBOL);var name=tok.value;while(this.skipValue(lexer.TOKEN_OPERATOR,".")){name+="."+this.expect(lexer.TOKEN_SYMBOL).value}node=new nodes.Filter(tok.lineno,tok.colno,new nodes.Symbol(tok.lineno,tok.colno,name),new nodes.NodeList(tok.lineno,tok.colno,[node]));if(this.peekToken().type==lexer.TOKEN_LEFT_PAREN){var call=this.parsePostfix(node);node.args.children=node.args.children.concat(call.args.children)}}return node},parseAggregate:function(){var tok=this.nextToken();var node;switch(tok.type){case lexer.TOKEN_LEFT_PAREN:node=new nodes.Group(tok.lineno,tok.colno);break;case lexer.TOKEN_LEFT_BRACKET:node=new nodes.Array(tok.lineno,tok.colno);break;case lexer.TOKEN_LEFT_CURLY:node=new nodes.Dict(tok.lineno,tok.colno);break;default:return null}while(1){var type=this.peekToken().type;if(type==lexer.TOKEN_RIGHT_PAREN||type==lexer.TOKEN_RIGHT_BRACKET||type==lexer.TOKEN_RIGHT_CURLY){this.nextToken();break}if(node.children.length>0){if(!this.skip(lexer.TOKEN_COMMA)){this.fail("parseAggregate: expected comma after expression",tok.lineno,tok.colno)}}if(node instanceof nodes.Dict){var key=this.parsePrimary();if(!this.skip(lexer.TOKEN_COLON)){this.fail("parseAggregate: expected colon after dict key",tok.lineno,tok.colno)}var value=this.parseExpression();node.addChild(new nodes.Pair(key.lineno,key.colno,key,value))}else{var expr=this.parseExpression();node.addChild(expr)}}return node},parseSignature:function(tolerant,noParens){var tok=this.peekToken();if(!noParens&&tok.type!=lexer.TOKEN_LEFT_PAREN){if(tolerant){return null}else{this.fail("expected arguments",tok.lineno,tok.colno)}}if(tok.type==lexer.TOKEN_LEFT_PAREN){tok=this.nextToken()}var args=new nodes.NodeList(tok.lineno,tok.colno);var kwargs=new nodes.KeywordArgs(tok.lineno,tok.colno);var kwnames=[];var checkComma=false;while(1){tok=this.peekToken();if(!noParens&&tok.type==lexer.TOKEN_RIGHT_PAREN){this.nextToken();break}else if(noParens&&tok.type==lexer.TOKEN_BLOCK_END){break}if(checkComma&&!this.skip(lexer.TOKEN_COMMA)){this.fail("parseSignature: expected comma after expression",tok.lineno,tok.colno)}else{var arg=this.parseExpression();if(this.skipValue(lexer.TOKEN_OPERATOR,"=")){kwargs.addChild(new nodes.Pair(arg.lineno,arg.colno,arg,this.parseExpression()))}else{args.addChild(arg)}}checkComma=true}if(kwargs.children.length){args.addChild(kwargs)}return args},parseUntilBlocks:function(){var prev=this.breakOnBlocks;this.breakOnBlocks=lib.toArray(arguments);var ret=this.parse();this.breakOnBlocks=prev;return ret},parseNodes:function(){var tok;var buf=[];while(tok=this.nextToken()){if(tok.type==lexer.TOKEN_DATA){var data=tok.value;var nextToken=this.peekToken();var nextVal=nextToken&&nextToken.value;if(this.dropLeadingWhitespace){data=data.replace(/^\s*/,"");this.dropLeadingWhitespace=false}if(nextToken&&nextToken.type==lexer.TOKEN_BLOCK_START&&nextVal.charAt(nextVal.length-1)=="-"){data=data.replace(/\s*$/,"")}buf.push(new nodes.Output(tok.lineno,tok.colno,[new nodes.TemplateData(tok.lineno,tok.colno,data)]))}else if(tok.type==lexer.TOKEN_BLOCK_START){var n=this.parseStatement();if(!n){break}buf.push(n)}else if(tok.type==lexer.TOKEN_VARIABLE_START){var e=this.parseExpression();this.advanceAfterVariableEnd();buf.push(new nodes.Output(tok.lineno,tok.colno,[e]))}else if(tok.type!=lexer.TOKEN_COMMENT){this.fail("Unexpected token at top-level: "+tok.type,tok.lineno,tok.colno)}}return buf},parse:function(){return new nodes.NodeList(0,0,this.parseNodes())},parseAsRoot:function(){return new nodes.Root(0,0,this.parseNodes())}});modules["parser"]={parse:function(src,extensions,lexerTags){var p=new Parser(lexer.lex(src,lexerTags));if(extensions!==undefined){p.extensions=extensions}return p.parseAsRoot()}}})();(function(){var nodes=modules["nodes"];var lib=modules["lib"];var sym=0;function gensym(){return"hole_"+sym++}function mapCOW(arr,func){var res=null;for(var i=0;i":">","<=":"<=",">=":">="};function binOpEmitter(str){return function(node,frame){this.compile(node.left,frame);this.emit(str);this.compile(node.right,frame)}}function quotedArray(arr){return"["+lib.map(arr,function(x){return'"'+x+'"'})+"]"}var Compiler=Object.extend({init:function(){this.codebuf=[];this.lastId=0;this.buffer=null;this.bufferStack=[];this.isChild=false;this.scopeClosers=""},fail:function(msg,lineno,colno){if(lineno!==undefined)lineno+=1;if(colno!==undefined)colno+=1;throw new lib.TemplateError(msg,lineno,colno)},pushBufferId:function(id){this.bufferStack.push(this.buffer);this.buffer=id;this.emit("var "+this.buffer+' = "";')},popBufferId:function(){this.buffer=this.bufferStack.pop()},emit:function(code){this.codebuf.push(code)},emitLine:function(code){this.emit(code+"\n")},emitLines:function(){lib.each(lib.toArray(arguments),function(line){this.emitLine(line)},this)},emitFuncBegin:function(name){this.buffer="output";this.scopeClosers="";this.emitLine("function "+name+"(env, context, frame, runtime, cb) {");this.emitLine("var lineno = null;");this.emitLine("var colno = null;");this.emitLine("var "+this.buffer+' = "";');this.emitLine("try {")},emitFuncEnd:function(noReturn){if(!noReturn){this.emitLine("cb(null, "+this.buffer+");")}this.closeScopeLevels();this.emitLine("} catch (e) {");this.emitLine(" cb(runtime.handleError(e, lineno, colno));");this.emitLine("}");this.emitLine("}");this.buffer=null},addScopeLevel:function(){this.scopeClosers+="})"},closeScopeLevels:function(){this.emitLine(this.scopeClosers+";");this.scopeClosers=""},withScopedSyntax:function(func){var scopeClosers=this.scopeClosers;this.scopeClosers="";func.call(this);this.closeScopeLevels();this.scopeClosers=scopeClosers},makeCallback:function(res){var err=this.tmpid();return"function("+err+(res?","+res:"")+") {\n"+"if("+err+") { cb("+err+"); return; }"},tmpid:function(){this.lastId++;return"t_"+this.lastId},_bufferAppend:function(func){this.emit(this.buffer+" += runtime.suppressValue(");func.call(this);this.emit(", env.autoesc);\n")},_compileChildren:function(node,frame){var children=node.children;for(var i=0,l=children.length;i0){this.emit(",")}this.compile(node.children[i],frame)}if(endChar){this.emit(endChar)}},_compileExpression:function(node,frame){this.assertType(node,nodes.Literal,nodes.Symbol,nodes.Group,nodes.Array,nodes.Dict,nodes.FunCall,nodes.Caller,nodes.Filter,nodes.LookupVal,nodes.Compare,nodes.InlineIf,nodes.In,nodes.And,nodes.Or,nodes.Not,nodes.Add,nodes.Sub,nodes.Mul,nodes.Div,nodes.FloorDiv,nodes.Mod,nodes.Pow,nodes.Neg,nodes.Pos,nodes.Compare,nodes.NodeList);this.compile(node,frame)},assertType:function(node){var types=lib.toArray(arguments).slice(1);var success=false;for(var i=0;i0){this.emit(",")}if(arg){var id=this.tmpid();this.emitLine("function(cb) {");this.emitLine("if(!cb) { cb = function(err) { if(err) { throw err; }}}");this.pushBufferId(id);this.withScopedSyntax(function(){this.compile(arg,frame);this.emitLine("cb(null, "+id+");")});this.popBufferId();this.emitLine("return "+id+";");this.emitLine("}")}else{this.emit("null")}},this)}if(async){var res=this.tmpid();this.emitLine(", "+this.makeCallback(res));this.emitLine(this.buffer+" += runtime.suppressValue("+res+", "+autoescape+" && env.autoesc);");this.addScopeLevel()}else{this.emit(")");this.emit(", "+autoescape+" && env.autoesc);\n")}},compileCallExtensionAsync:function(node,frame){this.compileCallExtension(node,frame,true)},compileNodeList:function(node,frame){this._compileChildren(node,frame)},compileLiteral:function(node,frame){if(typeof node.value=="string"){var val=node.value.replace(/\\/g,"\\\\");val=val.replace(/"/g,'\\"');val=val.replace(/\n/g,"\\n");val=val.replace(/\r/g,"\\r");val=val.replace(/\t/g,"\\t");this.emit('"'+val+'"')}else{this.emit(node.value.toString())}},compileSymbol:function(node,frame){var name=node.value;var v;if(v=frame.lookup(name)){this.emit(v)}else{this.emit("runtime.contextOrFrameLookup("+'context, frame, "'+name+'")')}},compileGroup:function(node,frame){this._compileAggregate(node,frame,"(",")")},compileArray:function(node,frame){this._compileAggregate(node,frame,"[","]")},compileDict:function(node,frame){this._compileAggregate(node,frame,"{","}")},compilePair:function(node,frame){var key=node.key;var val=node.value;if(key instanceof nodes.Symbol){key=new nodes.Literal(key.lineno,key.colno,key.value)}else if(!(key instanceof nodes.Literal&&typeof key.value=="string")){this.fail("compilePair: Dict keys must be strings or names",key.lineno,key.colno)}this.compile(key,frame);this.emit(": ");this._compileExpression(val,frame)},compileInlineIf:function(node,frame){this.emit("(");this.compile(node.cond,frame);this.emit("?");this.compile(node.body,frame);this.emit(":");if(node.else_!==null)this.compile(node.else_,frame);else this.emit('""');this.emit(")")},compileIn:function(node,frame){this.emit("(");this.compile(node.right,frame);this.emit(".indexOf(");this.compile(node.left,frame);this.emit(") !== -1)")},compileOr:binOpEmitter(" || "),compileAnd:binOpEmitter(" && "),compileAdd:binOpEmitter(" + "),compileSub:binOpEmitter(" - "),compileMul:binOpEmitter(" * "),compileDiv:binOpEmitter(" / "),compileMod:binOpEmitter(" % "),compileNot:function(node,frame){this.emit("!");this.compile(node.target,frame)},compileFloorDiv:function(node,frame){this.emit("Math.floor(");this.compile(node.left,frame);this.emit(" / ");this.compile(node.right,frame);this.emit(")")},compilePow:function(node,frame){this.emit("Math.pow(");this.compile(node.left,frame);this.emit(", ");this.compile(node.right,frame);this.emit(")")},compileNeg:function(node,frame){this.emit("-");this.compile(node.target,frame)},compilePos:function(node,frame){this.emit("+");this.compile(node.target,frame)},compileCompare:function(node,frame){this.compile(node.expr,frame);for(var i=0;i=width){return str}var spaces=width-str.length;var pre=lib.repeat(" ",spaces/2-spaces%2);var post=lib.repeat(" ",spaces/2);return r.copySafeness(str,pre+str+post)},"default":function(val,def){return val?val:def},dictsort:function(val,case_sensitive,by){if(!lib.isObject(val)){throw new lib.TemplateError("dictsort filter: val must be an object")}var array=[];for(var k in val){array.push([k,val[k]])}var si;if(by===undefined||by==="key"){si=0}else if(by==="value"){si=1}else{throw new lib.TemplateError("dictsort filter: You can only sort by either key or value")}array.sort(function(t1,t2){var a=t1[si];var b=t2[si];if(!case_sensitive){if(lib.isString(a)){a=a.toUpperCase()}if(lib.isString(b)){b=b.toUpperCase()}}return a>b?1:a==b?0:-1});return array},escape:function(str){if(typeof str=="string"||str instanceof r.SafeString){return lib.escape(str)}return str},safe:function(str){return r.markSafe(str)},first:function(arr){return arr[0]},groupby:function(arr,attr){return lib.groupBy(arr,attr)},indent:function(str,width,indentfirst){width=width||4;var res="";var lines=str.split("\n");var sp=lib.repeat(" ",width);for(var i=0;i=maxCount){break}last=res;res=res.replace(old,new_);count++}return r.copySafeness(str,res)},reverse:function(val){var arr;if(lib.isString(val)){arr=filters.list(val)}else{arr=lib.map(val,function(v){return v})}arr.reverse();if(lib.isString(val)){return r.copySafeness(val,arr.join(""))}return arr},round:function(val,precision,method){precision=precision||0;var factor=Math.pow(10,precision);var rounder;if(method=="ceil"){rounder=Math.ceil}else if(method=="floor"){rounder=Math.floor}else{rounder=Math.round}return rounder(val*factor)/factor},slice:function(arr,slices,fillWith){var sliceLength=Math.floor(arr.length/slices);var extra=arr.length%slices;var offset=0;var res=[];for(var i=0;i=extra){slice.push(fillWith)}res.push(slice)}return res},sort:function(arr,reverse,caseSens,attr){arr=lib.map(arr,function(v){return v});arr.sort(function(a,b){var x,y;if(attr){x=a[attr];y=b[attr]}else{x=a;y=b}if(!caseSens&&lib.isString(x)&&lib.isString(y)){x=x.toLowerCase();y=y.toLowerCase()}if(xy){return reverse?-1:1}else{return 0}});return arr},string:function(obj){return r.copySafeness(obj,obj)},title:function(str){var words=str.split(" ");for(var i=0;i"+possibleUrl.substr(0,length)+"";if(wwwRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";if(emailRE.test(possibleUrl))return''+possibleUrl+"";if(tldRE.test(possibleUrl))return'"+possibleUrl.substr(0,length)+"";return word});return words.join(" ")},wordcount:function(str){var words=str?str.match(/\w+/g):null;return words?words.length:null},"float":function(val,def){var res=parseFloat(val);return isNaN(res)?def:res},"int":function(val,def){var res=parseInt(val,10);return isNaN(res)?def:res}};filters.d=filters["default"];filters.e=filters.escape;modules["filters"]=filters})();(function(){function cycler(items){var index=-1;this.current=null;return{reset:function(){index=-1;this.current=null},next:function(){index++;if(index>=items.length){index=0}this.current=items[index];return this.current}}}function joiner(sep){sep=sep||",";var first=true;return function(){var val=first?"":sep;first=false;return val}}var globals={range:function(start,stop,step){if(!stop){stop=start;start=0;step=1}else if(!step){step=1}var arr=[];for(var i=start;i",
"dependencies": {
"optimist": "*",