diff --git a/LICENSE b/LICENSE index 15e227fb..e4e6ec0b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2012, John Roepke +Copyright (c) 2011-2013, John Roepke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/demos/node_express/public/vendor/twig.js b/demos/node_express/public/vendor/twig.js index 452261e9..cc7f7ddb 100644 --- a/demos/node_express/public/vendor/twig.js +++ b/demos/node_express/public/vendor/twig.js @@ -1,16 +1,16 @@ -// Twig.js 0.5.5 -// Copyright (c) 2011-2012 John Roepke +// Twig.js 0.5.6 +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js var Twig = (function (Twig) { - Twig.VERSION = "0.5.5"; + Twig.VERSION = "0.5.6"; return Twig; })(Twig || {}); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -1518,7 +1518,7 @@ var Twig = (function(Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -2317,7 +2317,7 @@ var Twig = (function (Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3297,7 +3297,7 @@ var Twig = (function (Twig) { })( Twig || { } ); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3327,7 +3327,7 @@ var Twig = (function (Twig) { token.precidence = 20; token.associativity = Twig.expression.operator.leftToRight; break; - + case ',': token.precidence = 18; token.associativity = Twig.expression.operator.leftToRight; @@ -3405,7 +3405,7 @@ var Twig = (function (Twig) { case ':': // Ignore break; - + case '?': c = stack.pop(); // false expr b = stack.pop(); // true expr @@ -3416,7 +3416,7 @@ var Twig = (function (Twig) { stack.push(c); } break; - + case '+': b = parseFloat(stack.pop()); a = parseFloat(stack.pop()); @@ -3537,28 +3537,28 @@ var Twig = (function (Twig) { a = stack.pop(); stack.push( !containment(a, b) ); break; - + case 'in': b = stack.pop(); a = stack.pop(); stack.push( containment(a, b) ); break; - + case '..': b = stack.pop(); a = stack.pop(); stack.push( Twig.functions.range(a, b) ); break; - + default: throw new Twig.Error(operator + " is an unknown operator."); } }; - + var containment = function(a, b) { if (b.indexOf != undefined) { return b.indexOf(a) > -1; - + } else { var el; for (el in b) { @@ -3574,7 +3574,7 @@ var Twig = (function (Twig) { })( Twig || { } ); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3685,7 +3685,7 @@ var Twig = (function (Twig) { if (value === undefined){ return; } - + return encodeURIComponent(value); }, join: function(value, params) { @@ -3841,7 +3841,7 @@ var Twig = (function (Twig) { if (value === undefined){ return; } - + return Twig.lib.strip_tags(value); }, @@ -3874,7 +3874,7 @@ var Twig = (function (Twig) { /** * Adapted from: http://phpjs.org/functions/number_format:481 - */ + */ number_format: function(value, params) { var number = value, decimals = (params && params[0]) ? params[0] : undefined, @@ -3939,7 +3939,7 @@ var Twig = (function (Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // 2012 Hadrien Lanneau // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3957,7 +3957,7 @@ var Twig = (function (Twig) { Twig.functions = { // attribute, block, constant, date, dump, parent, random,. - + // Range function from http://phpjs.org/functions/range:499 // Used under an MIT License range: function (low, high, step) { @@ -4110,7 +4110,7 @@ var Twig = (function (Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -4172,7 +4172,7 @@ var Twig = (function (Twig) { return Twig; })( Twig || { } ); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -4358,7 +4358,7 @@ var Twig = (function (Twig) { }) (Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -4369,17 +4369,17 @@ var Twig = (function (Twig) { /** * Namespace for compilation. */ - Twig.compiler = { + Twig.compiler = { module: {} }; - + // Compile a Twig Template to output. Twig.compiler.compile = function(template, options) { // Get tokens var tokens = JSON.stringify(template.tokens) , id = template.id , output; - + if (options.module) { if (Twig.compiler.module[options.module] === undefined) { throw new Twig.Error("Unable to find module type " + options.module); @@ -4390,7 +4390,7 @@ var Twig = (function (Twig) { } return output; }; - + Twig.compiler.module = { amd: function(id, tokens, pathToTwig) { return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig = Twig.twig;\n' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});'; @@ -4406,14 +4406,14 @@ var Twig = (function (Twig) { + '\n});' } }; - + Twig.compiler.wrap = function(id, tokens) { return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n'; }; - + return Twig; })(Twig || {});// Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js diff --git a/demos/twitter_backbone/vendor/twig.js b/demos/twitter_backbone/vendor/twig.js index 452261e9..cc7f7ddb 100644 --- a/demos/twitter_backbone/vendor/twig.js +++ b/demos/twitter_backbone/vendor/twig.js @@ -1,16 +1,16 @@ -// Twig.js 0.5.5 -// Copyright (c) 2011-2012 John Roepke +// Twig.js 0.5.6 +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js var Twig = (function (Twig) { - Twig.VERSION = "0.5.5"; + Twig.VERSION = "0.5.6"; return Twig; })(Twig || {}); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -1518,7 +1518,7 @@ var Twig = (function(Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -2317,7 +2317,7 @@ var Twig = (function (Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3297,7 +3297,7 @@ var Twig = (function (Twig) { })( Twig || { } ); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3327,7 +3327,7 @@ var Twig = (function (Twig) { token.precidence = 20; token.associativity = Twig.expression.operator.leftToRight; break; - + case ',': token.precidence = 18; token.associativity = Twig.expression.operator.leftToRight; @@ -3405,7 +3405,7 @@ var Twig = (function (Twig) { case ':': // Ignore break; - + case '?': c = stack.pop(); // false expr b = stack.pop(); // true expr @@ -3416,7 +3416,7 @@ var Twig = (function (Twig) { stack.push(c); } break; - + case '+': b = parseFloat(stack.pop()); a = parseFloat(stack.pop()); @@ -3537,28 +3537,28 @@ var Twig = (function (Twig) { a = stack.pop(); stack.push( !containment(a, b) ); break; - + case 'in': b = stack.pop(); a = stack.pop(); stack.push( containment(a, b) ); break; - + case '..': b = stack.pop(); a = stack.pop(); stack.push( Twig.functions.range(a, b) ); break; - + default: throw new Twig.Error(operator + " is an unknown operator."); } }; - + var containment = function(a, b) { if (b.indexOf != undefined) { return b.indexOf(a) > -1; - + } else { var el; for (el in b) { @@ -3574,7 +3574,7 @@ var Twig = (function (Twig) { })( Twig || { } ); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3685,7 +3685,7 @@ var Twig = (function (Twig) { if (value === undefined){ return; } - + return encodeURIComponent(value); }, join: function(value, params) { @@ -3841,7 +3841,7 @@ var Twig = (function (Twig) { if (value === undefined){ return; } - + return Twig.lib.strip_tags(value); }, @@ -3874,7 +3874,7 @@ var Twig = (function (Twig) { /** * Adapted from: http://phpjs.org/functions/number_format:481 - */ + */ number_format: function(value, params) { var number = value, decimals = (params && params[0]) ? params[0] : undefined, @@ -3939,7 +3939,7 @@ var Twig = (function (Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // 2012 Hadrien Lanneau // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -3957,7 +3957,7 @@ var Twig = (function (Twig) { Twig.functions = { // attribute, block, constant, date, dump, parent, random,. - + // Range function from http://phpjs.org/functions/range:499 // Used under an MIT License range: function (low, high, step) { @@ -4110,7 +4110,7 @@ var Twig = (function (Twig) { })(Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -4172,7 +4172,7 @@ var Twig = (function (Twig) { return Twig; })( Twig || { } ); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -4358,7 +4358,7 @@ var Twig = (function (Twig) { }) (Twig || { }); // Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js @@ -4369,17 +4369,17 @@ var Twig = (function (Twig) { /** * Namespace for compilation. */ - Twig.compiler = { + Twig.compiler = { module: {} }; - + // Compile a Twig Template to output. Twig.compiler.compile = function(template, options) { // Get tokens var tokens = JSON.stringify(template.tokens) , id = template.id , output; - + if (options.module) { if (Twig.compiler.module[options.module] === undefined) { throw new Twig.Error("Unable to find module type " + options.module); @@ -4390,7 +4390,7 @@ var Twig = (function (Twig) { } return output; }; - + Twig.compiler.module = { amd: function(id, tokens, pathToTwig) { return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig = Twig.twig;\n' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});'; @@ -4406,14 +4406,14 @@ var Twig = (function (Twig) { + '\n});' } }; - + Twig.compiler.wrap = function(id, tokens) { return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n'; }; - + return Twig; })(Twig || {});// Twig.js -// Copyright (c) 2011-2012 John Roepke +// Copyright (c) 2011-2013 John Roepke // Available under the BSD 2-Clause License // https://github.com/justjohn/twig.js diff --git a/docs/tests.md b/docs/tests.md index c09601c2..9244f31b 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -43,6 +43,7 @@ - [date ->](#twigjs-functions---built-in-functions---date--) - [dump ->](#twigjs-functions---built-in-functions---dump--) - [Twig.js Regression Tests ->](#twigjs-regression-tests--) + - [Twig.js Tags ->](#twigjs-tags--) - [Twig.js Tests ->](#twigjs-tests--) - [empty test ->](#twigjs-tests---empty-test--) - [odd test ->](#twigjs-tests---odd-test--) @@ -1843,6 +1844,18 @@ Twig.extendFunction('custom', function(value) { twig({data: '{% if (custom("val") and custom("val")) %}out{% endif %}'}).render({}).should.equal("out"); ``` + +# Twig.js Tags -> +should support spaceless. + +```js +twig({ + data: "{% spaceless %}
\n b i\n
{% endspaceless %}" +}).render().should.equal( + "
bi
" +); +``` + # Twig.js Tests -> diff --git a/docs/twig.html b/docs/twig.html index 9c6b0e2e..d127e542 100644 --- a/docs/twig.html +++ b/docs/twig.html @@ -1,14 +1,14 @@ - twig.js

twig.js

Twig.js 0.5.5
-Copyright (c) 2011-2012 John Roepke
+      twig.js               
         
     

twig.js

Twig.js 0.5.6
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
 
var Twig = (function (Twig) {
 
-    Twig.VERSION = "0.5.5";
+    Twig.VERSION = "0.5.6";
 
     return Twig;
 })(Twig || {});
Twig.js
-Copyright (c) 2011-2012 John Roepke
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
 
var Twig = (function (Twig) {
@@ -1259,7 +1259,7 @@
     return Twig;
 
 })(Twig || { });
Twig.js
-Copyright (c) 2011-2012 John Roepke
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
 

twig.logic.js

@@ -1289,7 +1289,9 @@ endblock: 'Twig.logic.type.endblock', extends_: 'Twig.logic.type.extends', use: 'Twig.logic.type.use', - include: 'Twig.logic.type.include' + include: 'Twig.logic.type.include', + spaceless: 'Twig.logic.type.spaceless', + endspaceless: 'Twig.logic.type.endspaceless' };

Regular expressions for handling logic tokens.

Properties:

@@ -1794,6 +1796,27 @@ output: template.render(innerContext) }; } + }, + { + type: Twig.logic.type.spaceless, + regex: /^spaceless$/, + next: [ + Twig.logic.type.endspaceless + ], + open: true,

Parse the html and return it without any spaces between tags

            parse: function (token, context, chain) {
+                var // Parse the output without any filter
+                    unfiltered = Twig.parse.apply(this, [token.output, context]),

A regular expression to find closing and opening tags with spaces between them

                    rBetweenTagSpaces = />\s+</g,

Replace all space between closing and opening html tags

                    output = unfiltered.replace(rBetweenTagSpaces,'><').trim();
+
+                return {
+                    chain: chain,
+                    output: output
+                };
+            }
+        },

Add the {% endspaceless %} token

        {
+            type: Twig.logic.type.endspaceless,
+            regex: /^endspaceless$/,
+            next: [ ],
+            open: false
         }
     ];
 
@@ -1841,7 +1864,7 @@
             Twig.logic.extendType(definition.type);
         }
         Twig.logic.handler[definition.type] = definition;
-    };

Extend with built-in expressions

    while (Twig.logic.definitions.length > 0) {
+    };

Extend with built-in expressions

    while (Twig.logic.definitions.length > 0) {
         Twig.logic.extend(Twig.logic.definitions.shift());
     }
 
@@ -1855,7 +1878,7 @@
     Twig.logic.compile = function (raw_token) {
         var expression = raw_token.value.trim(),
             token = Twig.logic.tokenize.apply(this, [expression]),
-            token_template = Twig.logic.handler[token.type];

Check if the token needs compiling

        if (token_template.compile) {
+            token_template = Twig.logic.handler[token.type];

Check if the token needs compiling

        if (token_template.compile) {
             token = token_template.compile.apply(this, [token]);
             Twig.log.trace("Twig.logic.compile: ", "Compiled logic token to ", token);
         }
@@ -1879,16 +1902,16 @@
             token_regex = null,
             regex_array = null,
             regex = null,
-            match = null;

Ignore whitespace around expressions.

        expression = expression.trim();
+            match = null;

Ignore whitespace around expressions.

        expression = expression.trim();
 
         for (token_template_type in Twig.logic.handler) {
-            if (Twig.logic.handler.hasOwnProperty(token_template_type)) {

Get the type and regex for this template type

                token_type = Twig.logic.handler[token_template_type].type;
-                token_regex = Twig.logic.handler[token_template_type].regex;

Handle multiple regular expressions per type.

                regex_array = [];
+            if (Twig.logic.handler.hasOwnProperty(token_template_type)) {

Get the type and regex for this template type

                token_type = Twig.logic.handler[token_template_type].type;
+                token_regex = Twig.logic.handler[token_template_type].regex;

Handle multiple regular expressions per type.

                regex_array = [];
                 if (token_regex instanceof Array) {
                     regex_array = token_regex;
                 } else {
                     regex_array.push(token_regex);
-                }

Check regular expressions in the order they were specified in the definition.

                while (regex_array.length > 0) {
+                }

Check regular expressions in the order they were specified in the definition.

                while (regex_array.length > 0) {
                     regex = regex_array.shift();
                     match = regex.exec(expression.trim());
                     if (match !== null) {
@@ -1899,7 +1922,7 @@
                     }
                 }
             }
-        }

No regex matches

        throw new Twig.Error("Unable to parse '" + expression.trim() + "'");
+        }

No regex matches

        throw new Twig.Error("Unable to parse '" + expression.trim() + "'");
     };
 
     /**
@@ -1940,11 +1963,11 @@
 
     return Twig;
 
-})(Twig || { });
Twig.js
-Copyright (c) 2011-2012 John Roepke
+})(Twig || { });
Twig.js
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.expression.js

+

twig.expression.js

This file handles tokenizing, compiling and parsing expressions.

var Twig = (function (Twig) {
     "use strict";
@@ -1996,7 +2019,7 @@
         test:       'Twig.expression.type.test'
     };
 
-    Twig.expression.set = {

What can follow an expression (in general)

        operations: [
+    Twig.expression.set = {

What can follow an expression (in general)

        operations: [
             Twig.expression.type.filter,
             Twig.expression.type.operator.unary,
             Twig.expression.type.operator.binary,
@@ -2017,9 +2040,9 @@
             Twig.expression.type.array.start,
             Twig.expression.type.object.start
         ]
-    };

Most expressions allow a '.' or '[' after them, so we provide a convenience set

    Twig.expression.set.operations_extended = Twig.expression.set.operations.concat([
+    };

Most expressions allow a '.' or '[' after them, so we provide a convenience set

    Twig.expression.set.operations_extended = Twig.expression.set.operations.concat([
                     Twig.expression.type.key.period,
-                    Twig.expression.type.key.brackets]);

Some commonly used compile and parse functions.

    Twig.expression.fn = {
+                    Twig.expression.type.key.brackets]);

Some commonly used compile and parse functions.

    Twig.expression.fn = {
         compile: {
             push: function(token, stack, output) {
                 output.push(token);
@@ -2037,7 +2060,7 @@
                 stack.push(token.value);
             }
         }
-    };

The regular expressions and compile/parse logic used to match tokens in expressions.

+ };

The regular expressions and compile/parse logic used to match tokens in expressions.

Properties:

@@ -2078,14 +2101,14 @@ } }, { - type: Twig.expression.type.comma,

Match a comma

            regex: /^,/,
+            type: Twig.expression.type.comma,

Match a comma

            regex: /^,/,
             next: Twig.expression.set.expressions,
             compile: function(token, stack, output) {
                 var i = stack.length - 1,
                     stack_token;
 
                 delete token.match;
-                delete token.value;

pop tokens off the stack until the start of the object

                for(;i >= 0; i--) {
+                delete token.value;

pop tokens off the stack until the start of the object

                for(;i >= 0; i--) {
                     stack_token = stack.pop();
                     if (stack_token.type === Twig.expression.type.object.start
                             || stack_token.type === Twig.expression.type.parameter.start
@@ -2099,7 +2122,7 @@
             }
         },
         {
-            type: Twig.expression.type.operator.binary,

Match any of +, , /, -, %, ~, <, <=, >, >=, !=, ==, *, ?, :, and, or, not

            regex: /(^[\+\-~%\?\:]|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^and\s+|^or\s+|^in\s+|^not in\s+|^\.\.)/,
+            type: Twig.expression.type.operator.binary,

Match any of +, , /, -, %, ~, <, <=, >, >=, !=, ==, *, ?, :, and, or, not

            regex: /(^[\+\-~%\?\:]|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^and\s+|^or\s+|^in\s+|^not in\s+|^\.\.)/,
             next: Twig.expression.set.expressions.concat([Twig.expression.type.operator.unary]),
             compile: function(token, stack, output) {
                 delete token.match;
@@ -2124,7 +2147,7 @@
                      output.push(temp);
                 }
 
-                if (value === ":") {

Check if this is a ternary or object key being set

                    if (stack[stack.length - 1] && stack[stack.length-1].value === "?") {

Continue as normal for a ternary

                    } else {

This is not a ternary so we push the token to the output where it can be handled + if (value === ":") {

Check if this is a ternary or object key being set

                    if (stack[stack.length - 1] && stack[stack.length-1].value === "?") {

Continue as normal for a ternary

                    } else {

This is not a ternary so we push the token to the output where it can be handled when the assocated object is closed.

                        var key_token = output.pop();
 
                         if (key_token.type === Twig.expression.type.string ||
@@ -2144,14 +2167,14 @@
                 }
             },
             parse: function(token, stack, context) {
-                if (token.key) {

handle ternary ':' operator

                    stack.push(token);
+                if (token.key) {

handle ternary ':' operator

                    stack.push(token);
                 } else {
                     Twig.expression.operator.parse(token.value, stack);
                 }
             }
         },
         {
-            type: Twig.expression.type.operator.unary,

Match any of not

            regex: /(^not\s+)/,
+            type: Twig.expression.type.operator.unary,

Match any of not

            regex: /(^not\s+)/,
             next: Twig.expression.set.expressions,
             compile: function(token, stack, output) {
                 delete token.match;
@@ -2186,11 +2209,11 @@
             /**
              * Match a string. This is anything between a pair of single or double quotes.
              */
-            type: Twig.expression.type.string,

See: http://blog.stevenlevithan.com/archives/match-quoted-string

            regex: /^(["'])(?:(?=(\\?))\2.)*?\1/,
+            type: Twig.expression.type.string,

See: http://blog.stevenlevithan.com/archives/match-quoted-string

            regex: /^(["'])(?:(?=(\\?))\2.)*?\1/,
             next: Twig.expression.set.operations,
             compile: function(token, stack, output) {
                 var value = token.value;
-                delete token.match

Remove the quotes from the string

                if (value.substring(0, 1) === '"') {
+                delete token.match

Remove the quotes from the string

                if (value.substring(0, 1) === '"') {
                     value = value.replace('\\"', '"');
                 } else {
                     value = value.replace("\\'", "'");
@@ -2226,13 +2249,13 @@
                 while(stack.length > 0 && stack_token.type != Twig.expression.type.parameter.start) {
                     output.push(stack_token);
                     stack_token = stack.pop();
-                }

Move contents of parens into preceding filter

                var param_stack = [];
-                while(token.type !== Twig.expression.type.parameter.start) {

Add token to arguments stack

                    param_stack.unshift(token);
+                }

Move contents of parens into preceding filter

                var param_stack = [];
+                while(token.type !== Twig.expression.type.parameter.start) {

Add token to arguments stack

                    param_stack.unshift(token);
                     token = output.pop();
                 }
                 param_stack.unshift(token);
 
-                var is_expression = false;

Get the token preceding the parameters

                token = output[output.length-1];
+                var is_expression = false;

Get the token preceding the parameters

                token = output[output.length-1];
 
                 if (token === undefined ||
                     (token.type !== Twig.expression.type._function &&
@@ -2241,7 +2264,7 @@
                     token.type !== Twig.expression.type.key.brackets &&
                     token.type !== Twig.expression.type.key.period)) {
 
-                    end_token.expression = true;

remove start and end token from stack

                    param_stack.pop();
+                    end_token.expression = true;

remove start and end token from stack

                    param_stack.pop();
                     param_stack.shift();
 
                     end_token.params = param_stack;
@@ -2265,7 +2288,7 @@
                 } else {
 
                     while (stack.length > 0) {
-                        value = stack.pop();

Push values into the array until the start of the array

                        if (value && value.type && value.type == Twig.expression.type.parameter.start) {
+                        value = stack.pop();

Push values into the array until the start of the array

                        if (value && value.type && value.type == Twig.expression.type.parameter.start) {
                             array_ended = true;
                             break;
                         }
@@ -2299,7 +2322,7 @@
             next: Twig.expression.set.operations_extended,
             compile: function(token, stack, output) {
                 var i = stack.length - 1,
-                    stack_token;

pop tokens off the stack until the start of the object

                for(;i >= 0; i--) {
+                    stack_token;

pop tokens off the stack until the start of the object

                for(;i >= 0; i--) {
                     stack_token = stack.pop();
                     if (stack_token.type === Twig.expression.type.array.start) {
                         break;
@@ -2314,7 +2337,7 @@
                     value = null;
 
                 while (stack.length > 0) {
-                    value = stack.pop();

Push values into the array until the start of the array

                    if (value.type && value.type == Twig.expression.type.array.start) {
+                    value = stack.pop();

Push values into the array until the start of the array

                    if (value.type && value.type == Twig.expression.type.array.start) {
                         array_ended = true;
                         break;
                     }
@@ -2326,7 +2349,7 @@
 
                 stack.push(new_array);
             }
-        },

Token that represents the start of a hash map '}'

+ },

Token that represents the start of a hash map '}'

Hash maps take the form: { "key": 'value', "another_key": item }

@@ -2337,7 +2360,7 @@ next: Twig.expression.set.expressions.concat([Twig.expression.type.object.end]), compile: Twig.expression.fn.compile.push_both, parse: Twig.expression.fn.parse.push - },

Token that represents the end of a Hash Map '}'

+ },

Token that represents the end of a Hash Map '}'

This is where the logic for building the internal representation of a hash map is defined.

        {
@@ -2346,7 +2369,7 @@
             next: Twig.expression.set.operations_extended,
             compile: function(token, stack, output) {
                 var i = stack.length-1,
-                    stack_token;

pop tokens off the stack until the start of the object

                for(;i >= 0; i--) {
+                    stack_token;

pop tokens off the stack until the start of the object

                for(;i >= 0; i--) {
                     stack_token = stack.pop();
                     if (stack_token && stack_token.type === Twig.expression.type.object.start) {
                         break;
@@ -2364,7 +2387,7 @@
                     value = null;
 
                 while (stack.length > 0) {
-                    token = stack.pop();

Push values into the array until the start of the object

                    if (token && token.type && token.type === Twig.expression.type.object.start) {
+                    token = stack.pop();

Push values into the array until the start of the object

                    if (token && token.type && token.type === Twig.expression.type.object.start) {
                         object_ended = true;
                         break;
                     }
@@ -2372,10 +2395,10 @@
                         if (!has_value) {
                             throw new Twig.Error("Missing value for key '" + token.key + "' in object definition.");
                         }
-                        new_object[token.key] = value;

Preserve the order that elements are added to the map + new_object[token.key] = value;

Preserve the order that elements are added to the map This is necessary since JavaScript objects don't guarantee the order of keys

                        if (new_object._keys === undefined) new_object._keys = [];
-                        new_object._keys.unshift(token.key);

reset value check

                        value = null;
+                        new_object._keys.unshift(token.key);

reset value check

                        value = null;
                         has_value = false;
 
                     } else {
@@ -2389,13 +2412,13 @@
 
                 stack.push(new_object);
             }
-        },

Token representing a filter

+ },

Token representing a filter

Filters can follow any expression and take the form: expression|filter(optional, args)

Filter parsing is done in the Twig.filters namespace.

        {
-            type: Twig.expression.type.filter,

match a | then a letter or _, then any number of letters, numbers, _ or -

            regex: /^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/,
+            type: Twig.expression.type.filter,

match a | then a letter or _, then any number of letters, numbers, _ or -

            regex: /^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/,
             next: Twig.expression.set.operations_extended.concat([
                     Twig.expression.type.parameter.start]),
             compile: function(token, stack, output) {
@@ -2410,14 +2433,14 @@
             }
         },
         {
-            type: Twig.expression.type._function,

match any letter or _, then any number of letters, numbers, _ or - followed by (

            regex: /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/,
+            type: Twig.expression.type._function,

match any letter or _, then any number of letters, numbers, _ or - followed by (

            regex: /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/,
             next: Twig.expression.type.parameter.start,
             transform: function(match, tokens) {
                 return '(';
             },
             compile: function(token, stack, output) {
                 var fn = token.match[1];
-                token.fn = fn;

cleanup token

                delete token.match;
+                token.fn = fn;

cleanup token

                delete token.match;
                 delete token.value;
 
                 output.push(token);
@@ -2427,9 +2450,9 @@
                     fn     = token.fn,
                     value;
 
-                if (Twig.functions[fn]) {

Get the function from the built-in functions

                    value = Twig.functions[fn].apply(this, params);
+                if (Twig.functions[fn]) {

Get the function from the built-in functions

                    value = Twig.functions[fn].apply(this, params);
 
-                } else if (typeof context[fn] == 'function') {

Get the function from the user/context defined functions

                    value = context[fn].apply(context, params);
+                } else if (typeof context[fn] == 'function') {

Get the function from the user/context defined functions

                    value = context[fn].apply(context, params);
 
                 } else {
                     throw new Twig.Error(fn + ' function does not exist and is not defined in the context');
@@ -2437,7 +2460,7 @@
 
                 stack.push(value);
             }
-        },

Token representing a variable.

+ },

Token representing a variable.

Variables can contain letters, numbers, underscores and dashes, but must start with a letter or underscore.

@@ -2445,14 +2468,14 @@

Variables are retrieved from the render context and take the value of 'undefined' if the given variable doesn't exist in the context.

        {
-            type: Twig.expression.type.variable,

match any letter or _, then any number of letters, numbers, _ or -

            regex: /^[a-zA-Z_][a-zA-Z0-9_]*/,
+            type: Twig.expression.type.variable,

match any letter or _, then any number of letters, numbers, _ or -

            regex: /^[a-zA-Z_][a-zA-Z0-9_]*/,
             next: Twig.expression.set.operations_extended.concat([
                     Twig.expression.type.parameter.start]),
             compile: Twig.expression.fn.compile.push,
             validate: function(match, tokens) {
                 return Twig.expression.reservedWords.indexOf(match[0]) == -1;
             },
-            parse: function(token, stack, context) {

Get the variable from the context

                var value = Twig.expression.resolve(context[token.value], context);
+            parse: function(token, stack, context) {

Get the variable from the context

                var value = Twig.expression.resolve(context[token.value], context);
                 stack.push(value);
             }
         },
@@ -2482,7 +2505,7 @@
                     }
                 }
 
-                var capitalize = function(value) {return value.substr(0, 1).toUpperCase() + value.substr(1);};

Get the variable from the context

                if (typeof object === 'object' && key in object) {
+                var capitalize = function(value) {return value.substr(0, 1).toUpperCase() + value.substr(1);};

Get the variable from the context

                if (typeof object === 'object' && key in object) {
                     value = object[key];
                 } else if (object["get"+capitalize(key)] !== undefined) {
                     value = object["get"+capitalize(key)];
@@ -2502,13 +2525,13 @@
             compile: function(token, stack, output) {
                 var match = token.match[1];
                 delete token.value;
-                delete token.match;

The expression stack for the key

                token.stack = Twig.expression.compile({
+                delete token.match;

The expression stack for the key

                token.stack = Twig.expression.compile({
                     value: match
                 }).stack;
 
                 output.push(token);
             },
-            parse: function(token, stack, context) {

Evaluate key

                var params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
+            parse: function(token, stack, context) {

Evaluate key

                var params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
                     key = Twig.expression.parse.apply(this, [token.stack, context]),
                     object = stack.pop(),
                     value;
@@ -2519,7 +2542,7 @@
                     } else {
                         return null;
                     }
-                }

Get the variable from the context

                if (typeof object === 'object' && key in object) {
+                }

Get the variable from the context

                if (typeof object === 'object' && key in object) {
                     value = object[key];
                 } else {
                     value = null;
@@ -2531,7 +2554,7 @@
             /**
              * Match a null value.
              */
-            type: Twig.expression.type._null,

match a number

            regex: /^null/,
+            type: Twig.expression.type._null,

match a number

            regex: /^null/,
             next: Twig.expression.set.operations,
             compile: function(token, stack, output) {
                 delete token.match;
@@ -2544,7 +2567,7 @@
             /**
              * Match a number (integer or decimal)
              */
-            type: Twig.expression.type.number,

match a number

            regex: /^\-?\d+(\.\d+)?/,
+            type: Twig.expression.type.number,

match a number

            regex: /^\-?\d+(\.\d+)?/,
             next: Twig.expression.set.operations,
             compile: function(token, stack, output) {
                 token.value = Number(token.value);
@@ -2624,7 +2647,7 @@
             throw new Twig.Error("Unable to extend logic definition. No type provided for " + definition);
         }
         Twig.expression.handler[definition.type] = definition;
-    };

Extend with built-in expressions

    while (Twig.expression.definitions.length > 0) {
+    };

Extend with built-in expressions

    while (Twig.expression.definitions.length > 0) {
         Twig.expression.extend(Twig.expression.definitions.shift());
     }
 
@@ -2636,7 +2659,7 @@
      * @return {Array} An array of tokens.
      */
     Twig.expression.tokenize = function (expression) {
-        var tokens = [],

Keep an offset of the location in the expression for error messages.

            exp_offset = 0,

The valid next tokens of the previous token

            next = null,

Match information

            type, regex, regex_array,

The possible next token for the match

            token_next,

Has a match been found from the definitions

            match_found, invalid_matches = [], match_function;
+        var tokens = [],

Keep an offset of the location in the expression for error messages.

            exp_offset = 0,

The valid next tokens of the previous token

            next = null,

Match information

            type, regex, regex_array,

The possible next token for the match

            token_next,

Has a match been found from the definitions

            match_found, invalid_matches = [], match_function;
 
         match_function = function () {
             var match = Array.prototype.slice.apply(arguments),
@@ -2651,8 +2674,8 @@
                     type + " cannot follow a " + tokens[tokens.length - 1].type +
                            " at template:" + exp_offset + " near '" + match[0].substring(0, 20) +
                            "...'"
-                );

Not a match, don't change the expression

                return match[0];
-            }

Validate the token if a validation function is provided

            if (Twig.expression.handler[type].validate &&
+                );

Not a match, don't change the expression

                return match[0];
+            }

Validate the token if a validation function is provided

            if (Twig.expression.handler[type].validate &&
                     !Twig.expression.handler[type].validate(match, tokens)) {
                 return match[0];
             }
@@ -2667,7 +2690,7 @@
 
             match_found = true;
             next = token_next;
-            exp_offset += match[0].length;

Does the token need to return output back to the expression string + exp_offset += match[0].length;

Does the token need to return output back to the expression string e.g. a function match of cycle( might return the '(' back to the expression This allows look-ahead to differentiate between token types (e.g. functions and variable names)

            if (Twig.expression.handler[type].transform) {
                 return Twig.expression.handler[type].transform(match, tokens);
@@ -2682,7 +2705,7 @@
             for (type in Twig.expression.handler) {
                 if (Twig.expression.handler.hasOwnProperty(type)) {
                     token_next = Twig.expression.handler[type].next;
-                    regex = Twig.expression.handler[type].regex;

Twig.log.trace("Checking type ", type, " on ", expression);

                    if (regex instanceof Array) {
+                    regex = Twig.expression.handler[type].regex;

Twig.log.trace("Checking type ", type, " on ", expression);

                    if (regex instanceof Array) {
                         regex_array = regex;
                     } else {
                         regex_array = [regex];
@@ -2692,7 +2715,7 @@
                     while (regex_array.length > 0) {
                         regex = regex_array.pop();
                         expression = expression.replace(regex, match_function);
-                    }

An expression token has been matched. Break the for loop and start trying to + }

An expression token has been matched. Break the for loop and start trying to match the next template (if expression isn't empty.)

                    if (match_found) {
                         break;
                     }
@@ -2719,18 +2742,18 @@
      * @return {Object} The compiled token.
      */
     Twig.expression.compile = function (raw_token) {
-        var expression = raw_token.value,

Tokenize expression

            tokens = Twig.expression.tokenize(expression),
+        var expression = raw_token.value,

Tokenize expression

            tokens = Twig.expression.tokenize(expression),
             token = null,
             output = [],
             stack = [],
             token_template = null;
 
-        Twig.log.trace("Twig.expression.compile: ", "Compiling ", expression);

Push tokens into RPN stack using the Sunting-yard algorithm + Twig.log.trace("Twig.expression.compile: ", "Compiling ", expression);

Push tokens into RPN stack using the Sunting-yard algorithm See http://en.wikipedia.org/wiki/Shuntingyardalgorithm

        while (tokens.length > 0) {
             token = tokens.shift();
             token_template = Twig.expression.handler[token.type];
 
-            Twig.log.trace("Twig.expression.compile: ", "Compiling ", token);

Compile the template

            token_template.compile && token_template.compile(token, stack, output);
+            Twig.log.trace("Twig.expression.compile: ", "Compiling ", token);

Compile the template

            token_template.compile && token_template.compile(token, stack, output);
 
             Twig.log.trace("Twig.expression.compile: ", "Stack is", stack);
             Twig.log.trace("Twig.expression.compile: ", "Output is", output);
@@ -2760,25 +2783,25 @@
      *                  the given expression.
      */
     Twig.expression.parse = function (tokens, context) {
-        var that = this;

If the token isn't an array, make it one.

        if (!(tokens instanceof Array)) {
+        var that = this;

If the token isn't an array, make it one.

        if (!(tokens instanceof Array)) {
             tokens = [tokens];
-        }

The output stack

        var stack = [],
+        }

The output stack

        var stack = [],
             token_template = null;
 
         tokens.forEach(function (token) {
             token_template = Twig.expression.handler[token.type];
 
             token_template.parse && token_template.parse.apply(that, [token, stack, context]);
-        });

Pop the final value off the stack

        return stack.pop();
+        });

Pop the final value off the stack

        return stack.pop();
     };
 
     return Twig;
 
-})( Twig || { } );
Twig.js
-Copyright (c) 2011-2012 John Roepke
+})( Twig || { } );
Twig.js
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.expression.operator.js

+

twig.expression.operator.js

This file handles operator lookups and parsing.

var Twig = (function (Twig) {
     "use strict";
@@ -2803,11 +2826,11 @@
                 token.precidence = 20;
                 token.associativity = Twig.expression.operator.leftToRight;
                 break;
-                
+
             case ',':
                 token.precidence = 18;
                 token.associativity = Twig.expression.operator.leftToRight;
-                break;

Ternary

            case '?':
+                break;

Ternary

            case '?':
             case ':':
                 token.precidence = 16;
                 token.associativity = Twig.expression.operator.rightToLeft;
@@ -2875,8 +2898,8 @@
         Twig.log.trace("Twig.expression.operator.parse: ", "Handling ", operator);
         var a, b, c;
         switch (operator) {
-            case ':':

Ignore

                break;
-              
+            case ':':

Ignore

                break;
+
             case '?':
                 c = stack.pop(); // false expr
                 b = stack.pop(); // true expr
@@ -2887,7 +2910,7 @@
                     stack.push(c);
                 }
                 break;
-            
+
             case '+':
                 b = parseFloat(stack.pop());
                 a = parseFloat(stack.pop());
@@ -3008,28 +3031,28 @@
                 a = stack.pop();
                 stack.push( !containment(a, b) );
                 break;
-                
+
             case 'in':
                 b = stack.pop();
                 a = stack.pop();
                 stack.push( containment(a, b) );
                 break;
-                
+
             case '..':
                 b = stack.pop();
                 a = stack.pop();
                 stack.push( Twig.functions.range(a, b) );
                 break;
-                
+
             default:
                 throw new Twig.Error(operator + " is an unknown operator.");
         }
     };
-    
+
     var containment = function(a, b) {
         if (b.indexOf != undefined) {
             return b.indexOf(a) > -1;
-            
+
         } else {
             var el;
             for (el in b) {
@@ -3043,18 +3066,18 @@
 
     return Twig;
 
-})( Twig || { } );
Twig.js
-Copyright (c) 2011-2012 John Roepke
+})( Twig || { } );
Twig.js
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.filters.js

+

twig.filters.js

-

This file handles parsing filters.

var Twig = (function (Twig) {

Determine object type

    function is(type, obj) {
+

This file handles parsing filters.

var Twig = (function (Twig) {

Determine object type

    function is(type, obj) {
         var clas = Object.prototype.toString.call(obj).slice(8, -1);
         return obj !== undefined && obj !== null && clas === type;
     }
 
-    Twig.filters = {

String Filters

        upper:  function(value) {
+    Twig.filters = {

String Filters

        upper:  function(value) {
             if ( typeof value !== "string" ) {
                return value;
             }
@@ -3096,7 +3119,7 @@
             } else {
                 return 0;
             }
-        },

Array/Object Filters

        reverse: function(value) {
+        },

Array/Object Filters

        reverse: function(value) {
             if (is("Array", value)) {
                 return value.reverse();
             } else if (is("String", value)) {
@@ -3110,7 +3133,7 @@
         sort: function(value) {
             if (is("Array", value)) {
                 return value.sort();
-            } else if (value instanceof Object) {

Sorting objects isn't obvious since the order of + } else if (value instanceof Object) {

Sorting objects isn't obvious since the order of returned keys isn't guaranteedin JavaScript. Because of this we use a "hidden" key called _keys to store the keys in the order we want to return them.

                delete value._keys;
@@ -3142,7 +3165,7 @@
             if (value === undefined){
                 return;
             }
-            
+
             return encodeURIComponent(value);
         },
         join: function(value, params) {
@@ -3192,7 +3215,7 @@
         merge: function(value, params) {
             var obj = [],
                 arr_index = 0,
-                keyset = [];

Check to see if all the objects being merged are arrays

            if (!(value instanceof Array)) {

Create obj as an Object

                obj = { };
+                keyset = [];

Check to see if all the objects being merged are arrays

            if (!(value instanceof Array)) {

Create obj as an Object

                obj = { };
             } else {
                 params.forEach(function(param) {
                     if (!(param instanceof Array)) {
@@ -3214,7 +3237,7 @@
                 keyset = value._keys || Object.keys(value);
                 keyset.forEach(function(key) {
                     obj[key] = value[key];
-                    obj._keys.push(key);

Handle edge case where a number index in an object is greater than + obj._keys.push(key);

Handle edge case where a number index in an object is greater than the array counter. In such a case, the array counter is increased one past the index.

@@ -3225,7 +3248,7 @@ arr_index = int_key + 1; } }); - }

mixin the merge arrays

            params.forEach(function(param) {
+            }

mixin the merge arrays

            params.forEach(function(param) {
                 if (param instanceof Array) {
                     param.forEach(function(val) {
                         if (obj._keys) obj._keys.push(arr_index);
@@ -3287,7 +3310,7 @@
             if (value === undefined){
                 return;
             }
-            
+
             return Twig.lib.strip_tags(value);
         },
 
@@ -3320,7 +3343,7 @@
 
         /**
          * Adapted from: http://phpjs.org/functions/number_format:481
-         */ 
+         */
         number_format: function(value, params) {
             var number = value,
                 decimals = (params && params[0]) ? params[0] : undefined,
@@ -3334,7 +3357,7 @@
                 toFixedFix = function (n, prec) {
                     var k = Math.pow(10, prec);
                     return '' + Math.round(n * k) / k;
-                };

Fix for IE parseFloat(0.55).toFixed(0) = 0;

            s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
+                };

Fix for IE parseFloat(0.55).toFixed(0) = 0;

            s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
             if (s[0].length > 3) {
                 s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
             }
@@ -3381,20 +3404,20 @@
 
     return Twig;
 
-})(Twig || { });
Twig.js
-Copyright (c) 2011-2012 John Roepke
+})(Twig || { });
Twig.js
+Copyright (c) 2011-2013 John Roepke
               2012 Hadrien Lanneau
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.functions.js

+

twig.functions.js

-

This file handles parsing filters.

var Twig = (function (Twig) {

Determine object type

    function is(type, obj) {
+

This file handles parsing filters.

var Twig = (function (Twig) {

Determine object type

    function is(type, obj) {
         var clas = Object.prototype.toString.call(obj).slice(8, -1);
         return obj !== undefined && obj !== null && clas === type;
     }
 
-    Twig.functions = {

attribute, block, constant, date, dump, parent, random,.

        

Range function from http://phpjs.org/functions/range:499 -Used under an MIT License

        range: function (low, high, step) {

http://kevin.vanzonneveld.net + Twig.functions = {

attribute, block, constant, date, dump, parent, random,.

Range function from http://phpjs.org/functions/range:499 +Used under an MIT License

        range: function (low, high, step) {

http://kevin.vanzonneveld.net + original by: Waldo Malqui Silva * example 1: range ( 0, 12 ); * returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] @@ -3494,7 +3517,7 @@ } else { displayVar(variable); } - };

handle no argument case by dumping the entire render context

      if (args.length == 0) args.push(this.context);
+              };

handle no argument case by dumping the entire render context

      if (args.length == 0) args.push(this.context);
 
       args.forEach(function(variable) {
         dumpVar(variable);
@@ -3510,13 +3533,13 @@
                 dateObj = date;
             } else if (Twig.lib.is("String", date)) {
                 dateObj = new Date(Twig.lib.strtotime(date) * 1000);
-            } else if (Twig.lib.is("Number", date)) {

timestamp

                dateObj = new Date(date * 1000);
+            } else if (Twig.lib.is("Number", date)) {

timestamp

                dateObj = new Date(date * 1000);
             } else {
                 throw new Twig.Error("Unable to parse date " + date);
             }
             return dateObj;
         },
-        parent: function() {

Add a placeholder

            return Twig.placeholders.parent;
+        parent: function() {

Add a placeholder

            return Twig.placeholders.parent;
         }
     };
 
@@ -3533,17 +3556,17 @@
 
     return Twig;
 
-})(Twig || { });
Twig.js
-Copyright (c) 2011-2012 John Roepke
+})(Twig || { });
Twig.js
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.tests.js

+

twig.tests.js

This file handles expression tests. (is empty, is not defined, etc...)

var Twig = (function (Twig) {
     "use strict";
     Twig.tests = {
         empty: function(value) {
-            if (value === null || value === undefined) return true;

Handler numbers

            if (typeof value === "number") return false; // numbers are never "empty"

Handle strings and arrays

            if (value.length && value.length > 0) return false;

Handle objects

            for (var key in value) {
+            if (value === null || value === undefined) return true;

Handler numbers

            if (typeof value === "number") return false; // numbers are never "empty"

Handle strings and arrays

            if (value.length && value.length > 0) return false;

Handle objects

            for (var key in value) {
                 if (value.hasOwnProperty(key)) return false;
             }
             return true;
@@ -3586,11 +3609,11 @@
     };
 
     return Twig;
-})( Twig || { } );
Twig.js
-Copyright (c) 2011-2012 John Roepke
+})( Twig || { } );
Twig.js
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.function.js

+

twig.function.js

This file provides extension points and other hooks into the twig functionality.

var Twig = (function (Twig) {
     "use strict";
@@ -3659,15 +3682,15 @@
 
             }, params.load, params.error);
         }
-    };

Extend Twig with a new filter.

    Twig.exports.extendFilter = function(filter, definition) {
+    };

Extend Twig with a new filter.

    Twig.exports.extendFilter = function(filter, definition) {
         Twig.filter.extend(filter, definition);
-    };

Extend Twig with a new function.

    Twig.exports.extendFunction = function(fn, definition) {
+    };

Extend Twig with a new function.

    Twig.exports.extendFunction = function(fn, definition) {
         Twig._function.extend(fn, definition);
-    };

Extend Twig with a new test.

    Twig.exports.extendTest = function(test, definition) {
+    };

Extend Twig with a new test.

    Twig.exports.extendTest = function(test, definition) {
         Twig.test.extend(test, definition);
-    };

Extend Twig with a new definition.

    Twig.exports.extendTag = function(definition) {
+    };

Extend Twig with a new definition.

    Twig.exports.extendTag = function(definition) {
         Twig.logic.extend(definition);
-    };

Provide an environment for extending Twig core. + };

Provide an environment for extending Twig core. Calls fn with the internal Twig object.

    Twig.exports.extend = function(fn) {
         fn(Twig);
     };
@@ -3684,7 +3707,7 @@
     Twig.exports.compile = function(markup, options) {
         var id = options.filename,
             path = options.filename,
-            template;

Try to load the template from the cache

        template = new Twig.Template({
+            template;

Try to load the template from the cache

        template = new Twig.Template({
             data: markup,
             path: path,
             id: id,
@@ -3704,7 +3727,7 @@
      * @param {Function} fn callback.
      */
 
-    Twig.exports.renderFile = function(path, options, fn) {

handle callback in options

        if ('function' == typeof options) {
+    Twig.exports.renderFile = function(path, options, fn) {

handle callback in options

        if ('function' == typeof options) {
             fn = options;
             options = {};
         }
@@ -3714,9 +3737,9 @@
         var params = {
                 path: path,
                 base: options.settings['views'],
-                load: function(template) {

render and return template

                    fn(null, template.render(options));
+                load: function(template) {

render and return template

                    fn(null, template.render(options));
                 }
-            };

mixin any options provided to the express app.

        var view_options = options.settings['twig options'];
+            };

mixin any options provided to the express app.

        var view_options = options.settings['twig options'];
 
         if (view_options) {
             for (var option in view_options) if (view_options.hasOwnProperty(option)) {
@@ -3725,7 +3748,7 @@
         }
 
         Twig.exports.twig(params);
-    };

Express 3 handler

    Twig.exports.__express = Twig.exports.renderFile;
+    };

Express 3 handler

    Twig.exports.__express = Twig.exports.renderFile;
 
     /**
      * Shoud Twig.js cache templates.
@@ -3739,23 +3762,22 @@
     }
 
     return Twig;
-}) (Twig || { });
Twig.js
-Copyright (c) 2011-2012 John Roepke
+}) (Twig || { });
Twig.js
+Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.tests.js

+

twig.tests.js

This file handles compiling templates into JS

var Twig = (function (Twig) {
     /**
      * Namespace for compilation.
      */
-    Twig.compiler = { 
+    Twig.compiler = {
         module: {}
-    };
-    

Compile a Twig Template to output.

    Twig.compiler.compile = function(template, options) {

Get tokens

        var tokens = JSON.stringify(template.tokens)
+    };

Compile a Twig Template to output.

    Twig.compiler.compile = function(template, options) {

Get tokens

        var tokens = JSON.stringify(template.tokens)
             , id = template.id
             , output;
-        
+
         if (options.module) {
             if (Twig.compiler.module[options.module] === undefined) {
                 throw new Twig.Error("Unable to find module type " + options.module);
@@ -3766,7 +3788,7 @@
         }
         return output;
     };
-    
+
     Twig.compiler.module = {
         amd: function(id, tokens, pathToTwig) {
             return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig = Twig.twig;\n' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});';
@@ -3782,25 +3804,29 @@
                     + '\n});'
         }
     };
-    
+
     Twig.compiler.wrap = function(id, tokens) {
         return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n';
     };
-    
+
     return Twig;
-})(Twig || {});//     Twig.js
Copyright (c) 2011-2012 John Roepke
+})(Twig || {});//     Twig.js
Copyright (c) 2011-2013 John Roepke
 Available under the BSD 2-Clause License
 https://github.com/justjohn/twig.js
-

twig.module.js

+

twig.module.js

-

Provide a CommonJS module export.

if (typeof module !== 'undefined' && module.declare) {

Provide a CommonJS Modules/2.0 draft 8 module

    module.declare([], function(require, exports, module) {

Add exports from the Twig exports

        for (key in Twig.exports) {
+

Provide a CommonJS module export.

if (typeof module !== 'undefined' && module.declare) {

Provide a CommonJS Modules/2.0 draft 8 module

    module.declare([], function(require, exports, module) {

Add exports from the Twig exports

        for (key in Twig.exports) {
             if (Twig.exports.hasOwnProperty(key)) {
                 exports[key] = Twig.exports[key];
-            }   
+            }
         }
     });
-} else if (typeof module !== 'undefined' && module.exports) {

Provide a CommonJS Modules/1.1 module

    module.exports = Twig.exports;
-} else {

Export for browser use

    window.twig = Twig.exports.twig;
+} else if (typeof define == 'function' && define.amd) {
+    define(function() {
+        return Twig.exports;
+    });
+} else if (typeof module !== 'undefined' && module.exports) {

Provide a CommonJS Modules/1.1 module

    module.exports = Twig.exports;
+} else {

Export for browser use

    window.twig = Twig.exports.twig;
     window.Twig = Twig.exports;
 }
 
diff --git a/package.json b/package.json
index d1f76109..542f25b1 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "author": "John Roepke  (http://john.sh/)",
   "name": "twig",
   "description": "JS port of the Twig templating language.",
-  "version": "0.5.5",
+  "version": "0.5.6",
   "homepage": "https://github.com/justjohn/twig.js",
   "licenses": [
     { "type": "BSD-2-Clause",
diff --git a/src/twig.compiler.js b/src/twig.compiler.js
index 93d98cd6..ef7a9fd7 100644
--- a/src/twig.compiler.js
+++ b/src/twig.compiler.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -10,17 +10,17 @@ var Twig = (function (Twig) {
     /**
      * Namespace for compilation.
      */
-    Twig.compiler = { 
+    Twig.compiler = {
         module: {}
     };
-    
+
     // Compile a Twig Template to output.
     Twig.compiler.compile = function(template, options) {
         // Get tokens
         var tokens = JSON.stringify(template.tokens)
             , id = template.id
             , output;
-        
+
         if (options.module) {
             if (Twig.compiler.module[options.module] === undefined) {
                 throw new Twig.Error("Unable to find module type " + options.module);
@@ -31,7 +31,7 @@ var Twig = (function (Twig) {
         }
         return output;
     };
-    
+
     Twig.compiler.module = {
         amd: function(id, tokens, pathToTwig) {
             return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig = Twig.twig;\n' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});';
@@ -47,10 +47,10 @@ var Twig = (function (Twig) {
                     + '\n});'
         }
     };
-    
+
     Twig.compiler.wrap = function(id, tokens) {
         return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n';
     };
-    
+
     return Twig;
 })(Twig || {});
\ No newline at end of file
diff --git a/src/twig.core.js b/src/twig.core.js
index 744cf5fa..398ad164 100644
--- a/src/twig.core.js
+++ b/src/twig.core.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/src/twig.exports.js b/src/twig.exports.js
index a0784c0f..0e02f2f1 100644
--- a/src/twig.exports.js
+++ b/src/twig.exports.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/src/twig.expression.js b/src/twig.expression.js
index 895395ff..2fd25e71 100644
--- a/src/twig.expression.js
+++ b/src/twig.expression.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/src/twig.expression.operator.js b/src/twig.expression.operator.js
index e3a48af2..b3f0c3d5 100644
--- a/src/twig.expression.operator.js
+++ b/src/twig.expression.operator.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -29,7 +29,7 @@ var Twig = (function (Twig) {
                 token.precidence = 20;
                 token.associativity = Twig.expression.operator.leftToRight;
                 break;
-                
+
             case ',':
                 token.precidence = 18;
                 token.associativity = Twig.expression.operator.leftToRight;
@@ -107,7 +107,7 @@ var Twig = (function (Twig) {
             case ':':
                 // Ignore
                 break;
-              
+
             case '?':
                 c = stack.pop(); // false expr
                 b = stack.pop(); // true expr
@@ -118,7 +118,7 @@ var Twig = (function (Twig) {
                     stack.push(c);
                 }
                 break;
-            
+
             case '+':
                 b = parseFloat(stack.pop());
                 a = parseFloat(stack.pop());
@@ -239,28 +239,28 @@ var Twig = (function (Twig) {
                 a = stack.pop();
                 stack.push( !containment(a, b) );
                 break;
-                
+
             case 'in':
                 b = stack.pop();
                 a = stack.pop();
                 stack.push( containment(a, b) );
                 break;
-                
+
             case '..':
                 b = stack.pop();
                 a = stack.pop();
                 stack.push( Twig.functions.range(a, b) );
                 break;
-                
+
             default:
                 throw new Twig.Error(operator + " is an unknown operator.");
         }
     };
-    
+
     var containment = function(a, b) {
         if (b.indexOf != undefined) {
             return b.indexOf(a) > -1;
-            
+
         } else {
             var el;
             for (el in b) {
diff --git a/src/twig.filters.js b/src/twig.filters.js
index 7e0d9799..8f57439f 100644
--- a/src/twig.filters.js
+++ b/src/twig.filters.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -110,7 +110,7 @@ var Twig = (function (Twig) {
             if (value === undefined){
                 return;
             }
-            
+
             return encodeURIComponent(value);
         },
         join: function(value, params) {
@@ -266,7 +266,7 @@ var Twig = (function (Twig) {
             if (value === undefined){
                 return;
             }
-            
+
             return Twig.lib.strip_tags(value);
         },
 
@@ -299,7 +299,7 @@ var Twig = (function (Twig) {
 
         /**
          * Adapted from: http://phpjs.org/functions/number_format:481
-         */ 
+         */
         number_format: function(value, params) {
             var number = value,
                 decimals = (params && params[0]) ? params[0] : undefined,
diff --git a/src/twig.functions.js b/src/twig.functions.js
index 201cbf5c..72509e90 100644
--- a/src/twig.functions.js
+++ b/src/twig.functions.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //                   2012 Hadrien Lanneau
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
@@ -17,7 +17,7 @@ var Twig = (function (Twig) {
 
     Twig.functions = {
         //  attribute, block, constant, date, dump, parent, random,.
-        
+
         // Range function from http://phpjs.org/functions/range:499
         // Used under an MIT License
         range: function (low, high, step) {
diff --git a/src/twig.header.js b/src/twig.header.js
index a357dda5..ed47ddd7 100644
--- a/src/twig.header.js
+++ b/src/twig.header.js
@@ -1,11 +1,11 @@
-//     Twig.js 0.5.5
-//     Copyright (c) 2011-2012 John Roepke
+//     Twig.js 0.5.6
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
 var Twig = (function (Twig) {
 
-    Twig.VERSION = "0.5.5";
+    Twig.VERSION = "0.5.6";
 
     return Twig;
 })(Twig || {});
diff --git a/src/twig.logic.js b/src/twig.logic.js
index 971ff918..f2678dc2 100644
--- a/src/twig.logic.js
+++ b/src/twig.logic.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/src/twig.module.js b/src/twig.module.js
index ea8780a1..07a832a1 100644
--- a/src/twig.module.js
+++ b/src/twig.module.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/src/twig.tests.js b/src/twig.tests.js
index 73b926b5..b60a2bba 100644
--- a/src/twig.tests.js
+++ b/src/twig.tests.js
@@ -1,5 +1,5 @@
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/twig.js b/twig.js
index 452261e9..cc7f7ddb 100644
--- a/twig.js
+++ b/twig.js
@@ -1,16 +1,16 @@
-//     Twig.js 0.5.5
-//     Copyright (c) 2011-2012 John Roepke
+//     Twig.js 0.5.6
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
 var Twig = (function (Twig) {
 
-    Twig.VERSION = "0.5.5";
+    Twig.VERSION = "0.5.6";
 
     return Twig;
 })(Twig || {});
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -1518,7 +1518,7 @@ var Twig = (function(Twig) {
 
 })(Twig || { });
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -2317,7 +2317,7 @@ var Twig = (function (Twig) {
 
 })(Twig || { });
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -3297,7 +3297,7 @@ var Twig = (function (Twig) {
 
 })( Twig || { } );
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -3327,7 +3327,7 @@ var Twig = (function (Twig) {
                 token.precidence = 20;
                 token.associativity = Twig.expression.operator.leftToRight;
                 break;
-                
+
             case ',':
                 token.precidence = 18;
                 token.associativity = Twig.expression.operator.leftToRight;
@@ -3405,7 +3405,7 @@ var Twig = (function (Twig) {
             case ':':
                 // Ignore
                 break;
-              
+
             case '?':
                 c = stack.pop(); // false expr
                 b = stack.pop(); // true expr
@@ -3416,7 +3416,7 @@ var Twig = (function (Twig) {
                     stack.push(c);
                 }
                 break;
-            
+
             case '+':
                 b = parseFloat(stack.pop());
                 a = parseFloat(stack.pop());
@@ -3537,28 +3537,28 @@ var Twig = (function (Twig) {
                 a = stack.pop();
                 stack.push( !containment(a, b) );
                 break;
-                
+
             case 'in':
                 b = stack.pop();
                 a = stack.pop();
                 stack.push( containment(a, b) );
                 break;
-                
+
             case '..':
                 b = stack.pop();
                 a = stack.pop();
                 stack.push( Twig.functions.range(a, b) );
                 break;
-                
+
             default:
                 throw new Twig.Error(operator + " is an unknown operator.");
         }
     };
-    
+
     var containment = function(a, b) {
         if (b.indexOf != undefined) {
             return b.indexOf(a) > -1;
-            
+
         } else {
             var el;
             for (el in b) {
@@ -3574,7 +3574,7 @@ var Twig = (function (Twig) {
 
 })( Twig || { } );
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -3685,7 +3685,7 @@ var Twig = (function (Twig) {
             if (value === undefined){
                 return;
             }
-            
+
             return encodeURIComponent(value);
         },
         join: function(value, params) {
@@ -3841,7 +3841,7 @@ var Twig = (function (Twig) {
             if (value === undefined){
                 return;
             }
-            
+
             return Twig.lib.strip_tags(value);
         },
 
@@ -3874,7 +3874,7 @@ var Twig = (function (Twig) {
 
         /**
          * Adapted from: http://phpjs.org/functions/number_format:481
-         */ 
+         */
         number_format: function(value, params) {
             var number = value,
                 decimals = (params && params[0]) ? params[0] : undefined,
@@ -3939,7 +3939,7 @@ var Twig = (function (Twig) {
 
 })(Twig || { });
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //                   2012 Hadrien Lanneau
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
@@ -3957,7 +3957,7 @@ var Twig = (function (Twig) {
 
     Twig.functions = {
         //  attribute, block, constant, date, dump, parent, random,.
-        
+
         // Range function from http://phpjs.org/functions/range:499
         // Used under an MIT License
         range: function (low, high, step) {
@@ -4110,7 +4110,7 @@ var Twig = (function (Twig) {
 
 })(Twig || { });
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -4172,7 +4172,7 @@ var Twig = (function (Twig) {
     return Twig;
 })( Twig || { } );
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -4358,7 +4358,7 @@ var Twig = (function (Twig) {
 }) (Twig || { });
 
 //     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
@@ -4369,17 +4369,17 @@ var Twig = (function (Twig) {
     /**
      * Namespace for compilation.
      */
-    Twig.compiler = { 
+    Twig.compiler = {
         module: {}
     };
-    
+
     // Compile a Twig Template to output.
     Twig.compiler.compile = function(template, options) {
         // Get tokens
         var tokens = JSON.stringify(template.tokens)
             , id = template.id
             , output;
-        
+
         if (options.module) {
             if (Twig.compiler.module[options.module] === undefined) {
                 throw new Twig.Error("Unable to find module type " + options.module);
@@ -4390,7 +4390,7 @@ var Twig = (function (Twig) {
         }
         return output;
     };
-    
+
     Twig.compiler.module = {
         amd: function(id, tokens, pathToTwig) {
             return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig = Twig.twig;\n' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});';
@@ -4406,14 +4406,14 @@ var Twig = (function (Twig) {
                     + '\n});'
         }
     };
-    
+
     Twig.compiler.wrap = function(id, tokens) {
         return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n';
     };
-    
+
     return Twig;
 })(Twig || {});//     Twig.js
-//     Copyright (c) 2011-2012 John Roepke
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
 
diff --git a/twig.min.js b/twig.min.js
index 38cef01a..c3609de1 100644
--- a/twig.min.js
+++ b/twig.min.js
@@ -1,6 +1,6 @@
-//     Twig.js 0.5.5
-//     Copyright (c) 2011-2012 John Roepke
+//     Twig.js 0.5.6
+//     Copyright (c) 2011-2013 John Roepke
 //     Available under the BSD 2-Clause License
 //     https://github.com/justjohn/twig.js
-var Twig=function(a){return a.VERSION="0.5.5",a}(Twig||{}),Twig=function(a){function b(a,b){var c=Object.prototype.toString.call(b).slice(8,-1);return b!==undefined&&b!==null&&c===a}function c(b,c){var d,e,f="/",g=[],h;if(b.url)d=b.url;else{if(!b.path)throw new a.Error("Cannot extend an inline template.");var i=require("path"),j=i.sep||f,k=new RegExp("^\\.{1,2}"+j.replace("\\","\\\\"));b.base!==undefined&&c.match(k)==null?(c=c.replace(b.base,""),d=b.base+j):d=b.path,d=d.replace(j+j,j),f=j}e=d.split(f),e.pop(),e=e.concat(c.split(f));while(e.length>0)h=e.shift(),h!="."&&(h==".."&&g.length>0&&g[g.length-1]!=".."?g.pop():g.push(h));return g.join(f)}return"use strict",a.trace=!1,a.debug=!1,a.cache=!0,a.placeholders={parent:"{{|PARENT|}}"},a.Error=function(a){this.message=a,this.name="TwigException",this.type="TwigException"},a.Error.prototype.toString=function(){return this.name+": "+this.message},a.log={trace:function(){a.trace&&console&&console.log(Array.prototype.slice.call(arguments))},debug:function(){a.debug&&console&&console.log(Array.prototype.slice.call(arguments))}},a.token={},a.token.type={output:"output",logic:"logic",comment:"comment",raw:"raw"},a.token.definitions={output:{type:a.token.type.output,open:"{{",close:"}}"},logic:{type:a.token.type.logic,open:"{%",close:"%}"},comment:{type:a.token.type.comment,open:"{#",close:"#}"}},a.token.strings=['"',"'"],a.token.findStart=function(b){var c={position:null,def:null},d,e,f;for(d in a.token.definitions)a.token.definitions.hasOwnProperty(d)&&(e=a.token.definitions[d],f=b.indexOf(e.open),a.log.trace("Twig.token.findStart: ","Searching for ",e.open," found at ",f),f>=0&&(c.position===null||f=0))throw new a.Error("Unable to find closing bracket '"+c.close+"'"+" opened near template position "+d);e=j,f=!0,o=a.token.strings.length;for(n=0;n0&&l0)e=a.token.findStart(b),a.log.trace("Twig.tokenize: ","Found token: ",e),e.position!==null?(e.position>0&&c.push({type:a.token.type.raw,value:b.substring(0,e.position)}),b=b.substr(e.position+e.def.open.length),d+=e.position+e.def.open.length,f=a.token.findEnd(b,e.def,d),a.log.trace("Twig.tokenize: ","Token ends at ",f),c.push({type:e.def.type,value:b.substring(0,f).trim()}),b=b.substr(f+e.def.close.length),d+=f+e.def.close.length):(c.push({type:a.token.type.raw,value:b}),b="");return c},a.compile=function(b){var c=[],d=[],e=[],f=null,g=null,h=null,i=null,j=null,k=null,l=null,m=null,n=null;while(b.length>0){f=b.shift(),a.log.trace("Compiling token ",f);switch(f.type){case a.token.type.raw:d.length>0?e.push(f):c.push(f);break;case a.token.type.logic:g=a.logic.compile.apply(this,[f]),l=g.type,m=a.logic.handler[l].open,n=a.logic.handler[l].next,a.log.trace("Twig.compile: ","Compiled logic token to ",g," next is: ",n," open is : ",m);if(m!==undefined&&!m){i=d.pop(),j=a.logic.handler[i.type];if(j.next.indexOf(l)<0)throw new Error(l+" not expected after a "+i.type);i.output=i.output||[],i.output=i.output.concat(e),e=[],k={type:a.token.type.logic,token:i},d.length>0?e.push(k):c.push(k)}n!==undefined&&n.length>0?(a.log.trace("Twig.compile: ","Pushing ",g," to logic stack."),d.length>0&&(i=d.pop(),i.output=i.output||[],i.output=i.output.concat(e),d.push(i),e=[]),d.push(g)):m!==undefined&&m&&(k={type:a.token.type.logic,token:g},d.length>0?e.push(k):c.push(k));break;case a.token.type.comment:break;case a.token.type.output:a.expression.compile.apply(this,[f]),d.length>0?e.push(f):c.push(f)}a.log.trace("Twig.compile: "," Output: ",c," Logic Stack: ",d," Pending Output: ",e)}if(d.length>0)throw h=d.pop(),new Error("Unable to find an end tag for "+h.type+", expecting one of "+h.next);return c},a.parse=function(b,c){var d=[],e=!0,f=this;return c=c||{},b.forEach(function(g){a.log.debug("Twig.parse: ","Parsing token: ",g);switch(g.type){case a.token.type.raw:d.push(g.value);break;case a.token.type.logic:var h=g.token,i=a.logic.parse.apply(f,[h,c,e]);i.chain!==undefined&&(e=i.chain),i.context!==undefined&&(c=i.context),i.output!==undefined&&d.push(i.output);break;case a.token.type.comment:break;case a.token.type.output:a.log.debug("Twig.parse: ","Output token: ",g.stack),d.push(a.expression.parse.apply(f,[g.stack,c]))}}),d.join("")},a.prepare=function(b){var c,d;return a.log.debug("Twig.prepare: ","Tokenizing ",b),d=a.tokenize.apply(this,[b]),a.log.debug("Twig.prepare: ","Compiling ",d),c=a.compile.apply(this,[d]),a.log.debug("Twig.prepare: ","Compiled ",c),c},a.Templates={registry:{}},a.validateId=function(b){if(b==="prototype")throw new a.Error(b+" is not a valid twig identifier");if(a.Templates.registry.hasOwnProperty(b))throw new a.Error("There is already a template with the ID "+b);return!0},a.Templates.save=function(b){if(b.id===undefined)throw new a.Error("Unable to save template with no id");a.Templates.registry[b.id]=b},a.Templates.load=function(b){return a.Templates.registry.hasOwnProperty(b)?a.Templates.registry[b]:null},a.Templates.loadRemote=function(b,c,d,e){var f=c.id,g=c.method,h=c.async,i=c.precompiled,j=null;h===undefined&&(h=!0),f===undefined&&(f=b),c.id=f;if(a.cache&&a.Templates.registry.hasOwnProperty(f))return d&&d(a.Templates.registry[f]),a.Templates.registry[f];if(g=="ajax"){if(typeof XMLHttpRequest=="undefined")throw new a.Error("Unsupported platform: Unable to do remote requests because there is no XMLHTTPRequest implementation");var k=new XMLHttpRequest;k.onreadystatechange=function(){var e=null;k.readyState==4&&(a.log.debug("Got template ",k.responseText),i===!0?e=JSON.parse(k.responseText):e=k.responseText,c.url=b,c.data=e,j=new a.Template(c),d&&d(j))},k.open("GET",b,h),k.send()}else(function(){var f=require("fs"),g=require("path"),k=null,l=function(f,g){if(f){e&&e(f);return}i===!0&&(g=JSON.parse(g)),c.data=g,c.path=b,j=new a.Template(c),d&&d(j)};if(h===!0)f.stat(b,function(c,d){if(c||!d.isFile())throw new a.Error("Unable to find template file "+b);f.readFile(b,"utf8",l)});else{if(!f.statSync(b).isFile())throw new a.Error("Unable to find template file "+b);k=f.readFileSync(b,"utf8"),l(undefined,k)}})();return h===!1?j:!0},a.Template=function(c){var d=c.data,e=c.id,f=c.blocks,g=c.base,h=c.path,i=c.url,j=c.options;this.id=e,this.base=g,this.path=h,this.url=i,this.options=j,this.reset(f),b("String",d)?this.tokens=a.prepare.apply(this,[d]):this.tokens=d,e!==undefined&&a.Templates.save(this)},a.Template.prototype.reset=function(b){a.log.debug("Twig.Template.reset","Reseting template "+this.id),this.blocks={},this.child={blocks:b||{}},this.extend=null},a.Template.prototype.render=function(b,d){d=d||{};var e,f;return this.context=b||{},this.reset(),d.blocks&&(this.blocks=d.blocks),e=a.parse.apply(this,[this.tokens,this.context]),this.extend?(f=c(this,this.extend),this.parent=a.Templates.loadRemote(f,{method:this.url?"ajax":"fs",base:this.base,async:!1,id:f,options:this.options}),this.parent.render(this.context,{blocks:this.blocks})):d.output=="blocks"?this.blocks:e},a.Template.prototype.importFile=function(b){var d=c(this,b),e=a.Templates.loadRemote(d,{method:this.url?"ajax":"fs",async:!1,options:this.options,id:d});return e},a.Template.prototype.importBlocks=function(a,b){var c=this.importFile(a),d=this.context,e=this,f;b=b||!1,c.render(d),Object.keys(c.blocks).forEach(function(a){if(b||e.blocks[a]===undefined)e.blocks[a]=c.blocks[a]})},a.Template.prototype.compile=function(b){return a.compiler.compile(this,b)},a}(Twig||{});(function(){"use strict",String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(this===void 0||this===null)throw new TypeError;var b=Object(this),c=b.length>>>0;if(c===0)return-1;var d=0;arguments.length>0&&(d=Number(arguments[1]),d!==d?d=0:d!==0&&d!==Infinity&&d!==-Infinity&&(d=(d>0||-1)*Math.floor(Math.abs(d))));if(d>=c)return-1;var e=d>=0?d:Math.max(c-Math.abs(d),0);for(;e>>0;if({}.toString.call(a)!="[object Function]")throw new TypeError(a+" is not a function");b&&(c=b),d=0;while(d0;c[--b]=a);return c.join("")}var d=function(){return d.cache.hasOwnProperty(arguments[0])||(d.cache[arguments[0]]=d.parse(arguments[0])),d.format.call(null,d.cache[arguments[0]],arguments)};return d.format=function(d,e){var f=1,g=d.length,h="",i,j=[],k,l,m,n,o,p;for(k=0;k=0?"+"+i:i,o=m[4]?m[4]=="0"?"0":m[4].charAt(1):" ",p=m[6]-String(i).length,n=m[6]?c(o,p):"",j.push(m[5]?i+n:n+i)}}return j.join("")},d.cache={},d.parse=function(a){var b=a,c=[],d=[],e=0;while(b){if((c=/^[^\x25]+/.exec(b))!==null)d.push(c[0]);else if((c=/^\x25{2}/.exec(b))!==null)d.push("%");else{if((c=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(b))===null)throw"[sprintf] huh?";if(c[2]){e|=1;var f=[],g=c[2],h=[];if((h=/^([a-z_][a-z_\d]*)/i.exec(g))===null)throw"[sprintf] huh?";f.push(h[1]);while((g=g.substring(h[0].length))!=="")if((h=/^\.([a-z_][a-z_\d]*)/i.exec(g))!==null)f.push(h[1]);else{if((h=/^\[(\d+)\]/.exec(g))===null)throw"[sprintf] huh?";f.push(h[1])}c[2]=f}else e|=2;if(e===3)throw"[sprintf] mixing positional and named placeholders is not (yet) supported";d.push(c)}b=b.substring(c[0].length)}return d},d}(),c=function(a,c){return c.unshift(a),b.apply(null,c)};return a.lib.sprintf=b,a.lib.vsprintf=c,function(){function f(a){return(a=Math.abs(a)%100)%10==1&&a!=11?"st":a%10==2&&a!=12?"nd":a%10==3&&a!=13?"rd":"th"}function g(a){var b=new Date(a.getFullYear()+1,0,4);return(b-a)/864e5<7&&(a.getDay()+6)%7<(b.getDay()+6)%7?b.getFullYear():a.getMonth()>0||a.getDate()>=4?a.getFullYear():a.getFullYear()-((a.getDay()+6)%7-a.getDate()>2?1:0)}function h(a){var b=new Date(g(a),0,4);return b.setDate(b.getDate()-(b.getDay()+6)%7),parseInt((a-b)/6048e5)+1}var b="Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),c="Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),d="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),e="January,February,March,April,May,June,July,August,September,October,November,December".split(",");a.lib.formatDate=function(a,i){if(typeof i!="string"||/^\s*$/.test(i))return a+"";var j=new Date(a.getFullYear(),0,1),k=a;return i.replace(/[dDjlNSwzWFmMntLoYyaABgGhHisu]/g,function(a){switch(a){case"d":return("0"+k.getDate()).replace(/^.+(..)$/,"$1");case"D":return b[k.getDay()];case"j":return k.getDate();case"l":return c[k.getDay()];case"N":return(k.getDay()+6)%7+1;case"S":return f(k.getDate());case"w":return k.getDay();case"z":return Math.ceil((j-k)/864e5);case"W":return("0"+h(k)).replace(/^.(..)$/,"$1");case"F":return e[k.getMonth()];case"m":return("0"+(k.getMonth()+1)).replace(/^.+(..)$/,"$1");case"M":return d[k.getMonth()];case"n":return k.getMonth()+1;case"t":return(new Date(k.getFullYear(),k.getMonth()+1,-1)).getDate();case"L":return(new Date(k.getFullYear(),1,29)).getDate()==29?1:0;case"o":return g(k);case"Y":return k.getFullYear();case"y":return(k.getFullYear()+"").replace(/^.+(..)$/,"$1");case"a":return k.getHours()<12?"am":"pm";case"A":return k.getHours()<12?"AM":"PM";case"B":return Math.floor(((k.getUTCHours()+1)%24+k.getUTCMinutes()/60+k.getUTCSeconds()/3600)*1e3/24);case"g":return k.getHours()%12!=0?k.getHours()%12:12;case"G":return k.getHours();case"h":return("0"+(k.getHours()%12!=0?k.getHours()%12:12)).replace(/^.+(..)$/,"$1");case"H":return("0"+k.getHours()).replace(/^.+(..)$/,"$1");case"i":return("0"+k.getMinutes()).replace(/^.+(..)$/,"$1");case"s":return("0"+k.getSeconds()).replace(/^.+(..)$/,"$1");case"u":return k.getMilliseconds()}})}}(),a.lib.strip_tags=function(a,b){b=(((b||"")+"").toLowerCase().match(/<[a-z][a-z0-9]*>/g)||[]).join("");var c=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,d=/|<\?(?:php)?[\s\S]*?\?>/gi;return a.replace(d,"").replace(c,function(a,c){return b.indexOf("<"+c.toLowerCase()+">")>-1?a:""})},a.lib.strtotime=function(a,b){var c,d,e,f,g="";a=a.replace(/\s{2,}|^\s|\s$/g," "),a=a.replace(/[\t\r\n]/g,"");if(a==="now")return b===null||isNaN(b)?(new Date).getTime()/1e3|0:b|0;if(!isNaN(g=Date.parse(a)))return g/1e3|0;b?b=new Date(b*1e3):b=new Date,a=a.toLowerCase();var h={day:{sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6},mon:["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]},i=function(a){var c=a[2]&&a[2]==="ago",d=(d=a[0]==="last"?-1:1)*(c?-1:1);switch(a[0]){case"last":case"next":switch(a[1].substring(0,3)){case"yea":b.setFullYear(b.getFullYear()+d);break;case"wee":b.setDate(b.getDate()+d*7);break;case"day":b.setDate(b.getDate()+d);break;case"hou":b.setHours(b.getHours()+d);break;case"min":b.setMinutes(b.getMinutes()+d);break;case"sec":b.setSeconds(b.getSeconds()+d);break;case"mon":if(a[1]==="month"){b.setMonth(b.getMonth()+d);break};default:var e=h.day[a[1].substring(0,3)];if(typeof e!="undefined"){var f=e-b.getDay();f===0?f=7*d:f>0?a[0]==="last"&&(f-=7):a[0]==="next"&&(f+=7),b.setDate(b.getDate()+f),b.setHours(0,0,0,0)}}break;default:if(!/\d+/.test(a[0]))return!1;d*=parseInt(a[0],10);switch(a[1].substring(0,3)){case"yea":b.setFullYear(b.getFullYear()+d);break;case"mon":b.setMonth(b.getMonth()+d);break;case"wee":b.setDate(b.getDate()+d*7);break;case"day":b.setDate(b.getDate()+d);break;case"hou":b.setHours(b.getHours()+d);break;case"min":b.setMinutes(b.getMinutes()+d);break;case"sec":b.setSeconds(b.getSeconds()+d)}}return!0};e=a.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);if(e!==null)return e[2]?e[3]||(e[2]+=":00"):e[2]="00:00:00",f=e[1].split(/-/g),f[1]=h.mon[f[1]-1]||f[1],f[0]=+f[0],f[0]=f[0]>=0&&f[0]<=69?"20"+(f[0]<10?"0"+f[0]:f[0]+""):f[0]>=70&&f[0]<=99?"19"+f[0]:f[0]+"",parseInt(this.strtotime(f[2]+" "+f[1]+" "+f[0]+" "+e[2])+(e[4]?e[4]/1e3:""),10);var j="([+-]?\\d+\\s(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)|(last|next)\\s(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))(\\sago)?";e=a.match(new RegExp(j,"gi"));if(e===null)return!1;for(c=0,d=e.length;c=0){f=c.split(",");if(f.length!==2)throw new a.Error("Invalid expression in for loop: "+c);b.key_var=f[0].trim(),b.value_var=f[1].trim()}else b.value_var=c;return b.expression=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:d}]).stack,e&&(b.conditional=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:e}]).stack),delete b.match,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.expression,c]),f=[],g,h=0,i,j=this,k=b.conditional,l=function(a,b){var d=k!==undefined;return{index:a+1,index0:a,revindex:d?undefined:b-a,revindex0:d?undefined:b-a-1,first:a===0,last:d?undefined:a===b-1,length:d?undefined:b,parent:c}},m=function(d,e){var i=a.lib.copy(c);i[b.value_var]=e,b.key_var&&(i[b.key_var]=d),i.loop=l(h,g);if(k===undefined||a.expression.parse.apply(j,[k,i]))f.push(a.parse.apply(j,[b.output,i])),h+=1};return e instanceof Array?(g=e.length,e.forEach(function(a){var b=h;m(b,a)})):e instanceof Object&&(e._keys!==undefined?i=e._keys:i=Object.keys(e),g=i.length,i.forEach(function(a){if(a==="_keys")return;m(a,e[a])})),d=f.length===0,{chain:d,output:f.join("")}}},{type:a.logic.type.endfor,regex:/^endfor$/,next:[],open:!1},{type:a.logic.type.set,regex:/^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*(.+)$/,next:[],open:!0,compile:function(b){var c=b.match[1].trim(),d=b.match[2],e=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:d}]).stack;return b.key=c,b.expression=e,delete b.match,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.expression,c]),f=b.key;return this.context[f]=e,c[f]=e,{chain:d,context:c}}},{type:a.logic.type.filter,regex:/^filter\s+(.+)$/,next:[a.logic.type.endfilter],open:!0,compile:function(b){var c="|"+b.match[1].trim();return b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:c}]).stack,delete b.match,b},parse:function(b,c,d){var e=a.parse.apply(this,[b.output,c]),f=[{type:a.expression.type.string,value:e}].concat(b.stack),g=a.expression.parse.apply(this,[f,c]);return{chain:d,output:g}}},{type:a.logic.type.endfilter,regex:/^endfilter$/,next:[],open:!1},{type:a.logic.type.block,regex:/^block\s+([a-zA-Z0-9_]+)$/,next:[a.logic.type.endblock],open:!0,compile:function(a){return a.block=a.match[1].trim(),delete a.match,a},parse:function(b,c,d){var e="",f="",g=this.blocks[b.block]&&this.blocks[b.block].indexOf(a.placeholders.parent)>-1;if(this.blocks[b.block]===undefined||g)e=a.expression.parse.apply(this,[{type:a.expression.type.string,value:a.parse.apply(this,[b.output,c])},c]),g?this.blocks[b.block]=this.blocks[b.block].replace(a.placeholders.parent,e):this.blocks[b.block]=e;return this.child.blocks[b.block]?f=this.child.blocks[b.block]:f=this.blocks[b.block],{chain:d,output:f}}},{type:a.logic.type.endblock,regex:/^endblock$/,next:[],open:!1},{type:a.logic.type.extends_,regex:/^extends\s+(.+)$/,next:[],open:!0,compile:function(b){var c=b.match[1].trim();return delete b.match,b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:c}]).stack,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.stack,c]);return this.extend=e,{chain:d,output:""}}},{type:a.logic.type.use,regex:/^use\s+(.+)$/,next:[],open:!0,compile:function(b){var c=b.match[1].trim();return delete b.match,b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:c}]).stack,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.stack,c]);return this.importBlocks(e),{chain:d,output:""}}},{type:a.logic.type.include,regex:/^include\s+(ignore missing\s+)?(.+?)\s*(?:with\s+(.+?))?\s*(only)?$/,next:[],open:!0,compile:function(b){var c=b.match,d=c[1]!==undefined,e=c[2].trim(),f=c[3],g=c[4]!==undefined;return delete b.match,b.only=g,b.includeMissing=d,b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:e}]).stack,f!==undefined&&(b.withStack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:f.trim()}]).stack),b},parse:function(b,c,d){var e={},f,g,h;if(!b.only)for(g in c)c.hasOwnProperty(g)&&(e[g]=c[g]);if(b.withStack!==undefined){f=a.expression.parse.apply(this,[b.withStack,c]);for(g in f)f.hasOwnProperty(g)&&(e[g]=f[g])}var i=a.expression.parse.apply(this,[b.stack,e]);return h=this.importFile(i),{chain:d,output:h.render(e)}}},{type:a.logic.type.spaceless,regex:/^spaceless$/,next:[a.logic.type.endspaceless],open:!0,parse:function(b,c,d){var e=a.parse.apply(this,[b.output,c]),f=/>\s+<").trim();return{chain:d,output:g}}},{type:a.logic.type.endspaceless,regex:/^endspaceless$/,next:[],open:!1}],a.logic.handler={},a.logic.extendType=function(b,c){c=c||"Twig.logic.type"+b,a.logic.type[b]=c},a.logic.extend=function(b){if(!b.type)throw new a.Error("Unable to extend logic definition. No type provided for "+b);if(a.logic.type[b.type])throw new a.Error("Unable to extend logic definitions. Type "+b.type+" is already defined.");a.logic.extendType(b.type),a.logic.handler[b.type]=b};while(a.logic.definitions.length>0)a.logic.extend(a.logic.definitions.shift());return a.logic.compile=function(b){var c=b.value.trim(),d=a.logic.tokenize.apply(this,[c]),e=a.logic.handler[d.type];return e.compile&&(d=e.compile.apply(this,[d]),a.log.trace("Twig.logic.compile: ","Compiled logic token to ",d)),d},a.logic.tokenize=function(b){var c={},d=null,e=null,f=null,g=null,h=null,i=null;b=b.trim();for(d in a.logic.handler)if(a.logic.handler.hasOwnProperty(d)){e=a.logic.handler[d].type,f=a.logic.handler[d].regex,g=[],f instanceof Array?g=f:g.push(f);while(g.length>0){h=g.shift(),i=h.exec(b.trim());if(i!==null)return c.type=e,c.match=i,a.log.trace("Twig.logic.tokenize: ","Matched a ",e," regular expression of ",i),c}}throw new a.Error("Unable to parse '"+b.trim()+"'")},a.logic.parse=function(b,c,d){var e="",f;return c=c||{},a.log.debug("Twig.logic.parse: ","Parsing logic token ",b),f=a.logic.handler[b.type],f.parse&&(e=f.parse.apply(this,[b,c,d])),e},a}(Twig||{}),Twig=function(a){"use strict",a.expression={},a.expression.reservedWords=["true","false","null"],a.expression.type={comma:"Twig.expression.type.comma",operator:{unary:"Twig.expression.type.operator.unary",binary:"Twig.expression.type.operator.binary"},string:"Twig.expression.type.string",bool:"Twig.expression.type.bool",array:{start:"Twig.expression.type.array.start",end:"Twig.expression.type.array.end"},object:{start:"Twig.expression.type.object.start",end:"Twig.expression.type.object.end"},parameter:{start:"Twig.expression.type.parameter.start",end:"Twig.expression.type.parameter.end"},key:{period:"Twig.expression.type.key.period",brackets:"Twig.expression.type.key.brackets"},filter:"Twig.expression.type.filter",_function:"Twig.expression.type._function",variable:"Twig.expression.type.variable",number:"Twig.expression.type.number",_null:"Twig.expression.type.null",test:"Twig.expression.type.test"},a.expression.set={operations:[a.expression.type.filter,a.expression.type.operator.unary,a.expression.type.operator.binary,a.expression.type.array.end,a.expression.type.object.end,a.expression.type.parameter.end,a.expression.type.comma,a.expression.type.test],expressions:[a.expression.type._function,a.expression.type.bool,a.expression.type.string,a.expression.type.variable,a.expression.type.number,a.expression.type._null,a.expression.type.parameter.start,a.expression.type.array.start,a.expression.type.object.start]},a.expression.set.operations_extended=a.expression.set.operations.concat([a.expression.type.key.period,a.expression.type.key.brackets]),a.expression.fn={compile:{push:function(a,b,c){c.push(a)},push_both:function(a,b,c){c.push(a),b.push(a)}},parse:{push:function(a,b,c){b.push(a)},push_value:function(a,b,c){b.push(a.value)}}},a.expression.definitions=[{type:a.expression.type.test,regex:/^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*)/,next:a.expression.set.operations.concat([a.expression.type.parameter.start]),compile:function(a,b,c){a.filter=a.match[2],a.modifier=a.match[1],delete a.match,delete a.value,c.push(a)},parse:function(b,c,d){var e=c.pop(),f=b.params&&a.expression.parse.apply(this,[b.params,d]),g=a.test(b.filter,e,f);b.modifier=="not"?c.push(!g):c.push(g)}},{type:a.expression.type.comma,regex:/^,/,next:a.expression.set.expressions,compile:function(b,c,d){var e=c.length-1,f;delete b.match,delete b.value;for(;e>=0;e--){f=c.pop();if(f.type===a.expression.type.object.start||f.type===a.expression.type.parameter.start||f.type===a.expression.type.array.start){c.push(f);break}d.push(f)}d.push(b)}},{type:a.expression.type.operator.binary,regex:/(^[\+\-~%\?\:]|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^and\s+|^or\s+|^in\s+|^not in\s+|^\.\.)/,next:a.expression.set.expressions.concat([a.expression.type.operator.unary]),compile:function(b,c,d){delete b.match,b.value=b.value.trim();var e=b.value,f=a.expression.operator.lookup(e,b);a.log.trace("Twig.expression.compile: ","Operator: ",f," from ",e);while(c.length>0&&(c[c.length-1].type==a.expression.type.operator.unary||c[c.length-1].type==a.expression.type.operator.binary)&&(f.associativity===a.expression.operator.leftToRight&&f.precidence>=c[c.length-1].precidence||f.associativity===a.expression.operator.rightToLeft&&f.precidence>c[c.length-1].precidence)){var g=c.pop();d.push(g)}if(e===":"){if(!c[c.length-1]||c[c.length-1].value!=="?"){var h=d.pop();if(h.type!==a.expression.type.string&&h.type!==a.expression.type.variable&&h.type!==a.expression.type.number)throw new a.Error("Unexpected value before ':' of "+h.type+" = "+h.value);b.key=h.value,d.push(b);return}}else c.push(f)},parse:function(b,c,d){b.key?c.push(b):a.expression.operator.parse(b.value,c)}},{type:a.expression.type.operator.unary,regex:/(^not\s+)/,next:a.expression.set.expressions,compile:function(b,c,d){delete b.match,b.value=b.value.trim();var e=b.value,f=a.expression.operator.lookup(e,b);a.log.trace("Twig.expression.compile: ","Operator: ",f," from ",e);while(c.length>0&&(c[c.length-1].type==a.expression.type.operator.unary||c[c.length-1].type==a.expression.type.operator.binary)&&(f.associativity===a.expression.operator.leftToRight&&f.precidence>=c[c.length-1].precidence||f.associativity===a.expression.operator.rightToLeft&&f.precidence>c[c.length-1].precidence)){var g=c.pop();d.push(g)}c.push(f)},parse:function(b,c,d){a.expression.operator.parse(b.value,c)}},{type:a.expression.type.string,regex:/^(["'])(?:(?=(\\?))\2.)*?\1/,next:a.expression.set.operations,compile:function(b,c,d){var e=b.value;delete b.match,e.substring(0,1)==='"'?e=e.replace('\\"','"'):e=e.replace("\\'","'"),b.value=e.substring(1,e.length-1),a.log.trace("Twig.expression.compile: ","String value: ",b.value),d.push(b)},parse:a.expression.fn.parse.push_value},{type:a.expression.type.parameter.start,regex:/^\(/,next:a.expression.set.expressions.concat([a.expression.type.parameter.end]),compile:a.expression.fn.compile.push_both,parse:a.expression.fn.parse.push},{type:a.expression.type.parameter.end,regex:/^\)/,next:a.expression.set.operations_extended,compile:function(b,c,d){var e,f=b;e=c.pop();while(c.length>0&&e.type!=a.expression.type.parameter.start)d.push(e),e=c.pop();var g=[];while(b.type!==a.expression.type.parameter.start)g.unshift(b),b=d.pop();g.unshift(b);var h=!1;b=d[d.length-1],b===undefined||b.type!==a.expression.type._function&&b.type!==a.expression.type.filter&&b.type!==a.expression.type.test&&b.type!==a.expression.type.key.brackets&&b.type!==a.expression.type.key.period?(f.expression=!0,g.pop(),g.shift(),f.params=g,d.push(f)):(f.expression=!1,b.params=g)},parse:function(b,c,d){var e=[],f=!1,g=null;if(b.expression)g=a.expression.parse.apply(this,[b.params,d]),c.push(g);else{while(c.length>0){g=c.pop();if(g&&g.type&&g.type==a.expression.type.parameter.start){f=!0;break}e.unshift(g)}if(!f)throw new a.Error("Expected end of parameter set.");c.push(e)}}},{type:a.expression.type.array.start,regex:/^\[/,next:a.expression.set.expressions.concat([a.expression.type.array.end]),compile:a.expression.fn.compile.push_both,parse:a.expression.fn.parse.push},{type:a.expression.type.array.end,regex:/^\]/,next:a.expression.set.operations_extended,compile:function(b,c,d){var e=c.length-1,f;for(;e>=0;e--){f=c.pop();if(f.type===a.expression.type.array.start)break;d.push(f)}d.push(b)},parse:function(b,c,d){var e=[],f=!1,g=null;while(c.length>0){g=c.pop();if(g.type&&g.type==a.expression.type.array.start){f=!0;break}e.unshift(g)}if(!f)throw new a.Error("Expected end of array.");c.push(e)}},{type:a.expression.type.object.start,regex:/^\{/,next:a.expression.set.expressions.concat([a.expression.type.object.end]),compile:a.expression.fn.compile.push_both,parse:a.expression.fn.parse.push},{type:a.expression.type.object.end,regex:/^\}/,next:a.expression.set.operations_extended,compile:function(b,c,d){var e=c.length-1,f;for(;e>=0;e--){f=c.pop();if(f&&f.type===a.expression.type.object.start)break;d.push(f)}d.push(b)},parse:function(b,c,d){var e={},f=!1,g=null,h=null,i=!1,j=null;while(c.length>0){g=c.pop();if(g&&g.type&&g.type===a.expression.type.object.start){f=!0;break}if(g&&g.type&&(g.type===a.expression.type.operator.binary||g.type===a.expression.type.operator.unary)&&g.key){if(!i)throw new a.Error("Missing value for key '"+g.key+"' in object definition.");e[g.key]=j,e._keys===undefined&&(e._keys=[]),e._keys.unshift(g.key),j=null,i=!1}else i=!0,j=g}if(!f)throw new a.Error("Unexpected end of object.");c.push(e)}},{type:a.expression.type.filter,regex:/^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/,next:a.expression.set.operations_extended.concat([a.expression.type.parameter.start]),compile:function(a,b,c){a.value=a.match[1],c.push(a)},parse:function(b,c,d){var e=c.pop(),f=b.params&&a.expression.parse.apply(this,[b.params,d]);c.push(a.filter.apply(this,[b.value,e,f]))}},{type:a.expression.type._function,regex:/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/,next:a.expression.type.parameter.start,transform:function(a,b){return"("},compile:function(a,b,c){var d=a.match[1];a.fn=d,delete a.match,delete a.value,c.push(a)},parse:function(b,c,d){var e=b.params&&a.expression.parse.apply(this,[b.params,d]),f=b.fn,g;if(a.functions[f])g=a.functions[f].apply(this,e);else{if(typeof d[f]!="function")throw new a.Error(f+" function does not exist and is not defined in the context");g=d[f].apply(d,e)}c.push(g)}},{type:a.expression.type.variable,regex:/^[a-zA-Z_][a-zA-Z0-9_]*/,next:a.expression.set.operations_extended.concat([a.expression.type.parameter.start]),compile:a.expression.fn.compile.push,validate:function(b,c){return a
+var Twig=function(a){return a.VERSION="0.5.6",a}(Twig||{}),Twig=function(a){function b(a,b){var c=Object.prototype.toString.call(b).slice(8,-1);return b!==undefined&&b!==null&&c===a}function c(b,c){var d,e,f="/",g=[],h;if(b.url)d=b.url;else{if(!b.path)throw new a.Error("Cannot extend an inline template.");var i=require("path"),j=i.sep||f,k=new RegExp("^\\.{1,2}"+j.replace("\\","\\\\"));b.base!==undefined&&c.match(k)==null?(c=c.replace(b.base,""),d=b.base+j):d=b.path,d=d.replace(j+j,j),f=j}e=d.split(f),e.pop(),e=e.concat(c.split(f));while(e.length>0)h=e.shift(),h!="."&&(h==".."&&g.length>0&&g[g.length-1]!=".."?g.pop():g.push(h));return g.join(f)}return"use strict",a.trace=!1,a.debug=!1,a.cache=!0,a.placeholders={parent:"{{|PARENT|}}"},a.Error=function(a){this.message=a,this.name="TwigException",this.type="TwigException"},a.Error.prototype.toString=function(){return this.name+": "+this.message},a.log={trace:function(){a.trace&&console&&console.log(Array.prototype.slice.call(arguments))},debug:function(){a.debug&&console&&console.log(Array.prototype.slice.call(arguments))}},a.token={},a.token.type={output:"output",logic:"logic",comment:"comment",raw:"raw"},a.token.definitions={output:{type:a.token.type.output,open:"{{",close:"}}"},logic:{type:a.token.type.logic,open:"{%",close:"%}"},comment:{type:a.token.type.comment,open:"{#",close:"#}"}},a.token.strings=['"',"'"],a.token.findStart=function(b){var c={position:null,def:null},d,e,f;for(d in a.token.definitions)a.token.definitions.hasOwnProperty(d)&&(e=a.token.definitions[d],f=b.indexOf(e.open),a.log.trace("Twig.token.findStart: ","Searching for ",e.open," found at ",f),f>=0&&(c.position===null||f=0))throw new a.Error("Unable to find closing bracket '"+c.close+"'"+" opened near template position "+d);e=j,f=!0,o=a.token.strings.length;for(n=0;n0&&l0)e=a.token.findStart(b),a.log.trace("Twig.tokenize: ","Found token: ",e),e.position!==null?(e.position>0&&c.push({type:a.token.type.raw,value:b.substring(0,e.position)}),b=b.substr(e.position+e.def.open.length),d+=e.position+e.def.open.length,f=a.token.findEnd(b,e.def,d),a.log.trace("Twig.tokenize: ","Token ends at ",f),c.push({type:e.def.type,value:b.substring(0,f).trim()}),b=b.substr(f+e.def.close.length),d+=f+e.def.close.length):(c.push({type:a.token.type.raw,value:b}),b="");return c},a.compile=function(b){var c=[],d=[],e=[],f=null,g=null,h=null,i=null,j=null,k=null,l=null,m=null,n=null;while(b.length>0){f=b.shift(),a.log.trace("Compiling token ",f);switch(f.type){case a.token.type.raw:d.length>0?e.push(f):c.push(f);break;case a.token.type.logic:g=a.logic.compile.apply(this,[f]),l=g.type,m=a.logic.handler[l].open,n=a.logic.handler[l].next,a.log.trace("Twig.compile: ","Compiled logic token to ",g," next is: ",n," open is : ",m);if(m!==undefined&&!m){i=d.pop(),j=a.logic.handler[i.type];if(j.next.indexOf(l)<0)throw new Error(l+" not expected after a "+i.type);i.output=i.output||[],i.output=i.output.concat(e),e=[],k={type:a.token.type.logic,token:i},d.length>0?e.push(k):c.push(k)}n!==undefined&&n.length>0?(a.log.trace("Twig.compile: ","Pushing ",g," to logic stack."),d.length>0&&(i=d.pop(),i.output=i.output||[],i.output=i.output.concat(e),d.push(i),e=[]),d.push(g)):m!==undefined&&m&&(k={type:a.token.type.logic,token:g},d.length>0?e.push(k):c.push(k));break;case a.token.type.comment:break;case a.token.type.output:a.expression.compile.apply(this,[f]),d.length>0?e.push(f):c.push(f)}a.log.trace("Twig.compile: "," Output: ",c," Logic Stack: ",d," Pending Output: ",e)}if(d.length>0)throw h=d.pop(),new Error("Unable to find an end tag for "+h.type+", expecting one of "+h.next);return c},a.parse=function(b,c){var d=[],e=!0,f=this;return c=c||{},b.forEach(function(g){a.log.debug("Twig.parse: ","Parsing token: ",g);switch(g.type){case a.token.type.raw:d.push(g.value);break;case a.token.type.logic:var h=g.token,i=a.logic.parse.apply(f,[h,c,e]);i.chain!==undefined&&(e=i.chain),i.context!==undefined&&(c=i.context),i.output!==undefined&&d.push(i.output);break;case a.token.type.comment:break;case a.token.type.output:a.log.debug("Twig.parse: ","Output token: ",g.stack),d.push(a.expression.parse.apply(f,[g.stack,c]))}}),d.join("")},a.prepare=function(b){var c,d;return a.log.debug("Twig.prepare: ","Tokenizing ",b),d=a.tokenize.apply(this,[b]),a.log.debug("Twig.prepare: ","Compiling ",d),c=a.compile.apply(this,[d]),a.log.debug("Twig.prepare: ","Compiled ",c),c},a.Templates={registry:{}},a.validateId=function(b){if(b==="prototype")throw new a.Error(b+" is not a valid twig identifier");if(a.Templates.registry.hasOwnProperty(b))throw new a.Error("There is already a template with the ID "+b);return!0},a.Templates.save=function(b){if(b.id===undefined)throw new a.Error("Unable to save template with no id");a.Templates.registry[b.id]=b},a.Templates.load=function(b){return a.Templates.registry.hasOwnProperty(b)?a.Templates.registry[b]:null},a.Templates.loadRemote=function(b,c,d,e){var f=c.id,g=c.method,h=c.async,i=c.precompiled,j=null;h===undefined&&(h=!0),f===undefined&&(f=b),c.id=f;if(a.cache&&a.Templates.registry.hasOwnProperty(f))return d&&d(a.Templates.registry[f]),a.Templates.registry[f];if(g=="ajax"){if(typeof XMLHttpRequest=="undefined")throw new a.Error("Unsupported platform: Unable to do remote requests because there is no XMLHTTPRequest implementation");var k=new XMLHttpRequest;k.onreadystatechange=function(){var e=null;k.readyState==4&&(a.log.debug("Got template ",k.responseText),i===!0?e=JSON.parse(k.responseText):e=k.responseText,c.url=b,c.data=e,j=new a.Template(c),d&&d(j))},k.open("GET",b,h),k.send()}else(function(){var f=require("fs"),g=require("path"),k=null,l=function(f,g){if(f){e&&e(f);return}i===!0&&(g=JSON.parse(g)),c.data=g,c.path=b,j=new a.Template(c),d&&d(j)};if(h===!0)f.stat(b,function(c,d){if(c||!d.isFile())throw new a.Error("Unable to find template file "+b);f.readFile(b,"utf8",l)});else{if(!f.statSync(b).isFile())throw new a.Error("Unable to find template file "+b);k=f.readFileSync(b,"utf8"),l(undefined,k)}})();return h===!1?j:!0},a.Template=function(c){var d=c.data,e=c.id,f=c.blocks,g=c.base,h=c.path,i=c.url,j=c.options;this.id=e,this.base=g,this.path=h,this.url=i,this.options=j,this.reset(f),b("String",d)?this.tokens=a.prepare.apply(this,[d]):this.tokens=d,e!==undefined&&a.Templates.save(this)},a.Template.prototype.reset=function(b){a.log.debug("Twig.Template.reset","Reseting template "+this.id),this.blocks={},this.child={blocks:b||{}},this.extend=null},a.Template.prototype.render=function(b,d){d=d||{};var e,f;return this.context=b||{},this.reset(),d.blocks&&(this.blocks=d.blocks),e=a.parse.apply(this,[this.tokens,this.context]),this.extend?(f=c(this,this.extend),this.parent=a.Templates.loadRemote(f,{method:this.url?"ajax":"fs",base:this.base,async:!1,id:f,options:this.options}),this.parent.render(this.context,{blocks:this.blocks})):d.output=="blocks"?this.blocks:e},a.Template.prototype.importFile=function(b){var d=c(this,b),e=a.Templates.loadRemote(d,{method:this.url?"ajax":"fs",async:!1,options:this.options,id:d});return e},a.Template.prototype.importBlocks=function(a,b){var c=this.importFile(a),d=this.context,e=this,f;b=b||!1,c.render(d),Object.keys(c.blocks).forEach(function(a){if(b||e.blocks[a]===undefined)e.blocks[a]=c.blocks[a]})},a.Template.prototype.compile=function(b){return a.compiler.compile(this,b)},a}(Twig||{});(function(){"use strict",String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(this===void 0||this===null)throw new TypeError;var b=Object(this),c=b.length>>>0;if(c===0)return-1;var d=0;arguments.length>0&&(d=Number(arguments[1]),d!==d?d=0:d!==0&&d!==Infinity&&d!==-Infinity&&(d=(d>0||-1)*Math.floor(Math.abs(d))));if(d>=c)return-1;var e=d>=0?d:Math.max(c-Math.abs(d),0);for(;e>>0;if({}.toString.call(a)!="[object Function]")throw new TypeError(a+" is not a function");b&&(c=b),d=0;while(d0;c[--b]=a);return c.join("")}var d=function(){return d.cache.hasOwnProperty(arguments[0])||(d.cache[arguments[0]]=d.parse(arguments[0])),d.format.call(null,d.cache[arguments[0]],arguments)};return d.format=function(d,e){var f=1,g=d.length,h="",i,j=[],k,l,m,n,o,p;for(k=0;k=0?"+"+i:i,o=m[4]?m[4]=="0"?"0":m[4].charAt(1):" ",p=m[6]-String(i).length,n=m[6]?c(o,p):"",j.push(m[5]?i+n:n+i)}}return j.join("")},d.cache={},d.parse=function(a){var b=a,c=[],d=[],e=0;while(b){if((c=/^[^\x25]+/.exec(b))!==null)d.push(c[0]);else if((c=/^\x25{2}/.exec(b))!==null)d.push("%");else{if((c=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(b))===null)throw"[sprintf] huh?";if(c[2]){e|=1;var f=[],g=c[2],h=[];if((h=/^([a-z_][a-z_\d]*)/i.exec(g))===null)throw"[sprintf] huh?";f.push(h[1]);while((g=g.substring(h[0].length))!=="")if((h=/^\.([a-z_][a-z_\d]*)/i.exec(g))!==null)f.push(h[1]);else{if((h=/^\[(\d+)\]/.exec(g))===null)throw"[sprintf] huh?";f.push(h[1])}c[2]=f}else e|=2;if(e===3)throw"[sprintf] mixing positional and named placeholders is not (yet) supported";d.push(c)}b=b.substring(c[0].length)}return d},d}(),c=function(a,c){return c.unshift(a),b.apply(null,c)};return a.lib.sprintf=b,a.lib.vsprintf=c,function(){function f(a){return(a=Math.abs(a)%100)%10==1&&a!=11?"st":a%10==2&&a!=12?"nd":a%10==3&&a!=13?"rd":"th"}function g(a){var b=new Date(a.getFullYear()+1,0,4);return(b-a)/864e5<7&&(a.getDay()+6)%7<(b.getDay()+6)%7?b.getFullYear():a.getMonth()>0||a.getDate()>=4?a.getFullYear():a.getFullYear()-((a.getDay()+6)%7-a.getDate()>2?1:0)}function h(a){var b=new Date(g(a),0,4);return b.setDate(b.getDate()-(b.getDay()+6)%7),parseInt((a-b)/6048e5)+1}var b="Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),c="Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),d="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),e="January,February,March,April,May,June,July,August,September,October,November,December".split(",");a.lib.formatDate=function(a,i){if(typeof i!="string"||/^\s*$/.test(i))return a+"";var j=new Date(a.getFullYear(),0,1),k=a;return i.replace(/[dDjlNSwzWFmMntLoYyaABgGhHisu]/g,function(a){switch(a){case"d":return("0"+k.getDate()).replace(/^.+(..)$/,"$1");case"D":return b[k.getDay()];case"j":return k.getDate();case"l":return c[k.getDay()];case"N":return(k.getDay()+6)%7+1;case"S":return f(k.getDate());case"w":return k.getDay();case"z":return Math.ceil((j-k)/864e5);case"W":return("0"+h(k)).replace(/^.(..)$/,"$1");case"F":return e[k.getMonth()];case"m":return("0"+(k.getMonth()+1)).replace(/^.+(..)$/,"$1");case"M":return d[k.getMonth()];case"n":return k.getMonth()+1;case"t":return(new Date(k.getFullYear(),k.getMonth()+1,-1)).getDate();case"L":return(new Date(k.getFullYear(),1,29)).getDate()==29?1:0;case"o":return g(k);case"Y":return k.getFullYear();case"y":return(k.getFullYear()+"").replace(/^.+(..)$/,"$1");case"a":return k.getHours()<12?"am":"pm";case"A":return k.getHours()<12?"AM":"PM";case"B":return Math.floor(((k.getUTCHours()+1)%24+k.getUTCMinutes()/60+k.getUTCSeconds()/3600)*1e3/24);case"g":return k.getHours()%12!=0?k.getHours()%12:12;case"G":return k.getHours();case"h":return("0"+(k.getHours()%12!=0?k.getHours()%12:12)).replace(/^.+(..)$/,"$1");case"H":return("0"+k.getHours()).replace(/^.+(..)$/,"$1");case"i":return("0"+k.getMinutes()).replace(/^.+(..)$/,"$1");case"s":return("0"+k.getSeconds()).replace(/^.+(..)$/,"$1");case"u":return k.getMilliseconds()}})}}(),a.lib.strip_tags=function(a,b){b=(((b||"")+"").toLowerCase().match(/<[a-z][a-z0-9]*>/g)||[]).join("");var c=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,d=/|<\?(?:php)?[\s\S]*?\?>/gi;return a.replace(d,"").replace(c,function(a,c){return b.indexOf("<"+c.toLowerCase()+">")>-1?a:""})},a.lib.strtotime=function(a,b){var c,d,e,f,g="";a=a.replace(/\s{2,}|^\s|\s$/g," "),a=a.replace(/[\t\r\n]/g,"");if(a==="now")return b===null||isNaN(b)?(new Date).getTime()/1e3|0:b|0;if(!isNaN(g=Date.parse(a)))return g/1e3|0;b?b=new Date(b*1e3):b=new Date,a=a.toLowerCase();var h={day:{sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6},mon:["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]},i=function(a){var c=a[2]&&a[2]==="ago",d=(d=a[0]==="last"?-1:1)*(c?-1:1);switch(a[0]){case"last":case"next":switch(a[1].substring(0,3)){case"yea":b.setFullYear(b.getFullYear()+d);break;case"wee":b.setDate(b.getDate()+d*7);break;case"day":b.setDate(b.getDate()+d);break;case"hou":b.setHours(b.getHours()+d);break;case"min":b.setMinutes(b.getMinutes()+d);break;case"sec":b.setSeconds(b.getSeconds()+d);break;case"mon":if(a[1]==="month"){b.setMonth(b.getMonth()+d);break};default:var e=h.day[a[1].substring(0,3)];if(typeof e!="undefined"){var f=e-b.getDay();f===0?f=7*d:f>0?a[0]==="last"&&(f-=7):a[0]==="next"&&(f+=7),b.setDate(b.getDate()+f),b.setHours(0,0,0,0)}}break;default:if(!/\d+/.test(a[0]))return!1;d*=parseInt(a[0],10);switch(a[1].substring(0,3)){case"yea":b.setFullYear(b.getFullYear()+d);break;case"mon":b.setMonth(b.getMonth()+d);break;case"wee":b.setDate(b.getDate()+d*7);break;case"day":b.setDate(b.getDate()+d);break;case"hou":b.setHours(b.getHours()+d);break;case"min":b.setMinutes(b.getMinutes()+d);break;case"sec":b.setSeconds(b.getSeconds()+d)}}return!0};e=a.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);if(e!==null)return e[2]?e[3]||(e[2]+=":00"):e[2]="00:00:00",f=e[1].split(/-/g),f[1]=h.mon[f[1]-1]||f[1],f[0]=+f[0],f[0]=f[0]>=0&&f[0]<=69?"20"+(f[0]<10?"0"+f[0]:f[0]+""):f[0]>=70&&f[0]<=99?"19"+f[0]:f[0]+"",parseInt(this.strtotime(f[2]+" "+f[1]+" "+f[0]+" "+e[2])+(e[4]?e[4]/1e3:""),10);var j="([+-]?\\d+\\s(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)|(last|next)\\s(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))(\\sago)?";e=a.match(new RegExp(j,"gi"));if(e===null)return!1;for(c=0,d=e.length;c=0){f=c.split(",");if(f.length!==2)throw new a.Error("Invalid expression in for loop: "+c);b.key_var=f[0].trim(),b.value_var=f[1].trim()}else b.value_var=c;return b.expression=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:d}]).stack,e&&(b.conditional=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:e}]).stack),delete b.match,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.expression,c]),f=[],g,h=0,i,j=this,k=b.conditional,l=function(a,b){var d=k!==undefined;return{index:a+1,index0:a,revindex:d?undefined:b-a,revindex0:d?undefined:b-a-1,first:a===0,last:d?undefined:a===b-1,length:d?undefined:b,parent:c}},m=function(d,e){var i=a.lib.copy(c);i[b.value_var]=e,b.key_var&&(i[b.key_var]=d),i.loop=l(h,g);if(k===undefined||a.expression.parse.apply(j,[k,i]))f.push(a.parse.apply(j,[b.output,i])),h+=1};return e instanceof Array?(g=e.length,e.forEach(function(a){var b=h;m(b,a)})):e instanceof Object&&(e._keys!==undefined?i=e._keys:i=Object.keys(e),g=i.length,i.forEach(function(a){if(a==="_keys")return;m(a,e[a])})),d=f.length===0,{chain:d,output:f.join("")}}},{type:a.logic.type.endfor,regex:/^endfor$/,next:[],open:!1},{type:a.logic.type.set,regex:/^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*(.+)$/,next:[],open:!0,compile:function(b){var c=b.match[1].trim(),d=b.match[2],e=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:d}]).stack;return b.key=c,b.expression=e,delete b.match,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.expression,c]),f=b.key;return this.context[f]=e,c[f]=e,{chain:d,context:c}}},{type:a.logic.type.filter,regex:/^filter\s+(.+)$/,next:[a.logic.type.endfilter],open:!0,compile:function(b){var c="|"+b.match[1].trim();return b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:c}]).stack,delete b.match,b},parse:function(b,c,d){var e=a.parse.apply(this,[b.output,c]),f=[{type:a.expression.type.string,value:e}].concat(b.stack),g=a.expression.parse.apply(this,[f,c]);return{chain:d,output:g}}},{type:a.logic.type.endfilter,regex:/^endfilter$/,next:[],open:!1},{type:a.logic.type.block,regex:/^block\s+([a-zA-Z0-9_]+)$/,next:[a.logic.type.endblock],open:!0,compile:function(a){return a.block=a.match[1].trim(),delete a.match,a},parse:function(b,c,d){var e="",f="",g=this.blocks[b.block]&&this.blocks[b.block].indexOf(a.placeholders.parent)>-1;if(this.blocks[b.block]===undefined||g)e=a.expression.parse.apply(this,[{type:a.expression.type.string,value:a.parse.apply(this,[b.output,c])},c]),g?this.blocks[b.block]=this.blocks[b.block].replace(a.placeholders.parent,e):this.blocks[b.block]=e;return this.child.blocks[b.block]?f=this.child.blocks[b.block]:f=this.blocks[b.block],{chain:d,output:f}}},{type:a.logic.type.endblock,regex:/^endblock$/,next:[],open:!1},{type:a.logic.type.extends_,regex:/^extends\s+(.+)$/,next:[],open:!0,compile:function(b){var c=b.match[1].trim();return delete b.match,b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:c}]).stack,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.stack,c]);return this.extend=e,{chain:d,output:""}}},{type:a.logic.type.use,regex:/^use\s+(.+)$/,next:[],open:!0,compile:function(b){var c=b.match[1].trim();return delete b.match,b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:c}]).stack,b},parse:function(b,c,d){var e=a.expression.parse.apply(this,[b.stack,c]);return this.importBlocks(e),{chain:d,output:""}}},{type:a.logic.type.include,regex:/^include\s+(ignore missing\s+)?(.+?)\s*(?:with\s+(.+?))?\s*(only)?$/,next:[],open:!0,compile:function(b){var c=b.match,d=c[1]!==undefined,e=c[2].trim(),f=c[3],g=c[4]!==undefined;return delete b.match,b.only=g,b.includeMissing=d,b.stack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:e}]).stack,f!==undefined&&(b.withStack=a.expression.compile.apply(this,[{type:a.expression.type.expression,value:f.trim()}]).stack),b},parse:function(b,c,d){var e={},f,g,h;if(!b.only)for(g in c)c.hasOwnProperty(g)&&(e[g]=c[g]);if(b.withStack!==undefined){f=a.expression.parse.apply(this,[b.withStack,c]);for(g in f)f.hasOwnProperty(g)&&(e[g]=f[g])}var i=a.expression.parse.apply(this,[b.stack,e]);return h=this.importFile(i),{chain:d,output:h.render(e)}}},{type:a.logic.type.spaceless,regex:/^spaceless$/,next:[a.logic.type.endspaceless],open:!0,parse:function(b,c,d){var e=a.parse.apply(this,[b.output,c]),f=/>\s+<").trim();return{chain:d,output:g}}},{type:a.logic.type.endspaceless,regex:/^endspaceless$/,next:[],open:!1}],a.logic.handler={},a.logic.extendType=function(b,c){c=c||"Twig.logic.type"+b,a.logic.type[b]=c},a.logic.extend=function(b){if(!b.type)throw new a.Error("Unable to extend logic definition. No type provided for "+b);if(a.logic.type[b.type])throw new a.Error("Unable to extend logic definitions. Type "+b.type+" is already defined.");a.logic.extendType(b.type),a.logic.handler[b.type]=b};while(a.logic.definitions.length>0)a.logic.extend(a.logic.definitions.shift());return a.logic.compile=function(b){var c=b.value.trim(),d=a.logic.tokenize.apply(this,[c]),e=a.logic.handler[d.type];return e.compile&&(d=e.compile.apply(this,[d]),a.log.trace("Twig.logic.compile: ","Compiled logic token to ",d)),d},a.logic.tokenize=function(b){var c={},d=null,e=null,f=null,g=null,h=null,i=null;b=b.trim();for(d in a.logic.handler)if(a.logic.handler.hasOwnProperty(d)){e=a.logic.handler[d].type,f=a.logic.handler[d].regex,g=[],f instanceof Array?g=f:g.push(f);while(g.length>0){h=g.shift(),i=h.exec(b.trim());if(i!==null)return c.type=e,c.match=i,a.log.trace("Twig.logic.tokenize: ","Matched a ",e," regular expression of ",i),c}}throw new a.Error("Unable to parse '"+b.trim()+"'")},a.logic.parse=function(b,c,d){var e="",f;return c=c||{},a.log.debug("Twig.logic.parse: ","Parsing logic token ",b),f=a.logic.handler[b.type],f.parse&&(e=f.parse.apply(this,[b,c,d])),e},a}(Twig||{}),Twig=function(a){"use strict",a.expression={},a.expression.reservedWords=["true","false","null"],a.expression.type={comma:"Twig.expression.type.comma",operator:{unary:"Twig.expression.type.operator.unary",binary:"Twig.expression.type.operator.binary"},string:"Twig.expression.type.string",bool:"Twig.expression.type.bool",array:{start:"Twig.expression.type.array.start",end:"Twig.expression.type.array.end"},object:{start:"Twig.expression.type.object.start",end:"Twig.expression.type.object.end"},parameter:{start:"Twig.expression.type.parameter.start",end:"Twig.expression.type.parameter.end"},key:{period:"Twig.expression.type.key.period",brackets:"Twig.expression.type.key.brackets"},filter:"Twig.expression.type.filter",_function:"Twig.expression.type._function",variable:"Twig.expression.type.variable",number:"Twig.expression.type.number",_null:"Twig.expression.type.null",test:"Twig.expression.type.test"},a.expression.set={operations:[a.expression.type.filter,a.expression.type.operator.unary,a.expression.type.operator.binary,a.expression.type.array.end,a.expression.type.object.end,a.expression.type.parameter.end,a.expression.type.comma,a.expression.type.test],expressions:[a.expression.type._function,a.expression.type.bool,a.expression.type.string,a.expression.type.variable,a.expression.type.number,a.expression.type._null,a.expression.type.parameter.start,a.expression.type.array.start,a.expression.type.object.start]},a.expression.set.operations_extended=a.expression.set.operations.concat([a.expression.type.key.period,a.expression.type.key.brackets]),a.expression.fn={compile:{push:function(a,b,c){c.push(a)},push_both:function(a,b,c){c.push(a),b.push(a)}},parse:{push:function(a,b,c){b.push(a)},push_value:function(a,b,c){b.push(a.value)}}},a.expression.definitions=[{type:a.expression.type.test,regex:/^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*)/,next:a.expression.set.operations.concat([a.expression.type.parameter.start]),compile:function(a,b,c){a.filter=a.match[2],a.modifier=a.match[1],delete a.match,delete a.value,c.push(a)},parse:function(b,c,d){var e=c.pop(),f=b.params&&a.expression.parse.apply(this,[b.params,d]),g=a.test(b.filter,e,f);b.modifier=="not"?c.push(!g):c.push(g)}},{type:a.expression.type.comma,regex:/^,/,next:a.expression.set.expressions,compile:function(b,c,d){var e=c.length-1,f;delete b.match,delete b.value;for(;e>=0;e--){f=c.pop();if(f.type===a.expression.type.object.start||f.type===a.expression.type.parameter.start||f.type===a.expression.type.array.start){c.push(f);break}d.push(f)}d.push(b)}},{type:a.expression.type.operator.binary,regex:/(^[\+\-~%\?\:]|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^and\s+|^or\s+|^in\s+|^not in\s+|^\.\.)/,next:a.expression.set.expressions.concat([a.expression.type.operator.unary]),compile:function(b,c,d){delete b.match,b.value=b.value.trim();var e=b.value,f=a.expression.operator.lookup(e,b);a.log.trace("Twig.expression.compile: ","Operator: ",f," from ",e);while(c.length>0&&(c[c.length-1].type==a.expression.type.operator.unary||c[c.length-1].type==a.expression.type.operator.binary)&&(f.associativity===a.expression.operator.leftToRight&&f.precidence>=c[c.length-1].precidence||f.associativity===a.expression.operator.rightToLeft&&f.precidence>c[c.length-1].precidence)){var g=c.pop();d.push(g)}if(e===":"){if(!c[c.length-1]||c[c.length-1].value!=="?"){var h=d.pop();if(h.type!==a.expression.type.string&&h.type!==a.expression.type.variable&&h.type!==a.expression.type.number)throw new a.Error("Unexpected value before ':' of "+h.type+" = "+h.value);b.key=h.value,d.push(b);return}}else c.push(f)},parse:function(b,c,d){b.key?c.push(b):a.expression.operator.parse(b.value,c)}},{type:a.expression.type.operator.unary,regex:/(^not\s+)/,next:a.expression.set.expressions,compile:function(b,c,d){delete b.match,b.value=b.value.trim();var e=b.value,f=a.expression.operator.lookup(e,b);a.log.trace("Twig.expression.compile: ","Operator: ",f," from ",e);while(c.length>0&&(c[c.length-1].type==a.expression.type.operator.unary||c[c.length-1].type==a.expression.type.operator.binary)&&(f.associativity===a.expression.operator.leftToRight&&f.precidence>=c[c.length-1].precidence||f.associativity===a.expression.operator.rightToLeft&&f.precidence>c[c.length-1].precidence)){var g=c.pop();d.push(g)}c.push(f)},parse:function(b,c,d){a.expression.operator.parse(b.value,c)}},{type:a.expression.type.string,regex:/^(["'])(?:(?=(\\?))\2.)*?\1/,next:a.expression.set.operations,compile:function(b,c,d){var e=b.value;delete b.match,e.substring(0,1)==='"'?e=e.replace('\\"','"'):e=e.replace("\\'","'"),b.value=e.substring(1,e.length-1),a.log.trace("Twig.expression.compile: ","String value: ",b.value),d.push(b)},parse:a.expression.fn.parse.push_value},{type:a.expression.type.parameter.start,regex:/^\(/,next:a.expression.set.expressions.concat([a.expression.type.parameter.end]),compile:a.expression.fn.compile.push_both,parse:a.expression.fn.parse.push},{type:a.expression.type.parameter.end,regex:/^\)/,next:a.expression.set.operations_extended,compile:function(b,c,d){var e,f=b;e=c.pop();while(c.length>0&&e.type!=a.expression.type.parameter.start)d.push(e),e=c.pop();var g=[];while(b.type!==a.expression.type.parameter.start)g.unshift(b),b=d.pop();g.unshift(b);var h=!1;b=d[d.length-1],b===undefined||b.type!==a.expression.type._function&&b.type!==a.expression.type.filter&&b.type!==a.expression.type.test&&b.type!==a.expression.type.key.brackets&&b.type!==a.expression.type.key.period?(f.expression=!0,g.pop(),g.shift(),f.params=g,d.push(f)):(f.expression=!1,b.params=g)},parse:function(b,c,d){var e=[],f=!1,g=null;if(b.expression)g=a.expression.parse.apply(this,[b.params,d]),c.push(g);else{while(c.length>0){g=c.pop();if(g&&g.type&&g.type==a.expression.type.parameter.start){f=!0;break}e.unshift(g)}if(!f)throw new a.Error("Expected end of parameter set.");c.push(e)}}},{type:a.expression.type.array.start,regex:/^\[/,next:a.expression.set.expressions.concat([a.expression.type.array.end]),compile:a.expression.fn.compile.push_both,parse:a.expression.fn.parse.push},{type:a.expression.type.array.end,regex:/^\]/,next:a.expression.set.operations_extended,compile:function(b,c,d){var e=c.length-1,f;for(;e>=0;e--){f=c.pop();if(f.type===a.expression.type.array.start)break;d.push(f)}d.push(b)},parse:function(b,c,d){var e=[],f=!1,g=null;while(c.length>0){g=c.pop();if(g.type&&g.type==a.expression.type.array.start){f=!0;break}e.unshift(g)}if(!f)throw new a.Error("Expected end of array.");c.push(e)}},{type:a.expression.type.object.start,regex:/^\{/,next:a.expression.set.expressions.concat([a.expression.type.object.end]),compile:a.expression.fn.compile.push_both,parse:a.expression.fn.parse.push},{type:a.expression.type.object.end,regex:/^\}/,next:a.expression.set.operations_extended,compile:function(b,c,d){var e=c.length-1,f;for(;e>=0;e--){f=c.pop();if(f&&f.type===a.expression.type.object.start)break;d.push(f)}d.push(b)},parse:function(b,c,d){var e={},f=!1,g=null,h=null,i=!1,j=null;while(c.length>0){g=c.pop();if(g&&g.type&&g.type===a.expression.type.object.start){f=!0;break}if(g&&g.type&&(g.type===a.expression.type.operator.binary||g.type===a.expression.type.operator.unary)&&g.key){if(!i)throw new a.Error("Missing value for key '"+g.key+"' in object definition.");e[g.key]=j,e._keys===undefined&&(e._keys=[]),e._keys.unshift(g.key),j=null,i=!1}else i=!0,j=g}if(!f)throw new a.Error("Unexpected end of object.");c.push(e)}},{type:a.expression.type.filter,regex:/^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/,next:a.expression.set.operations_extended.concat([a.expression.type.parameter.start]),compile:function(a,b,c){a.value=a.match[1],c.push(a)},parse:function(b,c,d){var e=c.pop(),f=b.params&&a.expression.parse.apply(this,[b.params,d]);c.push(a.filter.apply(this,[b.value,e,f]))}},{type:a.expression.type._function,regex:/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/,next:a.expression.type.parameter.start,transform:function(a,b){return"("},compile:function(a,b,c){var d=a.match[1];a.fn=d,delete a.match,delete a.value,c.push(a)},parse:function(b,c,d){var e=b.params&&a.expression.parse.apply(this,[b.params,d]),f=b.fn,g;if(a.functions[f])g=a.functions[f].apply(this,e);else{if(typeof d[f]!="function")throw new a.Error(f+" function does not exist and is not defined in the context");g=d[f].apply(d,e)}c.push(g)}},{type:a.expression.type.variable,regex:/^[a-zA-Z_][a-zA-Z0-9_]*/,next:a.expression.set.operations_extended.concat([a.expression.type.parameter.start]),compile:a.expression.fn.compile.push,validate:function(b,c){return a
 .expression.reservedWords.indexOf(b[0])==-1},parse:function(b,c,d){var e=a.expression.resolve(d[b.value],d);c.push(e)}},{type:a.expression.type.key.period,regex:/^\.([a-zA-Z0-9_]+)/,next:a.expression.set.operations_extended.concat([a.expression.type.parameter.start]),compile:function(a,b,c){a.key=a.match[1],delete a.match,delete a.value,c.push(a)},parse:function(b,c,d){var e=b.params&&a.expression.parse.apply(this,[b.params,d]),f=b.key,g=c.pop(),h;if(g===null||g===undefined){if(this.options.strict_variables)throw new a.Error("Can't access a key "+f+" on an null or undefined object.");return null}var i=function(a){return a.substr(0,1).toUpperCase()+a.substr(1)};typeof g=="object"&&f in g?h=g[f]:g["get"+i(f)]!==undefined?h=g["get"+i(f)]:g["is"+i(f)]!==undefined?h=g["is"+i(f)]:h=null,c.push(a.expression.resolve(h,g,e))}},{type:a.expression.type.key.brackets,regex:/^\[([^\]]*)\]/,next:a.expression.set.operations_extended.concat([a.expression.type.parameter.start]),compile:function(b,c,d){var e=b.match[1];delete b.value,delete b.match,b.stack=a.expression.compile({value:e}).stack,d.push(b)},parse:function(b,c,d){var e=b.params&&a.expression.parse.apply(this,[b.params,d]),f=a.expression.parse.apply(this,[b.stack,d]),g=c.pop(),h;if(g===null||g===undefined){if(this.options.strict_variables)throw new a.Error("Can't access a key "+f+" on an null or undefined object.");return null}typeof g=="object"&&f in g?h=g[f]:h=null,c.push(a.expression.resolve(h,g,e))}},{type:a.expression.type._null,regex:/^null/,next:a.expression.set.operations,compile:function(a,b,c){delete a.match,a.value=null,c.push(a)},parse:a.expression.fn.parse.push_value},{type:a.expression.type.number,regex:/^\-?\d+(\.\d+)?/,next:a.expression.set.operations,compile:function(a,b,c){a.value=Number(a.value),c.push(a)},parse:a.expression.fn.parse.push_value},{type:a.expression.type.bool,regex:/^(true|false)/,next:a.expression.set.operations,compile:function(a,b,c){a.value=a.match[0]=="true",delete a.match,c.push(a)},parse:a.expression.fn.parse.push_value}],a.expression.resolve=function(a,b,c){return typeof a=="function"?a.apply(b,c||[]):a},a.expression.handler={},a.expression.extendType=function(b){a.expression.type[b]="Twig.expression.type."+b},a.expression.extend=function(b){if(!b.type)throw new a.Error("Unable to extend logic definition. No type provided for "+b);a.expression.handler[b.type]=b};while(a.expression.definitions.length>0)a.expression.extend(a.expression.definitions.shift());return a.expression.tokenize=function(b){var c=[],d=0,e=null,f,g,h,i,j,k=[],l;l=function(){var b=Array.prototype.slice.apply(arguments),g=b.pop(),h=b.pop();return a.log.trace("Twig.expression.tokenize","Matched a ",f," regular expression of ",b),e&&e.indexOf(f)<0?(k.push(f+" cannot follow a "+c[c.length-1].type+" at template:"+d+" near '"+b[0].substring(0,20)+"...'"),b[0]):a.expression.handler[f].validate&&!a.expression.handler[f].validate(b,c)?b[0]:(k=[],c.push({type:f,value:b[0],match:b}),j=!0,e=i,d+=b[0].length,a.expression.handler[f].transform?a.expression.handler[f].transform(b,c):"")},a.log.debug("Twig.expression.tokenize","Tokenizing expression ",b);while(b.length>0){b=b.trim();for(f in a.expression.handler)if(a.expression.handler.hasOwnProperty(f)){i=a.expression.handler[f].next,g=a.expression.handler[f].regex,g instanceof Array?h=g:h=[g],j=!1;while(h.length>0)g=h.pop(),b=b.replace(g,l);if(j)break}if(!j)throw k.length>0?new a.Error(k.join(" OR ")):new a.Error("Unable to parse '"+b+"' at template position"+d)}return a.log.trace("Twig.expression.tokenize","Tokenized to ",c),c},a.expression.compile=function(b){var c=b.value,d=a.expression.tokenize(c),e=null,f=[],g=[],h=null;a.log.trace("Twig.expression.compile: ","Compiling ",c);while(d.length>0)e=d.shift(),h=a.expression.handler[e.type],a.log.trace("Twig.expression.compile: ","Compiling ",e),h.compile&&h.compile(e,g,f),a.log.trace("Twig.expression.compile: ","Stack is",g),a.log.trace("Twig.expression.compile: ","Output is",f);while(g.length>0)f.push(g.pop());return a.log.trace("Twig.expression.compile: ","Final output is",f),b.stack=f,delete b.value,b},a.expression.parse=function(b,c){var d=this;b instanceof Array||(b=[b]);var e=[],f=null;return b.forEach(function(b){f=a.expression.handler[b.type],f.parse&&f.parse.apply(d,[b,e,c])}),e.pop()},a}(Twig||{}),Twig=function(a){"use strict",a.expression.operator={leftToRight:"leftToRight",rightToLeft:"rightToLeft"},a.expression.operator.lookup=function(b,c){switch(b){case"..":case"not in":case"in":c.precidence=20,c.associativity=a.expression.operator.leftToRight;break;case",":c.precidence=18,c.associativity=a.expression.operator.leftToRight;break;case"?":case":":c.precidence=16,c.associativity=a.expression.operator.rightToLeft;break;case"or":c.precidence=14,c.associativity=a.expression.operator.leftToRight;break;case"and":c.precidence=13,c.associativity=a.expression.operator.leftToRight;break;case"==":case"!=":c.precidence=9,c.associativity=a.expression.operator.leftToRight;break;case"<":case"<=":case">":case">=":c.precidence=8,c.associativity=a.expression.operator.leftToRight;break;case"~":case"+":case"-":c.precidence=6,c.associativity=a.expression.operator.leftToRight;break;case"//":case"**":case"*":case"/":case"%":c.precidence=5,c.associativity=a.expression.operator.leftToRight;break;case"not":c.precidence=3,c.associativity=a.expression.operator.rightToLeft;break;default:throw new a.Error(b+" is an unknown operator.")}return c.operator=b,c},a.expression.operator.parse=function(c,d){a.log.trace("Twig.expression.operator.parse: ","Handling ",c);var e,f,g;switch(c){case":":break;case"?":g=d.pop(),f=d.pop(),e=d.pop(),e?d.push(f):d.push(g);break;case"+":f=parseFloat(d.pop()),e=parseFloat(d.pop()),d.push(e+f);break;case"-":f=parseFloat(d.pop()),e=parseFloat(d.pop()),d.push(e-f);break;case"*":f=parseFloat(d.pop()),e=parseFloat(d.pop()),d.push(e*f);break;case"/":f=parseFloat(d.pop()),e=parseFloat(d.pop()),d.push(e/f);break;case"//":f=parseFloat(d.pop()),e=parseFloat(d.pop()),d.push(parseInt(e/f));break;case"%":f=parseFloat(d.pop()),e=parseFloat(d.pop()),d.push(e%f);break;case"~":f=d.pop(),e=d.pop(),d.push((e!==undefined?e.toString():"")+(f!==undefined?f.toString():""));break;case"not":case"!":d.push(!d.pop());break;case"<":f=d.pop(),e=d.pop(),d.push(e":f=d.pop(),e=d.pop(),d.push(e>f);break;case">=":f=d.pop(),e=d.pop(),d.push(e>=f);break;case"===":f=d.pop(),e=d.pop(),d.push(e===f);break;case"==":f=d.pop(),e=d.pop(),d.push(e==f);break;case"!==":f=d.pop(),e=d.pop(),d.push(e!==f);break;case"!=":f=d.pop(),e=d.pop(),d.push(e!=f);break;case"or":f=d.pop(),e=d.pop(),d.push(e||f);break;case"and":f=d.pop(),e=d.pop(),d.push(e&&f);break;case"**":f=d.pop(),e=d.pop(),d.push(Math.pow(e,f));break;case"not in":f=d.pop(),e=d.pop(),d.push(!b(e,f));break;case"in":f=d.pop(),e=d.pop(),d.push(b(e,f));break;case"..":f=d.pop(),e=d.pop(),d.push(a.functions.range(e,f));break;default:throw new a.Error(c+" is an unknown operator.")}};var b=function(a,b){if(b.indexOf!=undefined)return b.indexOf(a)>-1;var c;for(c in b)if(b.hasOwnProperty(c)&&b[c]===a)return!0;return!1};return a}(Twig||{}),Twig=function(a){function b(a,b){var c=Object.prototype.toString.call(b).slice(8,-1);return b!==undefined&&b!==null&&c===a}return a.filters={upper:function(a){return typeof a!="string"?a:a.toUpperCase()},lower:function(a){return typeof a!="string"?a:a.toLowerCase()},capitalize:function(a){return typeof a!="string"?a:a.substr(0,1).toUpperCase()+a.substr(1)},title:function(a){return typeof a!="string"?a:a.replace(/(^|\s)([a-z])/g,function(a,b,c){return b+c.toUpperCase()})},length:function(a){return a instanceof Array||typeof a=="string"?a.length:a instanceof Object?a._keys===undefined?Object.keys(a).length:a._keys.length:0},reverse:function(a){if(b("Array",a))return a.reverse();if(b("String",a))return a.split("").reverse().join("");if(a instanceof Object){var c=a._keys||Object.keys(a).reverse();return a._keys=c,a}},sort:function(a){if(b("Array",a))return a.sort();if(a instanceof Object){delete a._keys;var c=Object.keys(a),d=c.sort(function(b,c){return a[b]>a[c]});return a._keys=d,a}},keys:function(a){if(a===undefined)return;var b=a._keys||Object.keys(a),c=[];return b.forEach(function(b){if(b==="_keys")return;a.hasOwnProperty(b)&&c.push(b)}),c},url_encode:function(a){if(a===undefined)return;return encodeURIComponent(a)},join:function(a,b){if(a===undefined)return;var c="",d=[],e=null;return b&&b[0]&&(c=b[0]),a instanceof Array?d=a:(e=a._keys||Object.keys(a),e.forEach(function(b){if(b==="_keys")return;a.hasOwnProperty(b)&&d.push(a[b])})),d.join(c)},"default":function(b,c){if(c===undefined||c.length!==1)throw new a.Error("default filter expects one argument");return b===undefined||b===null||b===""?c[0]:b},json_encode:function(a){return a&&a.hasOwnProperty("_keys")&&delete a._keys,a===undefined||a===null?"null":JSON.stringify(a)},merge:function(b,c){var d=[],e=0,f=[];b instanceof Array?c.forEach(function(a){a instanceof Array||(d={})}):d={},d instanceof Array||(d._keys=[]),b instanceof Array?b.forEach(function(a){d._keys&&d._keys.unshift(e),d[e]=a,e++}):(f=b._keys||Object.keys(b),f.forEach(function(a){d[a]=b[a],d._keys.push(a);var c=parseInt(a,10);!isNaN(c)&&c>=e&&(e=c+1)})),c.forEach(function(a){a instanceof Array?a.forEach(function(a){d._keys&&d._keys.push(e),d[e]=a,e++}):(f=a._keys||Object.keys(a),f.forEach(function(b){d[b]||d._keys.unshift(b),d[b]=a[b];var c=parseInt(b,10);!isNaN(c)&&c>=e&&(e=c+1)}))});if(c.length===0)throw new a.Error("Filter merge expects at least one parameter");return d},date:function(b,c){if(b===undefined)return;var d=a.functions.date(b);return a.lib.formatDate(d,c[0])},replace:function(a,b){if(a===undefined)return;var c=b[0],d;for(d in c)c.hasOwnProperty(d)&&d!=="_keys"&&(a=a.replace(d,c[d]));return a},format:function(b,c){if(b===undefined)return;return a.lib.vsprintf(b,c)},striptags:function(b){if(b===undefined)return;return a.lib.strip_tags(b)},escape:function(a){if(a===undefined)return;return a.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},e:function(b){return a.filters.escape(b)},nl2br:function(b){if(b===undefined)return;var c="
";return a.filters.escape(b).replace(/\r\n/g,c).replace(/\r/g,c).replace(/\n/g,c)},number_format:function(a,b){var c=a,d=b&&b[0]?b[0]:undefined,e=b&&b[1]!==undefined?b[1]:".",f=b&&b[2]!==undefined?b[2]:",";c=(c+"").replace(/[^0-9+\-Ee.]/g,"");var g=isFinite(+c)?+c:0,h=isFinite(+d)?Math.abs(d):0,i="",j=function(a,b){var c=Math.pow(10,b);return""+Math.round(a*c)/c};return i=(h?j(g,h):""+Math.round(g)).split("."),i[0].length>3&&(i[0]=i[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,f)),(i[1]||"").length=0;e--)if(d.indexOf(c.charAt(e))===-1){c=c.substring(0,e+1);break}return d.indexOf(c.charAt(0))===-1?c:""}},a.filter=function(b,c,d){if(!a.filters[b])throw"Unable to find filter "+b;return a.filters[b].apply(this,[c,d])},a.filter.extend=function(b,c){a.filters[b]=c},a}(Twig||{}),Twig=function(a){function b(a,b){var c=Object.prototype.toString.call(b).slice(8,-1);return b!==undefined&&b!==null&&c===a}return a.functions={range:function(a,b,c){var d=[],e,f,g,h=c||1,i=!1;!isNaN(a)&&!isNaN(b)?(e=parseInt(a,10),f=parseInt(b,10)):isNaN(a)&&isNaN(b)?(i=!0,e=a.charCodeAt(0),f=b.charCodeAt(0)):(e=isNaN(a)?0:a,f=isNaN(b)?0:b),g=e>f?!1:!0;if(g)while(e<=f)d.push(i?String.fromCharCode(e):e),e+=h;else while(e>=f)d.push(i?String.fromCharCode(e):e),e-=h;return d},cycle:function(a,b){var c=b%a.length;return a[c]},dump:function(){var a="\n",b=" ",c=0,d="",e=Array.prototype.slice.call(arguments),f=function(a){var c="";while(a>0)a--,c+=b;return c},g=function(b){d+=f(c),typeof b=="object"?h(b):typeof b=="function"?d+="function()"+a:typeof b=="string"?d+="string("+b.length+') "'+b+'"'+a:typeof b=="number"?d+="number("+b+")"+a:typeof b=="boolean"&&(d+="bool("+b+")"+a)},h=function(b){var e;if(b===null)d+="NULL"+a;else if(b===undefined)d+="undefined"+a;else if(typeof b=="object"){d+=f(c)+typeof b,c++,d+="("+function(a){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(b)+") {"+a;for(e in b)d+=f(c)+"["+e+"]=> "+a,g(b[e]);c--,d+=f(c)+"}"+a}else g(b)};return e.length==0&&e.push(this.context),e.forEach(function(a){h(a)}),d},date:function(b,c){var d;if(b===undefined)d=new Date;else if(a.lib.is("Date",b))d=b;else if(a.lib.is("String",b))d=new Date(a.lib.strtotime(b)*1e3);else{if(!a.lib.is("Number",b))throw new a.Error("Unable to parse date "+b);d=new Date(b*1e3)}return d},parent:function(){return a.placeholders.parent}},a._function=function(b,c,d){if(!a.functions[b])throw"Unable to find function "+b;return a.functions[b](c,d)},a._function.extend=function(b,c){a.functions[b]=c},a}(Twig||{}),Twig=function(a){return"use strict",a.tests={empty:function(a){if(a===null||a===undefined)return!0;if(typeof a=="number")return!1;if(a.length&&a.length>0)return!1;for(var b in a)if(a.hasOwnProperty(b))return!1;return!0},odd:function(a){return a%2===1},even:function(a){return a%2===0},divisibleby:function(a,b){return a%b[0]===0},defined:function(a){return a!==undefined},none:function(a){return a===null},"null":function(a){return this.none(a)},sameas:function(a,b){return a===b[0]}},a.test=function(b,c,d){if(!a.tests[b])throw"Test "+b+" is not defined.";return a.tests[b](c,d)},a.test.extend=function(b,c){a.tests[b]=c},a}(Twig||{}),Twig=function(a){return"use strict",a.exports={VERSION:a.VERSION},a.exports.twig=function(c){"use strict";var d=c.id,e={strict_variables:c.strict_variables||!1};d&&a.validateId(d),c.debug!==undefined&&(a.debug=c.debug),c.trace!==undefined&&(a.trace=c.trace);if(c.data!==undefined)return new a.Template({data:c.data,module:c.module,id:d,options:e});if(c.ref!==undefined){if(c.id!==undefined)throw new Error("Both ref and id cannot be set on a twig.js template.");return a.Templates.load(c.ref)}if(c.href!==undefined)return a.Templates.loadRemote(c.href,{id:d,method:"ajax",module:c.module,precompiled:c.precompiled,async:c.async,options:e},c.load,c.error);if(c.path!==undefined)return a.Templates.loadRemote(c.path,{id:d,method:"fs",base:c.base,module:c.module,precompiled:c.precompiled,async:c.async,options:e},c.load,c.error)},a.exports.extendFilter=function(b,c){a.filter.extend(b,c)},a.exports.extendFunction=function(b,c){a._function.extend(b,c)},a.exports.extendTest=function(b,c){a.test.extend(b,c)},a.exports.extendTag=function(b){a.logic.extend(b)},a.exports.extend=function(b){b(a)},a.exports.compile=function(b,c){var d=c.filename,e=c.filename,f;return f=new a.Template({data:b,path:e,id:d,options:c.settings["twig options"]}),function(a){return f.render(a)}},a.exports.renderFile=function(b,c,d){"function"==typeof c&&(d=c,c={}),c=c||{};var e={path:b,base:c.settings.views,load:function(a){d(null,a.render(c))}},f=c.settings["twig options"];if(f)for(var g in f)f.hasOwnProperty(g)&&(e[g]=f[g]);a.exports.twig(e)},a.exports.__express=a.exports.renderFile,a.exports.cache=function(b){a.cache=b},a}(Twig||{}),Twig=function(a){return a.compiler={module:{}},a.compiler.compile=function(b,c){var d=JSON.stringify(b.tokens),e=b.id,f;if(c.module){if(a.compiler.module[c.module]===undefined)throw new a.Error("Unable to find module type "+c.module);f=a.compiler.module[c.module](e,d,c.twig)}else f=a.compiler.wrap(e,d);return f},a.compiler.module={amd:function(b,c,d){return'define(["'+d+'"], function (Twig) {\n\tvar twig = Twig.twig;\n'+a.compiler.wrap(b,c)+"\n\treturn templates;\n});"},node:function(b,c){return'var twig = require("twig").twig;\nexports.template = '+a.compiler.wrap(b,c)},cjs2:function(b,c,d){return'module.declare([{ twig: "'+d+'" }], function (require, exports, module) {\n'+'\tvar twig = require("twig").twig;\n'+"\texports.template = "+a.compiler.wrap(b,c)+"\n});"}},a.compiler.wrap=function(a,b){return'twig({id:"'+a.replace('"','\\"')+'", data:'+b+", precompiled: true});\n"},a}(Twig||{});typeof module!="undefined"&&module.declare?module.declare([],function(a,b,c){for(key in Twig.exports)Twig.exports.hasOwnProperty(key)&&(b[key]=Twig.exports[key])}):typeof define=="function"&&define.amd?define(function(){return Twig.exports}):typeof module!="undefined"&&module.exports?module.exports=Twig.exports:(window.twig=Twig.exports.twig,window.Twig=Twig.exports); \ No newline at end of file