From 551d31426b7375fa3bb392ebb28beaa29125abcf Mon Sep 17 00:00:00 2001 From: John Roepke Date: Mon, 9 Jun 2014 23:57:55 -0400 Subject: [PATCH] Bump version to 0.7.2 and rebuild --- bower.json | 2 +- demos/node_express/public/vendor/twig.js | 4 +- demos/twitter_backbone/vendor/twig.js | 4 +- docs/docco.css | 130 +- docs/tests.md | 190 +- docs/twig.html | 5383 +++++++++++----------- package.json | 2 +- src/twig.header.js | 4 +- twig.js | 4 +- twig.min.js | 4 +- 10 files changed, 2932 insertions(+), 2795 deletions(-) diff --git a/bower.json b/bower.json index fb7f3090..11393a3b 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "twig.js", - "version": "0.7.1", + "version": "0.7.2", "main": "twig.min.js", "ignore": [ "Makefile", diff --git a/demos/node_express/public/vendor/twig.js b/demos/node_express/public/vendor/twig.js index b06fa2f8..5b8298d7 100644 --- a/demos/node_express/public/vendor/twig.js +++ b/demos/node_express/public/vendor/twig.js @@ -1,5 +1,5 @@ /** - * Twig.js 0.7.1 + * Twig.js 0.7.2 * * @copyright 2011-2013 John Roepke * @license Available under the BSD 2-Clause License @@ -8,7 +8,7 @@ var Twig = (function (Twig) { - Twig.VERSION = "0.7.1"; + Twig.VERSION = "0.7.2"; return Twig; })(Twig || {}); diff --git a/demos/twitter_backbone/vendor/twig.js b/demos/twitter_backbone/vendor/twig.js index b06fa2f8..5b8298d7 100644 --- a/demos/twitter_backbone/vendor/twig.js +++ b/demos/twitter_backbone/vendor/twig.js @@ -1,5 +1,5 @@ /** - * Twig.js 0.7.1 + * Twig.js 0.7.2 * * @copyright 2011-2013 John Roepke * @license Available under the BSD 2-Clause License @@ -8,7 +8,7 @@ var Twig = (function (Twig) { - Twig.VERSION = "0.7.1"; + Twig.VERSION = "0.7.2"; return Twig; })(Twig || {}); diff --git a/docs/docco.css b/docs/docco.css index a2899ac8..f690a079 100644 --- a/docs/docco.css +++ b/docs/docco.css @@ -51,17 +51,9 @@ b, strong { font-family: "aller-bold"; } -p { +p, ul, ol { margin: 15px 0 0px; } - .annotation ul, .annotation ol { - margin: 25px 0; - } - .annotation ul li, .annotation ol li { - font-size: 14px; - line-height: 18px; - margin: 10px 0; - } h1, h2, h3, h4, h5, h6 { color: #112233; @@ -78,7 +70,7 @@ h1 { hr { border: 0; - background: 1px #ddd; + background: 1px solid #ddd; height: 1px; margin: 20px 0; } @@ -213,6 +205,7 @@ ul.sections > li > div { } ul.sections > li > div.content { + background: #f5f5ff; overflow-x:auto; -webkit-box-shadow: inset 0 0 5px #e5e5ee; box-shadow: inset 0 0 5px #e5e5ee; @@ -313,6 +306,7 @@ ul.sections > li > div { ul.sections > li > div.content { padding: 13px; vertical-align: top; + background: #f5f5ff; border: none; -webkit-box-shadow: none; box-shadow: none; @@ -382,125 +376,125 @@ pre code { background: #f8f8ff } -pre .hljs-comment, -pre .hljs-template_comment, -pre .hljs-diff .hljs-header, -pre .hljs-javadoc { +pre .comment, +pre .template_comment, +pre .diff .header, +pre .javadoc { color: #408080; font-style: italic } -pre .hljs-keyword, -pre .hljs-assignment, -pre .hljs-literal, -pre .hljs-css .hljs-rule .hljs-keyword, -pre .hljs-winutils, -pre .hljs-javascript .hljs-title, -pre .hljs-lisp .hljs-title, -pre .hljs-subst { +pre .keyword, +pre .assignment, +pre .literal, +pre .css .rule .keyword, +pre .winutils, +pre .javascript .title, +pre .lisp .title, +pre .subst { color: #954121; /*font-weight: bold*/ } -pre .hljs-number, -pre .hljs-hexcolor { +pre .number, +pre .hexcolor { color: #40a070 } -pre .hljs-string, -pre .hljs-tag .hljs-value, -pre .hljs-phpdoc, -pre .hljs-tex .hljs-formula { +pre .string, +pre .tag .value, +pre .phpdoc, +pre .tex .formula { color: #219161; } -pre .hljs-title, -pre .hljs-id { +pre .title, +pre .id { color: #19469D; } -pre .hljs-params { +pre .params { color: #00F; } -pre .hljs-javascript .hljs-title, -pre .hljs-lisp .hljs-title, -pre .hljs-subst { +pre .javascript .title, +pre .lisp .title, +pre .subst { font-weight: normal } -pre .hljs-class .hljs-title, -pre .hljs-haskell .hljs-label, -pre .hljs-tex .hljs-command { +pre .class .title, +pre .haskell .label, +pre .tex .command { color: #458; font-weight: bold } -pre .hljs-tag, -pre .hljs-tag .hljs-title, -pre .hljs-rules .hljs-property, -pre .hljs-django .hljs-tag .hljs-keyword { +pre .tag, +pre .tag .title, +pre .rules .property, +pre .django .tag .keyword { color: #000080; font-weight: normal } -pre .hljs-attribute, -pre .hljs-variable, -pre .hljs-instancevar, -pre .hljs-lisp .hljs-body { +pre .attribute, +pre .variable, +pre .instancevar, +pre .lisp .body { color: #008080 } -pre .hljs-regexp { +pre .regexp { color: #B68 } -pre .hljs-class { +pre .class { color: #458; font-weight: bold } -pre .hljs-symbol, -pre .hljs-ruby .hljs-symbol .hljs-string, -pre .hljs-ruby .hljs-symbol .hljs-keyword, -pre .hljs-ruby .hljs-symbol .hljs-keymethods, -pre .hljs-lisp .hljs-keyword, -pre .hljs-tex .hljs-special, -pre .hljs-input_number { +pre .symbol, +pre .ruby .symbol .string, +pre .ruby .symbol .keyword, +pre .ruby .symbol .keymethods, +pre .lisp .keyword, +pre .tex .special, +pre .input_number { color: #990073 } -pre .hljs-builtin, -pre .hljs-constructor, -pre .hljs-built_in, -pre .hljs-lisp .hljs-title { +pre .builtin, +pre .constructor, +pre .built_in, +pre .lisp .title { color: #0086b3 } -pre .hljs-preprocessor, -pre .hljs-pi, -pre .hljs-doctype, -pre .hljs-shebang, -pre .hljs-cdata { +pre .preprocessor, +pre .pi, +pre .doctype, +pre .shebang, +pre .cdata { color: #999; font-weight: bold } -pre .hljs-deletion { +pre .deletion { background: #fdd } -pre .hljs-addition { +pre .addition { background: #dfd } -pre .hljs-diff .hljs-change { +pre .diff .change { background: #0086b3 } -pre .hljs-chunk { +pre .chunk { color: #aaa } -pre .hljs-tex .hljs-formula { +pre .tex .formula { opacity: 0.5; } diff --git a/docs/tests.md b/docs/tests.md index a4ac40aa..5ae399c6 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -40,7 +40,9 @@ - [abs ->](#twigjs-filters---abs--) - [first ->](#twigjs-filters---first--) - [split ->](#twigjs-filters---split--) + - [batch ->](#twigjs-filters---batch--) - [last ->](#twigjs-filters---last--) + - [round ->](#twigjs-filters---round--) - [Twig.js Loader ->](#twigjs-loader--) - [Twig.js Include ->](#twigjs-include--) - [Twig.js Functions ->](#twigjs-functions--) @@ -75,6 +77,7 @@ twig({ path: 'test/templates/template.twig', async: false }); + // Load the template twig({ref: 'remote-no-extends'}).render({ }).should.equal( "Default Title - body" ); ``` @@ -87,6 +90,7 @@ twig({ path: 'test/templates/blocks-extended-syntax.twig', async: false }); + // Load the template twig({ref: 'endblock-extended-syntax'}).render({ }).should.equal( "This is the only thing." ); ``` @@ -98,6 +102,7 @@ should load a child template and replace the parent block's content. twig({ id: 'child-extends', path: 'test/templates/child.twig', + load: function(template) { template.render({ base: "template.twig" }).should.equal( "Other Title - child" ); done(); @@ -112,6 +117,7 @@ should have access to a parent block content. twig({ id: 'child-parent', path: 'test/templates/child-parent.twig', + load: function(template) { template.render({ base: "template.twig", @@ -129,6 +135,7 @@ should include blocks from another template for horizontal reuse. twig({ id: 'use', path: 'test/templates/use.twig', + load: function(template) { // Load the template template.render({ place: "diner" }).should.equal("Coming soon to a diner near you!" ); @@ -144,6 +151,7 @@ should make the contents of blocks available after they're rendered. twig({ id: 'blocks', path: 'test/templates/blocks.twig', + load: function(template) { // Render the template with the blocks parameter template.render({ place: "block" }, {output: 'blocks'}).msg.should.equal("Coming soon to a block near you!" ); @@ -159,6 +167,7 @@ should render nested blocks. twig({ id: 'blocks-nested', path: 'test/templates/blocks-nested.twig', + load: function(template) { template.render({ }).should.equal( "parent:child" ) done(); @@ -173,6 +182,7 @@ should render extended nested blocks. twig({ id: 'child-blocks-nested', path: 'test/templates/child-blocks-nested.twig', + load: function(template) { template.render({ base: "template.twig" }).should.equal( "Default Title - parent:child" ); done(); @@ -187,6 +197,7 @@ should be able to extend to a absolute template path. twig({ base: 'test/templates', path: 'test/templates/a/child.twig', + load: function(template) { template.render({ base: "b/template.twig" }).should.equal( "Other Title - child" ); done(); @@ -201,6 +212,7 @@ twig({ id: 'inline-parent-template', data: 'Title: {% block title %}parent{% endblock %}' }); + twig({ allowInlineIncludes: true, data: '{% extends "inline-parent-template" %}{% block title %}child{% endblock %}' @@ -214,12 +226,14 @@ should render block content from an included block. ```js twig({ path: 'test/templates/block-function.twig', + load: function(template) { template.render({ base: "block-function-parent.twig", val: "abcd" }) .should.equal( "Child content = abcd / Result: Child content = abcd" ); + done(); } }) @@ -230,11 +244,13 @@ should render block content from a parent block. ```js twig({ path: 'test/templates/block-parent.twig', + load: function(template) { template.render({ base: "block-function-parent.twig" }) .should.equal( "parent block / Result: parent block" ); + done(); } }) @@ -245,12 +261,14 @@ should render block content with outer context. ```js twig({ path: 'test/templates/block-outer-context.twig', + load: function(template) { template.render({ base: "block-outer-context.twig", items: ["twig", "js", "rocks"] }) .should.equal( "Hello twig!Hello js!Hello rocks!twigjsrocks" ); + done(); } }) @@ -396,10 +414,13 @@ should support conditionals on for loops. ```js var test_template = twig({data: '{% for value in test if false %}{{ value }},{% endfor %}'}); test_template.render({test: ["one", "two", "a", "b", "other"]}).should.equal(""); + test_template = twig({data: '{% for value in test if true %}{{ value }}{% endfor %}'}); test_template.render({test: ["a", "s", "d", "f"]}).should.equal("asdf"); + test_template = twig({data: '{% for value in test if value|length > 2 %}{{ value }},{% endfor %}'}); test_template.render({test: ["one", "two", "a", "b", "other"]}).should.equal("one,two,other,"); + test_template = twig({data: '{% for key,item in test if item.show %}{{key}}:{{ item.value }},{% endfor %}'}); test_template.render({test: { a: {show:true, value: "one"}, @@ -426,7 +447,7 @@ should save and load a template by reference. id: 'test', data: '{{ "test" }}' }); - // Load and render the template +// Load and render the template twig({ref: 'test'}).render() .should.equal("test"); ``` @@ -472,6 +493,7 @@ twig({data: '{{ " }} " }}'}).render().should.equal(" }} "); twig({data: '{{ " \\"}} " }}'}).render().should.equal(' "}} '); twig({data: "{{ ' }} ' }}"}).render().should.equal(" }} "); twig({data: "{{ ' \\'}} ' }}"}).render().should.equal(" '}} "); + twig({data: '{{ " \'}} " }}'}).render().should.equal(" '}} "); twig({data: "{{ ' \"}} ' }}"}).render().should.equal(' "}} '); ``` @@ -534,6 +556,7 @@ should recognize null. ```js twig({data: '{{ null == val }}'}).render({val: null}).should.equal( "true" ); twig({data: '{{ null == val }}'}).render({val: undefined}).should.equal( "true" ); + twig({data: '{{ null == val }}'}).render({val: "test"}).should.equal( "false" ); twig({data: '{{ null == val }}'}).render({val: 0}).should.equal( "false" ); twig({data: '{{ null == val }}'}).render({val: false}).should.equal( "false" ); @@ -749,6 +772,7 @@ should parse parenthesis. var test_template = twig({data: '{{ a - (b + c) }}'}), d = {a: 10, b: 4, c: 2}, output = test_template.render(d); + output.should.equal( (d.a - (d.b + d.c)).toString() ); ``` @@ -758,6 +782,7 @@ should parse nested parenthesis. var test_template = twig({data: '{{ a - ((b) + (1 + c)) }}'}), d = {a: 10, b: 4, c: 2}, output = test_template.render(d); + output.should.equal( (d.a - (d.b + 1 + d.c)).toString() ); ``` @@ -809,6 +834,7 @@ numeric_test_data.forEach(function(pair) { var output = test_template.render(pair); // Get expected truncated result var c = parseInt(pair.a/pair.b); + output.should.equal(c.toString() ); }); ``` @@ -834,6 +860,7 @@ should concatanate values. twig({data: '{{ "test" ~ a }}'}).render({a:1234}).should.equal("test1234"); twig({data: '{{ a ~ "test" ~ a }}'}).render({a:1234}).should.equal("1234test1234"); twig({data: '{{ "this" ~ "test" }}'}).render({a:1234}).should.equal("thistest"); + // Test numbers var test_template = twig({data: '{{ a ~ b }}'}); numeric_test_data.forEach(function(pair) { @@ -974,6 +1001,7 @@ should support the ternary operator. var test_template = twig({data: '{{ a ? b:c }}'}) , output_t = test_template.render({a: true, b: "one", c: "two"}) , output_f = test_template.render({a: false, b: "one", c: "two"}); + output_t.should.equal( "one" ); output_f.should.equal( "two" ); ``` @@ -983,6 +1011,7 @@ should support the ternary operator with objects in it. ```js var test_template2 = twig({data: '{{ (a ? {"a":e+f}:{"a":1}).a }}'}) , output2 = test_template2.render({a: true, b: false, e: 1, f: 2}); + output2.should.equal( "3" ); ``` @@ -991,6 +1020,7 @@ should support the ternary operator inside objects. ```js var test_template2 = twig({data: '{{ {"b" : a or b ? {"a":e+f}:{"a":1} }.b.a }}'}) , output2 = test_template2.render({a: false, b: false, e: 1, f: 2}); + output2.should.equal( "1" ); ``` @@ -999,6 +1029,7 @@ should support in/containment functionality for arrays. ```js var test_template = twig({data: '{{ "a" in ["a", "b", "c"] }}'}); test_template.render().should.equal(true.toString()); + var test_template = twig({data: '{{ "d" in ["a", "b", "c"] }}'}); test_template.render().should.equal(false.toString()); ``` @@ -1008,6 +1039,7 @@ should support not in/containment functionality for arrays. ```js var test_template = twig({data: '{{ "a" not in ["a", "b", "c"] }}'}); test_template.render().should.equal(false.toString()); + var test_template = twig({data: '{{ "d" not in ["a", "b", "c"] }}'}); test_template.render().should.equal(true.toString()); ``` @@ -1017,6 +1049,7 @@ should support in/containment functionality for strings. ```js var test_template = twig({data: '{{ "at" in "hat" }}'}); test_template.render().should.equal(true.toString()); + var test_template = twig({data: '{{ "d" in "not" }}'}); test_template.render().should.equal(false.toString()); ``` @@ -1026,6 +1059,7 @@ should support not in/containment functionality for strings. ```js var test_template = twig({data: '{{ "at" not in "hat" }}'}); test_template.render().should.equal(false.toString()); + var test_template = twig({data: '{{ "d" not in "not" }}'}); test_template.render().should.equal(true.toString()); ``` @@ -1035,6 +1069,7 @@ should support in/containment functionality for objects. ```js var test_template = twig({data: '{{ "value" in {"key" : "value", "2": "other"} }}'}); test_template.render().should.equal(true.toString()); + var test_template = twig({data: '{{ "d" in {"key_a" : "no"} }}'}); test_template.render().should.equal(false.toString()); ``` @@ -1044,6 +1079,7 @@ should support not in/containment functionality for objects. ```js var test_template = twig({data: '{{ "value" not in {"key" : "value", "2": "other"} }}'}); test_template.render().should.equal(false.toString()); + var test_template = twig({data: '{{ "d" not in {"key_a" : "no"} }}'}); test_template.render().should.equal(true.toString()); ``` @@ -1054,8 +1090,9 @@ should be able to extend a meta-type tag. ```js var flags = {}; - Twig.extend(function(Twig) { - Twig.exports.extendTag({ + +Twig.extend(function(Twig) { + Twig.exports.extendTag({ type: "flag", regex: /^flag\s+(.+)$/, next: [ ], @@ -1083,27 +1120,29 @@ var flags = {}; output: output }; } - }); }); - var template = twig({data:"{% flag 'enabled' %}"}).render(); - flags.enabled.should.equal(true); +}); + +var template = twig({data:"{% flag 'enabled' %}"}).render(); +flags.enabled.should.equal(true); ``` should be able to extend paired tags. ```js // demo data - var App = { - user: "john", - users: { - john: {level: "admin"}, - tom: {level: "user"} - } - }; - Twig.extend(function(Twig) { - // example of extending a tag type that would - // restrict content to the specified "level" - Twig.exports.extendTag({ +var App = { + user: "john", + users: { + john: {level: "admin"}, + tom: {level: "user"} + } +}; + +Twig.extend(function(Twig) { + // example of extending a tag type that would + // restrict content to the specified "level" + Twig.exports.extendTag({ type: "auth", regex: /^auth\s+(.+)$/, next: ["endauth"], // match the type of the end tag @@ -1134,18 +1173,19 @@ should be able to extend paired tags. output: output }; } - }); - Twig.exports.extendTag({ + }); + Twig.exports.extendTag({ type: "endauth", regex: /^endauth$/, next: [ ], open: false }); - }); - var template = twig({data:"Welcome{% auth 'admin' %} ADMIN{% endauth %}!"}); +}); + +var template = twig({data:"Welcome{% auth 'admin' %} ADMIN{% endauth %}!"}); App.currentUser = "john"; - template.render().should.equal("Welcome ADMIN!"); +template.render().should.equal("Welcome ADMIN!"); App.currentUser = "tom"; template.render().should.equal("Welcome!"); @@ -1252,6 +1292,7 @@ should capitalize the first word in a string. ```js var test_template = twig({data: '{{ "hello world"|capitalize }}' }); test_template.render().should.equal("Hello world" ); + var test_template2 = twig({data: '{{ "HELLO WORLD"|capitalize }}' }); test_template2.render().should.equal("Hello world" ); ``` @@ -1270,6 +1311,7 @@ should capitalize all the words in a string. ```js var test_template = twig({data: '{{ "hello world"|title }}' }); test_template.render().should.equal("Hello World" ); + var test_template2 = twig({data: '{{ "HELLO WORLD"|title }}' }); test_template2.render().should.equal("Hello World" ); ``` @@ -1318,6 +1360,7 @@ should sort an array. ```js var test_template = twig({data: '{{ [1,5,2,7]|sort }}' }); test_template.render().should.equal("1,2,5,7" ); + test_template = twig({data: '{{ ["test","abc",2,7]|sort }}' }); test_template.render().should.equal("2,7,abc,test" ); ``` @@ -1327,6 +1370,7 @@ should sort an object. ```js var test_template = twig({data: "{% set obj = {'c': 1,'d': 5,'t': 2,'e':7}|sort %}{% for key,value in obj|sort %}{{key}}:{{value}} {%endfor %}" }); test_template.render().should.equal("c:1 t:2 d:5 e:7 " ); + test_template = twig({data: "{% set obj = {'m':'test','z':'abc','a':2,'y':7} %}{% for key,value in obj|sort %}{{key}}:{{value}} {%endfor %}" }); test_template.render().should.equal("a:2 y:7 z:abc m:test " ); ``` @@ -1374,6 +1418,7 @@ should return the keys of an object. ```js var test_template = twig({data: '{{ {"a": 1, "b": 4, "c": 5}|keys }}' }); test_template.render().should.equal("a,b,c" ); + test_template = twig({data: '{{ {"0":"a", "1":"b", "2":"c"}|keys }}' }); test_template.render().should.equal("0,1,2" ); ``` @@ -1409,6 +1454,7 @@ should merge an object and an array into an object. // Mixed merging var test_template = twig({data: '{% set obj= ["a", "b"]|merge({"a": "c", "3":4}, ["c", "d"]) %}{% for key in obj|keys|sort %}{{key}}:{{obj[key]}} {%endfor %}' }); test_template.render().should.equal('0:a 1:b 3:4 4:c 5:d a:c ' ); + // Mixed merging(2) test_template = twig({data: '{% set obj= {"1":"a", "a":"b"}|merge(["c", "d"]) %}{% for key in obj|keys %}{{key}}:{{obj[key]}} {%endfor %}' }); test_template.render().should.equal('1:a a:b 2:c 3:d ' ); @@ -1460,6 +1506,7 @@ should provide a default value if a value is empty. ```js var test_template = twig({data: '{{ ""|default("Empty String") }}' }); test_template.render().should.equal("Empty String" ); + test_template = twig({data: '{{ var.key|default("Empty Key") }}' }); test_template.render({'var':{}}).should.equal("Empty Key" ); ``` @@ -1471,6 +1518,7 @@ should recognize timestamps. ```js var template = twig({data: '{{ 27571323556|date("d/m/Y @ H:i:s") }}'}) , date = new Date(27571323556000); // 13/09/2843 @ 08:59:16 EST + template.render().should.equal( stringDate(date) ); ``` @@ -1479,6 +1527,7 @@ should recognize string date formats. ```js var template = twig({data: '{{ "Tue Aug 14 08:52:15 +0000 2007"|date("d/m/Y @ H:i:s") }}'}) , date = new Date(1187081535000); // 14/08/2007 @ 04:52:15 EST + template.render().should.equal( stringDate(date) ); ``` @@ -1755,8 +1804,10 @@ should not fail when passed empty obj, arr or str. ```js var test_template = twig({data: "{{ {}|first }}"}); test_template.render().should.equal(""); + var test_template = twig({data: "{{ []|first }}"}); test_template.render().should.equal(""); + var test_template = twig({data: "{{ myemptystr|first }}"}); test_template.render({myemptystr: ""}).should.equal(""); ``` @@ -1805,6 +1856,43 @@ var test_template = twig({data: "{{ 'aabbcc'|split('', 2) }}"}); test_template.render().should.equal("aa,bb,cc"); ``` + +## batch -> +should work with arrays that require filling (with fill specified). + +```js +var test_template = twig({data: "{{ ['a', 'b', 'c', 'd', 'e', 'f', 'g']|batch(3, 'x') }}"}); +test_template.render().should.equal("a,b,c,d,e,f,g,x,x"); +``` + +should work with arrays that require filling (without fill specified). + +```js +var test_template = twig({data: "{{ ['a', 'b', 'c', 'd', 'e', 'f', 'g']|batch(3) }}"}); +test_template.render().should.equal("a,b,c,d,e,f,g"); +``` + +should work with arrays that do not require filling (with fill specified). + +```js +var test_template = twig({data: "{{ ['a', 'b', 'c', 'd', 'e', 'f']|batch(3, 'x') }}"}); +test_template.render().should.equal("a,b,c,d,e,f"); +``` + +should work with arrays that do not require filling (without fill specified). + +```js +var test_template = twig({data: "{{ ['a', 'b', 'c', 'd', 'e', 'f']|batch(3) }}"}); +test_template.render().should.equal("a,b,c,d,e,f"); +``` + +should return an empty result for an empty array. + +```js +var test_template = twig({data: "{{ []|batch(3, 'x') }}"}); +test_template.render().should.equal(""); +``` + ## last -> should return last character in string. @@ -1828,6 +1916,57 @@ var test_template = twig({data: "{{ {'m':1, 'z':5, 'a':3}|sort|last }}" }); test_template.render().should.equal("5"); ``` + +## round -> +should round up (common). + +```js +var test_template = twig({data: "{{ 2.7|round }}"}); +test_template.render().should.equal("3"); +``` + +should round down (common). + +```js +var test_template = twig({data: "{{ 2.1|round }}"}); +test_template.render().should.equal("2"); +``` + +should truncate input when input decimal places exceeds precision (floor). + +```js +var test_template = twig({data: "{{ 2.1234|round(3, 'floor') }}" }); +test_template.render().should.equal("2.123"); +``` + +should round up (ceil). + +```js +var test_template = twig({data: "{{ 2.1|round(0, 'ceil') }}" }); +test_template.render().should.equal("3"); +``` + +should truncate precision when a negative precision is passed (common). + +```js +var test_template = twig({data: "{{ 21.3|round(-1)}}" }); +test_template.render().should.equal("20"); +``` + +should round up and truncate precision when a negative precision is passed (ceil). + +```js +var test_template = twig({data: "{{ 21.3|round(-1, 'ceil')}}" }); +test_template.render().should.equal("30"); +``` + +should round down and truncate precision when a negative precision is passed (floor). + +```js +var test_template = twig({data: "{{ 21.3|round(-1, 'ceil')}}" }); +test_template.render().should.equal("30"); +``` + # Twig.js Loader -> should load a template from the filesystem asynchronously. @@ -1842,6 +1981,7 @@ twig({ test: "yes", flag: true }).should.equal("Test template = yes\n\nFlag set!"); + done(); } }); @@ -1872,6 +2012,7 @@ twig({ path: 'test/templates/include.twig', async: false }); + // Load the template twig({ref: 'include'}).render({test: 'tst'}).should.equal( "BeforeTest template = tst\n\nAfter" ); ``` @@ -1884,6 +2025,7 @@ twig({ path: 'test/templates/include-with.twig', async: false }); + // Load the template twig({ref: 'include-with'}).render({test: 'tst'}).should.equal( "template: before,tst-mid-template: after,tst" ); ``` @@ -1896,6 +2038,7 @@ twig({ path: 'test/templates/include-only.twig', async: false }); + // Load the template twig({ref: 'include-only'}).render({test: 'tst'}).should.equal( "template: before,-mid-template: after," ); ``` @@ -2319,11 +2462,13 @@ twig({ id: 'other', data: 'another template' }); + var template = twig({ allowInlineIncludes: true, data: 'template with {% include "other" %}' }), output = template.render() + output.should.equal("template with another template"); ``` @@ -2343,6 +2488,7 @@ twig({data: '{% for note in notes %}{{note}}{% endfor %}'}).render({notes:['a', Twig.extendFunction('custom', function(value) { return true; }); + twig({data: '{% if (custom("val") and custom("val")) %}out{% endif %}'}).render({}).should.equal("out"); ``` diff --git a/docs/twig.html b/docs/twig.html index 1e02280b..acfb6651 100644 --- a/docs/twig.html +++ b/docs/twig.html @@ -30,19 +30,19 @@

twig.js

-
/**
- * Twig.js 0.7.1
+            
/**
+ * Twig.js 0.7.2
  *
  * @copyright 2011-2013 John Roepke
  * @license   Available under the BSD 2-Clause License
  * @link      https://github.com/justjohn/twig.js
  */
 
-var Twig = (function (Twig) {
+var Twig = (function (Twig) {
 
-    Twig.VERSION = "0.7.1";
+    Twig.VERSION = "0.7.2";
 
-    return Twig;
+    return Twig;
 })(Twig || {});
@@ -55,15 +55,14 @@

twig.js

Twig.js
-Copyright (c) 2011-2013 John Roepke
-Available under the BSD 2-Clause License
-https://github.com/justjohn/twig.js
-
+Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +
-
-var Twig = (function (Twig) {
-    "use strict";
+
var Twig = (function (Twig) {
+    "use strict";
@@ -79,9 +78,8 @@

twig.core.js

-
-    Twig.trace = false;
-    Twig.debug = false;
+
    Twig.trace = false;
+    Twig.debug = false;
@@ -96,37 +94,37 @@

twig.core.js

-
    Twig.cache = true;
+            
    Twig.cache = true;
 
     Twig.placeholders = {
-        parent: "{{|PARENT|}}"
+        parent: "{{|PARENT|}}"
     };
 
-    /**
+    /**
      * Fallback for Array.indexOf for IE8 et al
      */
-    Twig.indexOf = function (arr, searchElement /*, fromIndex */ ) {
-        if (Array.prototype.hasOwnProperty("indexOf")) {
-            return arr.indexOf(searchElement);
+    Twig.indexOf = function (arr, searchElement /*, fromIndex */ ) {
+        if (Array.prototype.hasOwnProperty("indexOf")) {
+            return arr.indexOf(searchElement);
         }
-        if (arr === void 0 || arr === null) {
-            throw new TypeError();
+        if (arr === void 0 || arr === null) {
+            throw new TypeError();
         }
-        var t = Object(arr);
-        var len = t.length >>> 0;
-        if (len === 0) {
-            return -1;
+        var t = Object(arr);
+        var len = t.length >>> 0;
+        if (len === 0) {
+            return -1;
         }
-        var n = 0;
-        if (arguments.length > 0) {
-            n = Number(arguments[1]);
-            if (n !== n) { // shortcut for verifying if it's NaN
-                n = 0;
-            } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
-                n = (n > 0 || -1) * Math.floor(Math.abs(n));
+        var n = 0;
+        if (arguments.length > 0) {
+            n = Number(arguments[1]);
+            if (n !== n) { // shortcut for verifying if it's NaN
+                n = 0;
+            } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
+                n = (n > 0 || -1) * Math.floor(Math.abs(n));
             }
         }
-        if (n >= len) {
+ if (n >= len) {
@@ -137,20 +135,20 @@

twig.core.js

-

console.log(“indexOf not found1 “, JSON.stringify(searchElement), JSON.stringify(arr));

+

console.log("indexOf not found1 ", JSON.stringify(searchElement), JSON.stringify(arr));

-
            return -1;
+            
            return -1;
         }
-        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
-        for (; k < len; k++) {
-            if (k in t && t[k] === searchElement) {
-                return k;
+        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+        for (; k < len; k++) {
+            if (k in t && t[k] === searchElement) {
+                return k;
             }
         }
-        if (arr == searchElement) {
-            return 0;
+        if (arr == searchElement) {
+            return 0;
         }
@@ -162,23 +160,22 @@

twig.core.js

-

console.log(“indexOf not found2 “, JSON.stringify(searchElement), JSON.stringify(arr));

+

console.log("indexOf not found2 ", JSON.stringify(searchElement), JSON.stringify(arr));

-
-        return -1;
+            
        return -1;
     }
 
-    Twig.forEach = function (arr, callback, thisArg) {
-        if (Array.prototype.forEach ) {
-            return arr.forEach(callback, thisArg);
+    Twig.forEach = function (arr, callback, thisArg) {
+        if (Array.prototype.forEach ) {
+            return arr.forEach(callback, thisArg);
         }
 
-        var T, k;
+        var T, k;
 
-        if ( arr == null ) {
-          throw new TypeError( " this is null or not defined" );
+        if ( arr == null ) {
+          throw new TypeError( " this is null or not defined" );
         }
@@ -196,7 +193,7 @@

twig.core.js

-
        var O = Object(arr);
+
        var O = Object(arr);
@@ -208,13 +205,13 @@

twig.core.js

    -
  1. Let lenValue be the result of calling the Get internal method of O with the argument “length”.
  2. +
  3. Let lenValue be the result of calling the Get internal method of O with the argument "length".
  4. Let len be ToUint32(lenValue).
-
        var len = O.length >>> 0; // Hack to convert O.length to a UInt32
+
        var len = O.length >>> 0; // Hack to convert O.length to a UInt32
@@ -232,8 +229,8 @@

twig.core.js

-
        if ( {}.toString.call(callback) != "[object Function]" ) {
-          throw new TypeError( callback + " is not a function" );
+            
        if ( {}.toString.call(callback) != "[object Function]" ) {
+          throw new TypeError( callback + " is not a function" );
         }
@@ -251,7 +248,7 @@

twig.core.js

-
        if ( thisArg ) {
+            
        if ( thisArg ) {
           T = thisArg;
         }
@@ -270,7 +267,7 @@

twig.core.js

-
        k = 0;
+
        k = 0;
@@ -287,9 +284,9 @@

twig.core.js

-
        while( k < len ) {
+            
        while( k < len ) {
 
-          var kValue;
+ var kValue;
@@ -308,7 +305,7 @@

twig.core.js

-
          if ( k in O ) {
+
          if ( k in O ) {
@@ -375,65 +372,65 @@

twig.core.js

    };
 
-    /**
+    /**
      * Exception thrown by twig.js.
      */
-    Twig.Error = function(message) {
-       this.message = message;
-       this.name = "TwigException";
-       this.type = "TwigException";
+    Twig.Error = function(message) {
+       this.message = message;
+       this.name = "TwigException";
+       this.type = "TwigException";
     };
 
-    /**
+    /**
      * Get the string representation of a Twig error.
      */
-    Twig.Error.prototype.toString = function() {
-        var output = this.name + ": " + this.message;
+    Twig.Error.prototype.toString = function() {
+        var output = this.name + ": " + this.message;
 
-        return output;
+        return output;
     };
 
-    /**
+    /**
      * Wrapper for logging to the console.
      */
     Twig.log = {
-        trace: function() {if (Twig.trace && console) {console.log(Array.prototype.slice.call(arguments));}},
-        debug: function() {if (Twig.debug && console) {console.log(Array.prototype.slice.call(arguments));}},
+        trace: function() {if (Twig.trace && console) {console.log(Array.prototype.slice.call(arguments));}},
+        debug: function() {if (Twig.debug && console) {console.log(Array.prototype.slice.call(arguments));}},
     };
 
-    if (typeof console !== "undefined" && 
-        typeof console.log !== "undefined") {
-        Twig.log.error = function() {
-            console.log.apply(console, arguments);
+    if (typeof console !== "undefined" && 
+        typeof console.log !== "undefined") {
+        Twig.log.error = function() {
+            console.log.apply(console, arguments);
         }
-    } else {
-        Twig.log.error = function(){};
+    } else {
+        Twig.log.error = function(){};
     }
 
-    /**
+    /**
      * Container for methods related to handling high level template tokens
      *      (for example: {{ expression }}, {% logic %}, {# comment #}, raw data)
      */
     Twig.token = {};
 
-    /**
+    /**
      * Token types.
      */
     Twig.token.type = {
-        output:  'output',
-        logic:   'logic',
-        comment: 'comment',
-        raw:     'raw'
+        output:  'output',
+        logic:   'logic',
+        comment: 'comment',
+        raw:     'raw'
     };
 
-    /**
+    /**
      * Token syntax definitions.
      */
     Twig.token.definitions = [
         {
             type: Twig.token.type.raw,
-            open: '{% raw %}',
-            close: '{% endraw %}'
+            open: '{% raw %}',
+            close: '{% endraw %}'
         },
@@ -452,8 +449,8 @@

twig.core.js

        {
             type: Twig.token.type.output,
-            open: '{{',
-            close: '}}'
+            open: '{{',
+            close: '}}'
         },
@@ -472,8 +469,8 @@

twig.core.js

        {
             type: Twig.token.type.logic,
-            open: '{%',
-            close: '%}'
+            open: '{%',
+            close: '%}'
         },
@@ -492,32 +489,32 @@

twig.core.js

        {
             type: Twig.token.type.comment,
-            open: '{#',
-            close: '#}'
+            open: '{#',
+            close: '#}'
         }
     ];
 
 
-    /**
+    /**
      * What characters start "strings" in token definitions. We need this to ignore token close
      * strings inside an expression.
      */
-    Twig.token.strings = ['"', "'"];
+    Twig.token.strings = ['"', "'"];
 
-    Twig.token.findStart = function (template) {
-        var output = {
-                position: null,
-                def: null
+    Twig.token.findStart = function (template) {
+        var output = {
+                position: null,
+                def: null
             },
             i,
             token_template,
             first_key_position;
 
-        for (i=0;i<Twig.token.definitions.length;i++) {
+        for (i=0;i<Twig.token.definitions.length;i++) {
             token_template = Twig.token.definitions[i];
             first_key_position = template.indexOf(token_template.open);
 
-            Twig.log.trace("Twig.token.findStart: ", "Searching for ", token_template.open, " found at ", first_key_position);
+ Twig.log.trace("Twig.token.findStart: ", "Searching for ", token_template.open, " found at ", first_key_position); @@ -532,19 +529,19 @@

twig.core.js

-
            if (first_key_position >= 0 && (output.position === null || first_key_position < output.position)) {
+            
            if (first_key_position >= 0 && (output.position === null || first_key_position < output.position)) {
                 output.position = first_key_position;
                 output.def = token_template;
             }
         }
 
-        return output;
+        return output;
     };
 
-    Twig.token.findEnd = function (template, token_def, start) {
-        var end = null,
-            found = false,
-            offset = 0,
+ Twig.token.findEnd = function (template, token_def, start) { + var end = null, + found = false, + offset = 0,
@@ -559,12 +556,12 @@

twig.core.js

-
            str_pos = null,
-            str_found = null,
-            pos = null,
-            end_offset = null,
-            this_str_pos = null,
-            end_str_pos = null,
+
            str_pos = null,
+            str_found = null,
+            pos = null,
+            end_offset = null,
+            this_str_pos = null,
+            end_str_pos = null,
@@ -582,15 +579,15 @@

twig.core.js

            i,
             l;
 
-        while (!found) {
-            str_pos = null;
-            str_found = null;
+        while (!found) {
+            str_pos = null;
+            str_found = null;
             pos = template.indexOf(token_def.close, offset);
 
-            if (pos >= 0) {
+            if (pos >= 0) {
                 end = pos;
-                found = true;
-            } else {
+ found = true; + } else { @@ -605,8 +602,8 @@

twig.core.js

-
                throw new Twig.Error("Unable to find closing bracket '" + token_def.close +
-                                "'" + " opened near template position " + start);
+            
                throw new Twig.Error("Unable to find closing bracket '" + token_def.close +
+                                "'" + " opened near template position " + start);
             }
@@ -623,16 +620,16 @@

twig.core.js

-
            if (token_def.type === Twig.token.type.comment) {
-              break;
+            
            if (token_def.type === Twig.token.type.comment) {
+              break;
             }
 
             l = Twig.token.strings.length;
-            for (i = 0; i < l; i += 1) {
+            for (i = 0; i < l; i += 1) {
                 this_str_pos = template.indexOf(Twig.token.strings[i], offset);
 
-                if (this_str_pos > 0 && this_str_pos < pos &&
-                        (str_pos === null || this_str_pos < str_pos)) {
+                if (this_str_pos > 0 && this_str_pos < pos &&
+                        (str_pos === null || this_str_pos < str_pos)) {
                     str_pos = this_str_pos;
                     str_found = Twig.token.strings[i];
                 }
@@ -647,18 +644,18 @@ 

twig.core.js

-

We found a string before the end of the token, now find the string’s end and set the search offset to it

+

We found a string before the end of the token, now find the string's end and set the search offset to it

-
            if (str_pos !== null) {
-                end_offset = str_pos + 1;
-                end = null;
-                found = false;
-                while (true) {
+            
            if (str_pos !== null) {
+                end_offset = str_pos + 1;
+                end = null;
+                found = false;
+                while (true) {
                     end_str_pos = template.indexOf(str_found, end_offset);
-                    if (end_str_pos < 0) {
-                        throw "Unclosed string in template";
+                    if (end_str_pos < 0) {
+                        throw "Unclosed string in template";
                     }
@@ -674,23 +671,23 @@

twig.core.js

-
                    if (template.substr(end_str_pos - 1, 1) !== "\\") {
-                        offset = end_str_pos + 1;
-                        break;
-                    } else {
-                        end_offset = end_str_pos + 1;
+            
                    if (template.substr(end_str_pos - 1, 1) !== "\\") {
+                        offset = end_str_pos + 1;
+                        break;
+                    } else {
+                        end_offset = end_str_pos + 1;
                     }
                 }
             }
         }
-        return end;
+        return end;
     };
 
-    /**
+    /**
      * Convert a template into high-level tokens.
      */
-    Twig.tokenize = function (template) {
-        var tokens = [],
+ Twig.tokenize = function (template) { + var tokens = [],
@@ -705,7 +702,7 @@

twig.core.js

-
            error_offset = 0,
+
            error_offset = 0,
@@ -720,7 +717,7 @@

twig.core.js

-
            found_token = null,
+
            found_token = null,
@@ -735,9 +732,9 @@

twig.core.js

-
            end = null;
+            
            end = null;
 
-        while (template.length > 0) {
+ while (template.length > 0) {
@@ -754,9 +751,9 @@

twig.core.js

            found_token = Twig.token.findStart(template);
 
-            Twig.log.trace("Twig.tokenize: ", "Found token: ", found_token);
+            Twig.log.trace("Twig.tokenize: ", "Found token: ", found_token);
 
-            if (found_token.position !== null) {
+ if (found_token.position !== null) {
@@ -771,10 +768,10 @@

twig.core.js

-
                if (found_token.position > 0) {
+            
                if (found_token.position > 0) {
                     tokens.push({
                         type: Twig.token.type.raw,
-                        value: template.substring(0, found_token.position)
+                        value: template.substring(0, found_token.position)
                     });
                 }
                 template = template.substr(found_token.position + found_token.def.open.length);
@@ -795,14 +792,14 @@ 

twig.core.js

                end = Twig.token.findEnd(template, found_token.def, error_offset);
 
-                Twig.log.trace("Twig.tokenize: ", "Token ends at ", end);
+                Twig.log.trace("Twig.tokenize: ", "Token ends at ", end);
 
                 tokens.push({
                     type:  found_token.def.type,
-                    value: template.substring(0, end).trim()
+                    value: template.substring(0, end).trim()
                 });
 
-                if ( found_token.def.type === "logic" && template.substr( end + found_token.def.close.length, 1 ) === "\n" ) {
+ if ( found_token.def.type === "logic" && template.substr( end + found_token.def.close.length, 1 ) === "\n" ) {
@@ -817,7 +814,7 @@

twig.core.js

-
                    end += 1;
+            
                    end += 1;
                 }
 
                 template = template.substr(end + found_token.def.close.length);
@@ -837,7 +834,7 @@

twig.core.js

                error_offset += end + found_token.def.close.length;
 
-            } else {
+ } else {
@@ -856,16 +853,16 @@

twig.core.js

type: Twig.token.type.raw, value: template }); - template = ''; + template = ''; } } - return tokens; + return tokens; }; - Twig.compile = function (tokens) { - try {
+ Twig.compile = function (tokens) { + try { @@ -880,7 +877,7 @@

twig.core.js

-
            var output = [],
+            
            var output = [],
                 stack = [],
@@ -898,9 +895,9 @@

twig.core.js

                intermediate_output = [],
 
-                token = null,
-                logic_token = null,
-                unclosed_token = null,
+ token = null, + logic_token = null, + unclosed_token = null,
@@ -915,7 +912,7 @@

twig.core.js

-
                prev_token = null,
+
                prev_token = null,
@@ -926,11 +923,11 @@

twig.core.js

-

The previous token’s template

+

The previous token's template

-
                prev_template = null,
+
                prev_template = null,
@@ -945,7 +942,7 @@

twig.core.js

-
                tok_output = null,
+
                tok_output = null,
@@ -960,23 +957,23 @@

twig.core.js

-
                type = null,
-                open = null,
-                next = null;
+            
                type = null,
+                open = null,
+                next = null;
 
-            while (tokens.length > 0) {
+            while (tokens.length > 0) {
                 token = tokens.shift();
-                Twig.log.trace("Compiling token ", token);
-                switch (token.type) {
-                    case Twig.token.type.raw:
-                        if (stack.length > 0) {
+                Twig.log.trace("Compiling token ", token);
+                switch (token.type) {
+                    case Twig.token.type.raw:
+                        if (stack.length > 0) {
                             intermediate_output.push(token);
-                        } else {
+                        } else {
                             output.push(token);
                         }
-                        break;
+                        break;
 
-                    case Twig.token.type.logic:
+ case Twig.token.type.logic:
@@ -991,14 +988,14 @@

twig.core.js

-
                        logic_token = Twig.logic.compile.apply(this, [token]);
+            
                        logic_token = Twig.logic.compile.apply(this, [token]);
 
                         type = logic_token.type;
                         open = Twig.logic.handler[type].open;
                         next = Twig.logic.handler[type].next;
 
-                        Twig.log.trace("Twig.compile: ", "Compiled logic token to ", logic_token,
-                                                         " next is: ", next, " open is : ", open);
+ Twig.log.trace("Twig.compile: ", "Compiled logic token to ", logic_token, + " next is: ", next, " open is : ", open);
@@ -1013,12 +1010,12 @@

twig.core.js

-
                        if (open !== undefined && !open) {
+            
                        if (open !== undefined && !open) {
                             prev_token = stack.pop();
                             prev_template = Twig.logic.handler[prev_token.type];
 
-                            if (Twig.indexOf(prev_template.next, type) < 0) {
-                                throw new Error(type + " not expected after a " + prev_token.type);
+                            if (Twig.indexOf(prev_template.next, type) < 0) {
+                                throw new Error(type + " not expected after a " + prev_token.type);
                             }
 
                             prev_token.output = prev_token.output || [];
@@ -1030,9 +1027,9 @@ 

twig.core.js

type: Twig.token.type.logic, token: prev_token }; - if (stack.length > 0) { + if (stack.length > 0) { intermediate_output.push(tok_output); - } else { + } else { output.push(tok_output); } }
@@ -1050,10 +1047,10 @@

twig.core.js

-
                        if (next !== undefined && next.length > 0) {
-                            Twig.log.trace("Twig.compile: ", "Pushing ", logic_token, " to logic stack.");
+            
                        if (next !== undefined && next.length > 0) {
+                            Twig.log.trace("Twig.compile: ", "Pushing ", logic_token, " to logic stack.");
 
-                            if (stack.length > 0) {
+ if (stack.length > 0) {
@@ -1091,7 +1088,7 @@

twig.core.js

                            stack.push(logic_token);
 
-                        } else if (open !== undefined && open) {
+                        } else if (open !== undefined && open) {
                             tok_output = {
                                 type: Twig.token.type.logic,
                                 token: logic_token
@@ -1106,17 +1103,17 @@ 

twig.core.js

-

Standalone token (like {% set … %}

+

Standalone token (like {% set ... %}

-
                            if (stack.length > 0) {
+            
                            if (stack.length > 0) {
                                 intermediate_output.push(tok_output);
-                            } else {
+                            } else {
                                 output.push(tok_output);
                             }
                         }
-                        break;
+ break;
@@ -1131,22 +1128,22 @@

twig.core.js

-
                    case Twig.token.type.comment:
-                        break;
+            
                    case Twig.token.type.comment:
+                        break;
 
-                    case Twig.token.type.output:
-                        Twig.expression.compile.apply(this, [token]);
-                        if (stack.length > 0) {
+                    case Twig.token.type.output:
+                        Twig.expression.compile.apply(this, [token]);
+                        if (stack.length > 0) {
                             intermediate_output.push(token);
-                        } else {
+                        } else {
                             output.push(token);
                         }
-                        break;
+                        break;
                 }
 
-                Twig.log.trace("Twig.compile: ", " Output: ", output,
-                                                 " Logic Stack: ", stack,
-                                                 " Pending Output: ", intermediate_output );
+                Twig.log.trace("Twig.compile: ", " Output: ", output,
+                                                 " Logic Stack: ", stack,
+                                                 " Pending Output: ", intermediate_output );
             }
@@ -1162,25 +1159,25 @@

twig.core.js

-
            if (stack.length > 0) {
+            
            if (stack.length > 0) {
                 unclosed_token = stack.pop();
-                throw new Error("Unable to find an end tag for " + unclosed_token.type +
-                                ", expecting one of " + unclosed_token.next);
+                throw new Error("Unable to find an end tag for " + unclosed_token.type +
+                                ", expecting one of " + unclosed_token.next);
             }
-            return output;
-        } catch (ex) {
-            Twig.log.error("Error compiling twig template " + this.id + ": ");
-            if (ex.stack) {
+            return output;
+        } catch (ex) {
+            Twig.log.error("Error compiling twig template " + this.id + ": ");
+            if (ex.stack) {
                 Twig.log.error(ex.stack);
-            } else {
+            } else {
                 Twig.log.error(ex.toString());
             }
 
-            if (this.options.rethrow) throw ex;
+            if (this.options.rethrow) throw ex;
         }
     };
 
-    /**
+    /**
      * Parse a compiled template.
      *
      * @param {Array} tokens The compiled tokens.
@@ -1188,9 +1185,9 @@ 

twig.core.js

* * @return {string} The parsed template. */
- Twig.parse = function (tokens, context) { - try { - var output = [],
+ Twig.parse = function (tokens, context) { + try { + var output = [],
@@ -1205,8 +1202,8 @@

twig.core.js

-
                chain = true,
-                that = this;
+
                chain = true,
+                that = this;
@@ -1224,30 +1221,30 @@

twig.core.js

            context = context || { };
 
 
-            Twig.forEach(tokens, function parseToken(token) {
-                Twig.log.debug("Twig.parse: ", "Parsing token: ", token);
+            Twig.forEach(tokens, function parseToken(token) {
+                Twig.log.debug("Twig.parse: ", "Parsing token: ", token);
 
-                switch (token.type) {
-                    case Twig.token.type.raw:
+                switch (token.type) {
+                    case Twig.token.type.raw:
                         output.push(token.value);
-                        break;
+                        break;
 
-                    case Twig.token.type.logic:
-                        var logic_token = token.token,
+                    case Twig.token.type.logic:
+                        var logic_token = token.token,
                             logic = Twig.logic.parse.apply(that, [logic_token, context, chain]);
 
-                        if (logic.chain !== undefined) {
+                        if (logic.chain !== undefined) {
                             chain = logic.chain;
                         }
-                        if (logic.context !== undefined) {
+                        if (logic.context !== undefined) {
                             context = logic.context;
                         }
-                        if (logic.output !== undefined) {
+                        if (logic.output !== undefined) {
                             output.push(logic.output);
                         }
-                        break;
+                        break;
 
-                    case Twig.token.type.comment:
+ case Twig.token.type.comment:
@@ -1262,10 +1259,10 @@

twig.core.js

-
                        break;
+            
                        break;
 
-                    case Twig.token.type.output:
-                        Twig.log.debug("Twig.parse: ", "Output token: ", token.stack);
+ case Twig.token.type.output: + Twig.log.debug("Twig.parse: ", "Output token: ", token.stack);
@@ -1281,35 +1278,35 @@

twig.core.js

                        output.push(Twig.expression.parse.apply(that, [token.stack, context]));
-                        break;
+                        break;
                 }
             });
-            return output.join("");
-        } catch (ex) {
-            Twig.log.error("Error parsing twig template " + this.id + ": ");
-            if (ex.stack) {
+            return output.join("");
+        } catch (ex) {
+            Twig.log.error("Error parsing twig template " + this.id + ": ");
+            if (ex.stack) {
                 Twig.log.error(ex.stack);
-            } else {
+            } else {
                 Twig.log.error(ex.toString());
             }
 
-            if (this.options.rethrow) throw ex;
+            if (this.options.rethrow) throw ex;
 
-            if (Twig.debug) {
-                return ex.toString();
+            if (Twig.debug) {
+                return ex.toString();
             }
         }
     };
 
-    /**
+    /**
      * Tokenize and compile a string template.
      *
      * @param {string} data The template.
      *
      * @return {Array} The compiled tokens.
      */
-    Twig.prepare = function(data) {
-        var tokens, raw_tokens;
+ Twig.prepare = function(data) { + var tokens, raw_tokens; @@ -1324,8 +1321,8 @@

twig.core.js

-
        Twig.log.debug("Twig.prepare: ", "Tokenizing ", data);
-        raw_tokens = Twig.tokenize.apply(this, [data]);
+
        Twig.log.debug("Twig.prepare: ", "Tokenizing ", data);
+        raw_tokens = Twig.tokenize.apply(this, [data]);
@@ -1340,12 +1337,12 @@

twig.core.js

-
        Twig.log.debug("Twig.prepare: ", "Compiling ", raw_tokens);
-        tokens = Twig.compile.apply(this, [raw_tokens]);
+            
        Twig.log.debug("Twig.prepare: ", "Compiling ", raw_tokens);
+        tokens = Twig.compile.apply(this, [raw_tokens]);
 
-        Twig.log.debug("Twig.prepare: ", "Compiled ", tokens);
+        Twig.log.debug("Twig.prepare: ", "Compiled ", tokens);
 
-        return tokens;
+        return tokens;
     };
@@ -1365,7 +1362,7 @@

twig.core.js

registry: {} }; - /** + /** * Is this id valid for a twig template? * * @param {string} id The ID to check. @@ -1373,42 +1370,42 @@

twig.core.js

* @throws {Twig.Error} If the ID is invalid or used. * @return {boolean} True if the ID is valid. */
- Twig.validateId = function(id) { - if (id === "prototype") { - throw new Twig.Error(id + " is not a valid twig identifier"); - } else if (Twig.Templates.registry.hasOwnProperty(id)) { - throw new Twig.Error("There is already a template with the ID " + id); + Twig.validateId = function(id) { + if (id === "prototype") { + throw new Twig.Error(id + " is not a valid twig identifier"); + } else if (Twig.Templates.registry.hasOwnProperty(id)) { + throw new Twig.Error("There is already a template with the ID " + id); } - return true; + return true; } - /** + /** * Save a template object to the store. * * @param {Twig.Template} template The twig.js template to store. */ - Twig.Templates.save = function(template) { - if (template.id === undefined) { - throw new Twig.Error("Unable to save template with no id"); + Twig.Templates.save = function(template) { + if (template.id === undefined) { + throw new Twig.Error("Unable to save template with no id"); } Twig.Templates.registry[template.id] = template; }; - /** + /** * Load a previously saved template from the store. * * @param {string} id The ID of the template to load. * * @return {Twig.Template} A twig.js template stored with the provided ID. */ - Twig.Templates.load = function(id) { - if (!Twig.Templates.registry.hasOwnProperty(id)) { - return null; + Twig.Templates.load = function(id) { + if (!Twig.Templates.registry.hasOwnProperty(id)) { + return null; } - return Twig.Templates.registry[id]; + return Twig.Templates.registry[id]; }; - /** + /** * Load a template from a remote location using AJAX and saves in with the given ID. * * Available parameters: @@ -1426,12 +1423,12 @@

twig.core.js

* * */
- Twig.Templates.loadRemote = function(location, params, callback, error_callback) { - var id = params.id, + Twig.Templates.loadRemote = function(location, params, callback, error_callback) { + var id = params.id, method = params.method, async = params.async, precompiled = params.precompiled, - template = null;
+ template = null; @@ -1446,7 +1443,7 @@

twig.core.js

-
        if (async === undefined) async = true;
+
        if (async === undefined) async = true;
@@ -1461,7 +1458,7 @@

twig.core.js

-
        if (id === undefined) {
+            
        if (id === undefined) {
             id = location;
         }
         params.id = id;
@@ -1479,7 +1476,7 @@

twig.core.js

-
        if (Twig.cache && Twig.Templates.registry.hasOwnProperty(id)) {
+
        if (Twig.cache && Twig.Templates.registry.hasOwnProperty(id)) {
@@ -1494,51 +1491,51 @@

twig.core.js

-
            if (callback) {
+            
            if (callback) {
                 callback(Twig.Templates.registry[id]);
             }
-            return Twig.Templates.registry[id];
+            return Twig.Templates.registry[id];
         }
 
-        if (method == 'ajax') {
-            if (typeof XMLHttpRequest == "undefined") {
-                throw new Twig.Error("Unsupported platform: Unable to do remote requests " +
-                                     "because there is no XMLHTTPRequest implementation");
+        if (method == 'ajax') {
+            if (typeof XMLHttpRequest == "undefined") {
+                throw new Twig.Error("Unsupported platform: Unable to do remote requests " +
+                                     "because there is no XMLHTTPRequest implementation");
             }
 
-            var xmlhttp = new XMLHttpRequest();
-            xmlhttp.onreadystatechange = function() {
-                var data = null;
+            var xmlhttp = new XMLHttpRequest();
+            xmlhttp.onreadystatechange = function() {
+                var data = null;
 
-                if(xmlhttp.readyState == 4) {
-                    if (xmlhttp.status == 200) {
-                        Twig.log.debug("Got template ", xmlhttp.responseText);
+                if(xmlhttp.readyState == 4) {
+                    if (xmlhttp.status == 200) {
+                        Twig.log.debug("Got template ", xmlhttp.responseText);
 
-                        if (precompiled === true) {
-                            data = JSON.parse(xmlhttp.responseText);
-                        } else {
+                        if (precompiled === true) {
+                            data = JSON.parse(xmlhttp.responseText);
+                        } else {
                             data = xmlhttp.responseText;
                         }
 
                         params.url = location;
                         params.data = data;
 
-                        template = new Twig.Template(params);
+                        template = new Twig.Template(params);
 
-                        if (callback) {
+                        if (callback) {
                             callback(template);
                         }
-                    } else {
-                        if (error_callback) {
+                    } else {
+                        if (error_callback) {
                             error_callback(xmlhttp);
                         }
                     }
                 }
             };
-            xmlhttp.open("GET", location, async);
+            xmlhttp.open("GET", location, async);
             xmlhttp.send();
 
-        } else { // if method = 'fs'
+ } else { // if method = 'fs'
@@ -1553,20 +1550,20 @@

twig.core.js

-
            (function() {
-                var fs = require('fs'),
-                    path = require('path'),
-                    data = null,
-                    loadTemplateFn = function(err, data) {
-                        if (err) {
-                            if (error_callback) {
+            
            (function() {
+                var fs = require('fs'),
+                    path = require('path'),
+                    data = null,
+                    loadTemplateFn = function(err, data) {
+                        if (err) {
+                            if (error_callback) {
                                 error_callback(err);
                             }
-                            return;
+                            return;
                         }
 
-                        if (precompiled === true) {
-                            data = JSON.parse(data);
+                        if (precompiled === true) {
+                            data = JSON.parse(data);
                         }
 
                         params.data = data;
@@ -1585,32 +1582,32 @@ 

twig.core.js

-
                        template = new Twig.Template(params);
+            
                        template = new Twig.Template(params);
 
-                        if (callback) {
+                        if (callback) {
                             callback(template);
                         }
                     };
 
-                if (async === true) {
-                    fs.stat(location, function (err, stats) {
-                        if (err || !stats.isFile())
-                            throw new Twig.Error("Unable to find template file " + location);
+                if (async === true) {
+                    fs.stat(location, function (err, stats) {
+                        if (err || !stats.isFile())
+                            throw new Twig.Error("Unable to find template file " + location);
 
-                        fs.readFile(location, 'utf8', loadTemplateFn);
+                        fs.readFile(location, 'utf8', loadTemplateFn);
                     });
-                } else {
-                    if (!fs.statSync(location).isFile())
-                        throw new Twig.Error("Unable to find template file " + location);
+                } else {
+                    if (!fs.statSync(location).isFile())
+                        throw new Twig.Error("Unable to find template file " + location);
 
-                    data = fs.readFileSync(location, 'utf8');
-                    loadTemplateFn(undefined, data);
+                    data = fs.readFileSync(location, 'utf8');
+                    loadTemplateFn(undefined, data);
                 }
             })();
         }
-        if (async === false) {
-            return template;
-        } else {
+ if (async === false) { + return template; + } else {
@@ -1625,7 +1622,7 @@

twig.core.js

-
            return true;
+            
            return true;
         }
     };
@@ -1642,12 +1639,12 @@

twig.core.js

-
    function is(type, obj) {
-        var clas = Object.prototype.toString.call(obj).slice(8, -1);
-        return obj !== undefined && obj !== null && clas === type;
+            
    function is(type, obj) {
+        var clas = Object.prototype.toString.call(obj).slice(8, -1);
+        return obj !== undefined && obj !== null && clas === type;
     }
 
-    /**
+    /**
      * Create a new twig.js template.
      *
      * Parameters: {
@@ -1658,8 +1655,8 @@ 

twig.core.js

* * @param {Object} params The template parameters. */
- Twig.Template = function ( params ) { - var data = params.data, + Twig.Template = function ( params ) { + var data = params.data, id = params.id, blocks = params.blocks, macros = params.macros || {}, @@ -1694,57 +1691,56 @@

twig.core.js

What is stored in a Twig.Template

The Twig Template hold several chucks of data.

{
-     id:     The token ID (if any)
-     tokens: The list of tokens that makes up this template.
-     blocks: The list of block this template contains.
-     base:   The base template (if any)
+     id:     The token ID (if any)
+     tokens: The list of tokens that makes up this template.
+     blocks: The list of block this template contains.
+     base:   The base template (if any)
        options:  {
            Compiler/parser options
 
-           strict_variables: true/false
-               Should missing variable/keys emit an error message. If false, they default to null.
+           strict_variables: true/false
+               Should missing variable/keys emit an error message. If false, they default to null.
        }
-}
-
+}
+
-
-        this.id     = id;
-        this.base   = base;
-        this.path   = path;
-        this.url    = url;
-        this.macros = macros;
-        this.options = options;
+            
        this.id     = id;
+        this.base   = base;
+        this.path   = path;
+        this.url    = url;
+        this.macros = macros;
+        this.options = options;
 
-        this.reset(blocks);
+        this.reset(blocks);
 
-        if (is('String', data)) {
-            this.tokens = Twig.prepare.apply(this, [data]);
-        } else {
-            this.tokens = data;
+        if (is('String', data)) {
+            this.tokens = Twig.prepare.apply(this, [data]);
+        } else {
+            this.tokens = data;
         }
 
-        if (id !== undefined) {
-            Twig.Templates.save(this);
+        if (id !== undefined) {
+            Twig.Templates.save(this);
         }
     };
 
-    Twig.Template.prototype.reset = function(blocks) {
-        Twig.log.debug("Twig.Template.reset", "Reseting template " + this.id);
-        this.blocks = {};
-        this.child = {
+    Twig.Template.prototype.reset = function(blocks) {
+        Twig.log.debug("Twig.Template.reset", "Reseting template " + this.id);
+        this.blocks = {};
+        this.child = {
             blocks: blocks || {}
         };
-        this.extend = null;
+        this.extend = null;
     };
 
-    Twig.Template.prototype.render = function (context, params) {
+    Twig.Template.prototype.render = function (context, params) {
         params = params || {};
 
-        var output,
+        var output,
             url;
 
-        this.context = context || {};
+ this.context = context || {};
@@ -1759,15 +1755,15 @@

What is stored in a Twig.Template

-
        this.reset();
-        if (params.blocks) {
-            this.blocks = params.blocks;
+            
        this.reset();
+        if (params.blocks) {
+            this.blocks = params.blocks;
         }
-        if (params.macros) {
-            this.macros = params.macros;
+        if (params.macros) {
+            this.macros = params.macros;
         }
 
-        output = Twig.parse.apply(this, [this.tokens, this.context]);
+ output = Twig.parse.apply(this, [this.tokens, this.context]);
@@ -1782,8 +1778,8 @@

What is stored in a Twig.Template

-
        if (this.extend) {
-            var ext_template;
+
        if (this.extend) {
+            var ext_template;
@@ -1798,10 +1794,10 @@

What is stored in a Twig.Template

-
            if ( this.options.allowInlineIncludes ) {
-                ext_template = Twig.Templates.load(this.extend);
-                if ( ext_template ) {
-                    ext_template.options = this.options;
+            
            if ( this.options.allowInlineIncludes ) {
+                ext_template = Twig.Templates.load(this.extend);
+                if ( ext_template ) {
+                    ext_template.options = this.options;
                 }
             }
@@ -1818,47 +1814,47 @@

What is stored in a Twig.Template

-
            if (!ext_template) {
-                url = relativePath(this, this.extend);
+            
            if (!ext_template) {
+                url = relativePath(this, this.extend);
 
                 ext_template = Twig.Templates.loadRemote(url, {
-                    method: this.url?'ajax':'fs',
-                    base: this.base,
-                    async:  false,
+                    method: this.url?'ajax':'fs',
+                    base: this.base,
+                    async:  false,
                     id:     url,
-                    options: this.options
+                    options: this.options
                 });
             }
 
-            this.parent = ext_template;
+            this.parent = ext_template;
 
-            return this.parent.render(this.context, {
-                blocks: this.blocks
+            return this.parent.render(this.context, {
+                blocks: this.blocks
             });
         }
 
-        if (params.output == 'blocks') {
-            return this.blocks;
-        } else if (params.output == 'macros') {
-            return this.macros;
-        } else {
-            return output;
+        if (params.output == 'blocks') {
+            return this.blocks;
+        } else if (params.output == 'macros') {
+            return this.macros;
+        } else {
+            return output;
         }
     };
 
-    Twig.Template.prototype.importFile = function(file) {
-        var url, sub_template;
-        if ( !this.url && !this.path && this.options.allowInlineIncludes ) {
+    Twig.Template.prototype.importFile = function(file) {
+        var url, sub_template;
+        if ( !this.url && !this.path && this.options.allowInlineIncludes ) {
             sub_template = Twig.Templates.load(file);
-            sub_template.options = this.options;
-            if ( sub_template ) {
-                return sub_template;
+            sub_template.options = this.options;
+            if ( sub_template ) {
+                return sub_template;
             }
 
-            throw new Twig.Error("Didn't find the inline template by id");
+            throw new Twig.Error("Didn't find the inline template by id");
         }
 
-        url = relativePath(this, file);
+ url = relativePath(this, file);
@@ -1874,23 +1870,23 @@

What is stored in a Twig.Template

        sub_template = Twig.Templates.loadRemote(url, {
-            method: this.url?'ajax':'fs',
-            base: this.base,
-            async: false,
-            options: this.options,
+            method: this.url?'ajax':'fs',
+            base: this.base,
+            async: false,
+            options: this.options,
             id: url
         });
 
-        return sub_template;
+        return sub_template;
     };
 
-    Twig.Template.prototype.importBlocks = function(file, override) {
-        var sub_template = this.importFile(file),
-            context = this.context,
-            that = this,
+    Twig.Template.prototype.importBlocks = function(file, override) {
+        var sub_template = this.importFile(file),
+            context = this.context,
+            that = this,
             key;
 
-        override = override || false;
+        override = override || false;
 
         sub_template.render(context);
@@ -1907,15 +1903,15 @@

What is stored in a Twig.Template

-
        Twig.forEach(Object.keys(sub_template.blocks), function(key) {
-            if (override || that.blocks[key] === undefined) {
+            
        Twig.forEach(Object.keys(sub_template.blocks), function(key) {
+            if (override || that.blocks[key] === undefined) {
                 that.blocks[key] = sub_template.blocks[key];
             }
         });
     };
 
-    Twig.Template.prototype.importMacros = function(file) {
-        var url = relativePath(this, file);
+ Twig.Template.prototype.importMacros = function(file) { + var url = relativePath(this, file);
@@ -1930,16 +1926,16 @@

What is stored in a Twig.Template

-
        var remoteTemplate = Twig.Templates.loadRemote(url, {
-            method: this.url?'ajax':'fs',
-            async: false,
+            
        var remoteTemplate = Twig.Templates.loadRemote(url, {
+            method: this.url?'ajax':'fs',
+            async: false,
             id: url
         });
 
-        return remoteTemplate;
+        return remoteTemplate;
     };
 
-    Twig.Template.prototype.compile = function(options) {
+ Twig.Template.prototype.compile = function(options) {
@@ -1954,10 +1950,10 @@

What is stored in a Twig.Template

-
        return Twig.compiler.compile(this, options);
+            
        return Twig.compiler.compile(this, options);
     };
 
-    /**
+    /**
      * Generate the relative canonical version of a url based on the given base path and file path.
      *
      * @param {string} template The Twig.Template.
@@ -1965,20 +1961,20 @@ 

What is stored in a Twig.Template

- function relativePath(template, file) { - var base, + function relativePath(template, file) { + var base, base_path, - sep_chr = "/", + sep_chr = "/", new_path = [], val; - if (template.url) { - if (typeof template.base !== 'undefined') { - base = template.base + ((template.base.charAt(template.base.length-1) === '/') ? '' : '/'); - } else { + if (template.url) { + if (typeof template.base !== 'undefined') { + base = template.base + ((template.base.charAt(template.base.length-1) === '/') ? '' : '/'); + } else { base = template.url; } - } else if (template.path) {
+ } else if (template.path) {
@@ -1993,21 +1989,22 @@

What is stored in a Twig.Template

-
            var path = require("path"),
+            
            var path = require("path"),
                 sep = path.sep || sep_chr,
-                relative = new RegExp("^\\.{1,2}" + sep.replace("\\", "\\\\"));
+                relative = new RegExp("^\\.{1,2}" + sep.replace("\\", "\\\\"));
+            file = file.replace(/\//g, sep);
 
-            if (template.base !== undefined && file.match(relative) == null) {
-                file = file.replace(template.base, '');
+            if (template.base !== undefined && file.match(relative) == null) {
+                file = file.replace(template.base, '');
                 base = template.base + sep;
-            } else {
+            } else {
                 base = template.path;
             }
 
             base = base.replace(sep+sep, sep);
             sep_chr = sep;
-        } else {
-            throw new Twig.Error("Cannot extend an inline template.");
+        } else {
+            throw new Twig.Error("Cannot extend an inline template.");
         }
 
         base_path = base.split(sep_chr);
@@ -2028,9 +2025,9 @@

What is stored in a Twig.Template

        base_path.pop();
         base_path = base_path.concat(file.split(sep_chr));
 
-        while (base_path.length > 0) {
+        while (base_path.length > 0) {
             val = base_path.shift();
-            if (val == ".") {
+ if (val == ".") {
@@ -2045,17 +2042,17 @@

What is stored in a Twig.Template

-
            } else if (val == ".." && new_path.length > 0 && new_path[new_path.length-1] != "..") {
+            
            } else if (val == ".." && new_path.length > 0 && new_path[new_path.length-1] != "..") {
                 new_path.pop();
-            } else {
+            } else {
                 new_path.push(val);
             }
         }
 
-        return new_path.join(sep_chr);
+        return new_path.join(sep_chr);
     }
 
-    return Twig;
+    return Twig;
 
 }) (Twig || { });
@@ -2075,53 +2072,40 @@

What is stored in a Twig.Template

  • Object.keys - MDN
  • - -
    - - - - -
  • -
    - -
    - -
    -

    twig.fills.js

    +

    twig.fills.js

    This file contains fills for backwards compatability.

    -
    (function() {
    -    "use strict";
    +
    (function() {
    +    "use strict";
  • -
  • +
  • - +
    -

    Handle methods that don’t yet exist in every browser

    +

    Handle methods that don't yet exist in every browser

    -
    -    if (!String.prototype.trim) {
    -        String.prototype.trim = function() {
    -            return this.replace(/^\s+|\s+$/g,'');
    +            
        if (!String.prototype.trim) {
    +        String.prototype.trim = function() {
    +            return this.replace(/^\s+|\s+$/g,'');
             }
         };
     
    -    if(!Object.keys) Object.keys = function(o){
    -        if (o !== Object(o)) {
    -            throw new TypeError('Object.keys called on non-object');
    +    if(!Object.keys) Object.keys = function(o){
    +        if (o !== Object(o)) {
    +            throw new TypeError('Object.keys called on non-object');
             }
    -        var ret = [], p;
    -        for (p in o) if (Object.prototype.hasOwnProperty.call(o, p)) ret.push(p);
    -        return ret;
    +        var ret = [], p;
    +        for (p in o) if (Object.prototype.hasOwnProperty.call(o, p)) ret.push(p);
    +        return ret;
         }
     
     })();
    @@ -2129,11 +2113,11 @@

    twig.fills.js

  • -
  • +
  • - +

    twig.lib.js

    This file contains 3rd party libraries used within twig.

    @@ -2142,17 +2126,16 @@

    twig.lib.js

    -
    -var Twig = (function(Twig) {
    +
    var Twig = (function(Twig) {
  • -
  • +
  • - +

    Namespace for libraries

    @@ -2160,142 +2143,142 @@

    twig.lib.js

        Twig.lib = { };
     
    -    /**
    +    /**
         sprintf() for JavaScript 0.7-beta1
         http://www.diveintojavascript.com/projects/javascript-sprintf
         **/
    -    var sprintf = (function() {
    -            function get_type(variable) {
    -                    return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
    +    var sprintf = (function() {
    +            function get_type(variable) {
    +                    return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
                 }
    -            function str_repeat(input, multiplier) {
    -                    for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
    -                    return output.join('');
    +            function str_repeat(input, multiplier) {
    +                    for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
    +                    return output.join('');
                 }
     
    -            var str_format = function() {
    -                    if (!str_format.cache.hasOwnProperty(arguments[0])) {
    -                            str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
    +            var str_format = function() {
    +                    if (!str_format.cache.hasOwnProperty(arguments[0])) {
    +                            str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
                         }
    -                    return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
    +                    return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
                 };
     
    -            str_format.format = function(parse_tree, argv) {
    -                    var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
    -                    for (i = 0; i < tree_length; i++) {
    +            str_format.format = function(parse_tree, argv) {
    +                    var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
    +                    for (i = 0; i < tree_length; i++) {
                                 node_type = get_type(parse_tree[i]);
    -                            if (node_type === 'string') {
    +                            if (node_type === 'string') {
                                         output.push(parse_tree[i]);
                                 }
    -                            else if (node_type === 'array') {
    -                                    match = parse_tree[i]; // convenience purposes only
    -                                    if (match[2]) { // keyword argument
    +                            else if (node_type === 'array') {
    +                                    match = parse_tree[i]; // convenience purposes only
    +                                    if (match[2]) { // keyword argument
                                                 arg = argv[cursor];
    -                                            for (k = 0; k < match[2].length; k++) {
    -                                                    if (!arg.hasOwnProperty(match[2][k])) {
    -                                                            throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
    +                                            for (k = 0; k < match[2].length; k++) {
    +                                                    if (!arg.hasOwnProperty(match[2][k])) {
    +                                                            throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
                                                         }
    -                                                    arg = arg[match[2][k]];
    +                                                    arg = arg[match[2][k]];
                                                 }
                                         }
    -                                    else if (match[1]) { // positional argument (explicit)
    -                                            arg = argv[match[1]];
    +                                    else if (match[1]) { // positional argument (explicit)
    +                                            arg = argv[match[1]];
                                         }
    -                                    else { // positional argument (implicit)
    +                                    else { // positional argument (implicit)
                                                 arg = argv[cursor++];
                                         }
     
    -                                    if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
    -                                            throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
    +                                    if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
    +                                            throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
                                         }
    -                                    switch (match[8]) {
    -                                            case 'b': arg = arg.toString(2); break;
    -                                            case 'c': arg = String.fromCharCode(arg); break;
    -                                            case 'd': arg = parseInt(arg, 10); break;
    -                                            case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
    -                                            case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
    -                                            case 'o': arg = arg.toString(8); break;
    -                                            case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
    -                                            case 'u': arg = Math.abs(arg); break;
    -                                            case 'x': arg = arg.toString(16); break;
    -                                            case 'X': arg = arg.toString(16).toUpperCase(); break;
    +                                    switch (match[8]) {
    +                                            case 'b': arg = arg.toString(2); break;
    +                                            case 'c': arg = String.fromCharCode(arg); break;
    +                                            case 'd': arg = parseInt(arg, 10); break;
    +                                            case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
    +                                            case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
    +                                            case 'o': arg = arg.toString(8); break;
    +                                            case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
    +                                            case 'u': arg = Math.abs(arg); break;
    +                                            case 'x': arg = arg.toString(16); break;
    +                                            case 'X': arg = arg.toString(16).toUpperCase(); break;
                                         }
    -                                    arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
    -                                    pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
    -                                    pad_length = match[6] - String(arg).length;
    -                                    pad = match[6] ? str_repeat(pad_character, pad_length) : '';
    -                                    output.push(match[5] ? arg + pad : pad + arg);
    +                                    arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
    +                                    pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
    +                                    pad_length = match[6] - String(arg).length;
    +                                    pad = match[6] ? str_repeat(pad_character, pad_length) : '';
    +                                    output.push(match[5] ? arg + pad : pad + arg);
                                 }
                         }
    -                    return output.join('');
    +                    return output.join('');
                 };
     
                 str_format.cache = {};
     
    -            str_format.parse = function(fmt) {
    -                    var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
    -                    while (_fmt) {
    -                            if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
    -                                    parse_tree.push(match[0]);
    +            str_format.parse = function(fmt) {
    +                    var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
    +                    while (_fmt) {
    +                            if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
    +                                    parse_tree.push(match[0]);
                                 }
    -                            else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
    -                                    parse_tree.push('%');
    +                            else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
    +                                    parse_tree.push('%');
                                 }
    -                            else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
    -                                    if (match[2]) {
    -                                            arg_names |= 1;
    -                                            var field_list = [], replacement_field = match[2], field_match = [];
    -                                            if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
    -                                                    field_list.push(field_match[1]);
    -                                                    while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
    -                                                            if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
    -                                                                    field_list.push(field_match[1]);
    +                            else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
    +                                    if (match[2]) {
    +                                            arg_names |= 1;
    +                                            var field_list = [], replacement_field = match[2], field_match = [];
    +                                            if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
    +                                                    field_list.push(field_match[1]);
    +                                                    while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
    +                                                            if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
    +                                                                    field_list.push(field_match[1]);
                                                                 }
    -                                                            else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
    -                                                                    field_list.push(field_match[1]);
    +                                                            else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
    +                                                                    field_list.push(field_match[1]);
                                                                 }
    -                                                            else {
    -                                                                    throw('[sprintf] huh?');
    +                                                            else {
    +                                                                    throw('[sprintf] huh?');
                                                                 }
                                                         }
                                                 }
    -                                            else {
    -                                                    throw('[sprintf] huh?');
    +                                            else {
    +                                                    throw('[sprintf] huh?');
                                                 }
    -                                            match[2] = field_list;
    +                                            match[2] = field_list;
                                         }
    -                                    else {
    -                                            arg_names |= 2;
    +                                    else {
    +                                            arg_names |= 2;
                                         }
    -                                    if (arg_names === 3) {
    -                                            throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
    +                                    if (arg_names === 3) {
    +                                            throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
                                         }
                                         parse_tree.push(match);
                                 }
    -                            else {
    -                                    throw('[sprintf] huh?');
    +                            else {
    +                                    throw('[sprintf] huh?');
                                 }
    -                            _fmt = _fmt.substring(match[0].length);
    +                            _fmt = _fmt.substring(match[0].length);
                         }
    -                    return parse_tree;
    +                    return parse_tree;
                 };
     
    -            return str_format;
    +            return str_format;
         })();
     
    -    var vsprintf = function(fmt, argv) {
    +    var vsprintf = function(fmt, argv) {
             argv.unshift(fmt);
    -        return sprintf.apply(null, argv);
    +        return sprintf.apply(null, argv);
         };
  • -
  • +
  • - +

    Expose to Twig

    @@ -2305,7 +2288,7 @@

    twig.lib.js

    Twig.lib.vsprintf = vsprintf; - /** + /** * jPaq - A fully customizable JavaScript/JScript library * http://jpaq.org/ * @@ -2316,67 +2299,67 @@

    twig.lib.js

    * Version: 1.0.6.0000W * Revised: April 6, 2011 */
    - ; (function() { - var shortDays = "Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","); - var fullDays = "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","); - var shortMonths = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","); - var fullMonths = "January,February,March,April,May,June,July,August,September,October,November,December".split(","); - function getOrdinalFor(intNum) { - return (((intNum = Math.abs(intNum) % 100) % 10 == 1 && intNum != 11) ? "st" - : (intNum % 10 == 2 && intNum != 12) ? "nd" : (intNum % 10 == 3 - && intNum != 13) ? "rd" : "th"); + ; (function() { + var shortDays = "Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","); + var fullDays = "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","); + var shortMonths = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","); + var fullMonths = "January,February,March,April,May,June,July,August,September,October,November,December".split(","); + function getOrdinalFor(intNum) { + return (((intNum = Math.abs(intNum) % 100) % 10 == 1 && intNum != 11) ? "st" + : (intNum % 10 == 2 && intNum != 12) ? "nd" : (intNum % 10 == 3 + && intNum != 13) ? "rd" : "th"); } - function getISO8601Year(aDate) { - var d = new Date(aDate.getFullYear() + 1, 0, 4); - if((d - aDate) / 86400000 < 7 && (aDate.getDay() + 6) % 7 < (d.getDay() + 6) % 7) - return d.getFullYear(); - if(aDate.getMonth() > 0 || aDate.getDate() >= 4) - return aDate.getFullYear(); - return aDate.getFullYear() - (((aDate.getDay() + 6) % 7 - aDate.getDate() > 2) ? 1 : 0); + function getISO8601Year(aDate) { + var d = new Date(aDate.getFullYear() + 1, 0, 4); + if((d - aDate) / 86400000 < 7 && (aDate.getDay() + 6) % 7 < (d.getDay() + 6) % 7) + return d.getFullYear(); + if(aDate.getMonth() > 0 || aDate.getDate() >= 4) + return aDate.getFullYear(); + return aDate.getFullYear() - (((aDate.getDay() + 6) % 7 - aDate.getDate() > 2) ? 1 : 0); } - function getISO8601Week(aDate) {
  • + function getISO8601Week(aDate) {
    -
  • +
  • - +

    Get a day during the first week of the year.

    -
                    var d = new Date(getISO8601Year(aDate), 0, 4);
    +
                    var d = new Date(getISO8601Year(aDate), 0, 4);
  • -
  • +
  • - +

    Get the first monday of the year.

    -
                    d.setDate(d.getDate() - (d.getDay() + 6) % 7);
    -                return parseInt((aDate - d) / 604800000) + 1;
    +            
                    d.setDate(d.getDate() - (d.getDay() + 6) % 7);
    +                return parseInt((aDate - d) / 604800000) + 1;
             }
    -        Twig.lib.formatDate = function(date, format) {
    + Twig.lib.formatDate = function(date, format) {
  • -
  • +
  • - +

    /

    / Gets a string for this date, formatted according to the given format @@ -2386,13 +2369,13 @@

    twig.lib.js

    / The format of the output date string. The format string works in a / nearly identical way to the PHP date function which is highlighted here: / http://php.net/manual/en/function.date.php. -/ The only difference is the fact that “u” signifies milliseconds +/ The only difference is the fact that "u" signifies milliseconds / instead of microseconds. The following characters are recognized in / the format parameter string: / d - Day of the month, 2 digits with leading zeros / D - A textual representation of a day, three letters / j - Day of the month without leading zeros -/ l (lowercase ‘L’) - A full textual representation of the day of the week +/ l (lowercase 'L') - A full textual representation of the day of the week / N - ISO-8601 numeric representation of the day of the week (starting from 1) / S - English ordinal suffix for the day of the month, 2 characters st, / nd, rd or th. Works well with j. @@ -2404,7 +2387,7 @@

    twig.lib.js

    / M - A short textual representation of a month, three letters / n - Numeric representation of a month, without leading zeros / t - Number of days in the given month -/ L - Whether it’s a leap year +/ L - Whether it's a leap year / o - ISO-8601 year number. This has the same value as Y, except that if / the ISO week number (W) belongs to the previous or next year, that / year is used instead. @@ -2430,12 +2413,27 @@

    twig.lib.js

    -
                if(typeof format !== "string" || /^\s*$/.test(format))
    -                    return date + "";
    -            var jan1st = new Date(date.getFullYear(), 0, 1);
    -            var me = date;
    -            return format.replace(/[dDjlNSwzWFmMntLoYyaABgGhHisuU]/g, function(option) {
    -                switch(option) {
    +
                if(typeof format !== "string" || /^\s*$/.test(format))
    +                    return date + "";
    +            var jan1st = new Date(date.getFullYear(), 0, 1);
    +            var me = date;
    +            return format.replace(/[dDjlNSwzWFmMntLoYyaABgGhHisuU]/g, function(option) {
    +                switch(option) {
    + +
  • + + +
  • +
    + +
    + +
    +

    Day of the month, 2 digits with leading zeros

    + +
    + +
                        case "d": return ("0" + me.getDate()).replace(/^.+(..)$/, "$1");
  • @@ -2446,11 +2444,11 @@

    twig.lib.js

    -

    Day of the month, 2 digits with leading zeros

    +

    A textual representation of a day, three letters

    -
                        case "d": return ("0" + me.getDate()).replace(/^.+(..)$/, "$1");
    +
                        case "D": return shortDays[me.getDay()];
    @@ -2461,11 +2459,11 @@

    twig.lib.js

    -

    A textual representation of a day, three letters

    +

    Day of the month without leading zeros

    -
                        case "D": return shortDays[me.getDay()];
    +
                        case "j": return me.getDate();
    @@ -2476,11 +2474,11 @@

    twig.lib.js

    -

    Day of the month without leading zeros

    +

    A full textual representation of the day of the week

    -
                        case "j": return me.getDate();
    +
                        case "l": return fullDays[me.getDay()];
    @@ -2491,11 +2489,11 @@

    twig.lib.js

    -

    A full textual representation of the day of the week

    +

    ISO-8601 numeric representation of the day of the week

    -
                        case "l": return fullDays[me.getDay()];
    +
                        case "N": return (me.getDay() + 6) % 7 + 1;
    @@ -2506,11 +2504,11 @@

    twig.lib.js

    -

    ISO-8601 numeric representation of the day of the week

    +

    English ordinal suffix for the day of the month, 2 characters

    -
                        case "N": return (me.getDay() + 6) % 7 + 1;
    +
                        case "S": return getOrdinalFor(me.getDate());
    @@ -2521,11 +2519,11 @@

    twig.lib.js

    -

    English ordinal suffix for the day of the month, 2 characters

    +

    Numeric representation of the day of the week

    -
                        case "S": return getOrdinalFor(me.getDate());
    +
                        case "w": return me.getDay();
    @@ -2536,11 +2534,11 @@

    twig.lib.js

    -

    Numeric representation of the day of the week

    +

    The day of the year (starting from 0)

    -
                        case "w": return me.getDay();
    +
                        case "z": return Math.ceil((jan1st - me) / 86400000);
    @@ -2551,11 +2549,11 @@

    twig.lib.js

    -

    The day of the year (starting from 0)

    +

    ISO-8601 week number of year, weeks starting on Monday

    -
                        case "z": return Math.ceil((jan1st - me) / 86400000);
    +
                        case "W": return ("0" + getISO8601Week(me)).replace(/^.(..)$/, "$1");
    @@ -2566,11 +2564,11 @@

    twig.lib.js

    -

    ISO-8601 week number of year, weeks starting on Monday

    +

    A full textual representation of a month, such as January or March

    -
                        case "W": return ("0" + getISO8601Week(me)).replace(/^.(..)$/, "$1");
    +
                        case "F": return fullMonths[me.getMonth()];
    @@ -2581,11 +2579,11 @@

    twig.lib.js

    -

    A full textual representation of a month, such as January or March

    +

    Numeric representation of a month, with leading zeros

    -
                        case "F": return fullMonths[me.getMonth()];
    +
                        case "m": return ("0" + (me.getMonth() + 1)).replace(/^.+(..)$/, "$1");
    @@ -2596,11 +2594,11 @@

    twig.lib.js

    -

    Numeric representation of a month, with leading zeros

    +

    A short textual representation of a month, three letters

    -
                        case "m": return ("0" + (me.getMonth() + 1)).replace(/^.+(..)$/, "$1");
    +
                        case "M": return shortMonths[me.getMonth()];
    @@ -2611,11 +2609,11 @@

    twig.lib.js

    -

    A short textual representation of a month, three letters

    +

    Numeric representation of a month, without leading zeros

    -
                        case "M": return shortMonths[me.getMonth()];
    +
                        case "n": return me.getMonth() + 1;
    @@ -2626,11 +2624,11 @@

    twig.lib.js

    -

    Numeric representation of a month, without leading zeros

    +

    Number of days in the given month

    -
                        case "n": return me.getMonth() + 1;
    +
                        case "t": return new Date(me.getFullYear(), me.getMonth() + 1, -1).getDate();
    @@ -2641,11 +2639,11 @@

    twig.lib.js

    -

    Number of days in the given month

    +

    Whether it's a leap year

    -
                        case "t": return new Date(me.getFullYear(), me.getMonth() + 1, -1).getDate();
    +
                        case "L": return new Date(me.getFullYear(), 1, 29).getDate() == 29 ? 1 : 0;
    @@ -2656,11 +2654,13 @@

    twig.lib.js

    -

    Whether it’s a leap year

    +

    ISO-8601 year number. This has the same value as Y, except that if the +ISO week number (W) belongs to the previous or next year, that year is +used instead.

    -
                        case "L": return new Date(me.getFullYear(), 1, 29).getDate() == 29 ? 1 : 0;
    +
                        case "o": return getISO8601Year(me);
    @@ -2671,13 +2671,11 @@

    twig.lib.js

    -

    ISO-8601 year number. This has the same value as Y, except that if the -ISO week number (W) belongs to the previous or next year, that year is -used instead.

    +

    A full numeric representation of a year, 4 digits

    -
                        case "o": return getISO8601Year(me);
    +
                        case "Y": return me.getFullYear();
    @@ -2688,11 +2686,11 @@

    twig.lib.js

    -

    A full numeric representation of a year, 4 digits

    +

    A two digit representation of a year

    -
                        case "Y": return me.getFullYear();
    +
                        case "y": return (me.getFullYear() + "").replace(/^.+(..)$/, "$1");
    @@ -2703,11 +2701,11 @@

    twig.lib.js

    -

    A two digit representation of a year

    +

    Lowercase Ante meridiem and Post meridiem

    -
                        case "y": return (me.getFullYear() + "").replace(/^.+(..)$/, "$1");
    +
                        case "a": return me.getHours() < 12 ? "am" : "pm";
    @@ -2718,11 +2716,11 @@

    twig.lib.js

    -

    Lowercase Ante meridiem and Post meridiem

    +

    Uppercase Ante meridiem and Post meridiem

    -
                        case "a": return me.getHours() < 12 ? "am" : "pm";
    +
                        case "A": return me.getHours() < 12 ? "AM" : "PM";
    @@ -2733,11 +2731,11 @@

    twig.lib.js

    -

    Uppercase Ante meridiem and Post meridiem

    +

    Swatch Internet time

    -
                        case "A": return me.getHours() < 12 ? "AM" : "PM";
    +
                        case "B": return Math.floor((((me.getUTCHours() + 1) % 24) + me.getUTCMinutes() / 60 + me.getUTCSeconds() / 3600) * 1000 / 24);
    @@ -2748,11 +2746,11 @@

    twig.lib.js

    -

    Swatch Internet time

    +

    12-hour format of an hour without leading zeros

    -
                        case "B": return Math.floor((((me.getUTCHours() + 1) % 24) + me.getUTCMinutes() / 60 + me.getUTCSeconds() / 3600) * 1000 / 24);
    +
                        case "g": return me.getHours() % 12 != 0 ? me.getHours() % 12 : 12;
    @@ -2763,11 +2761,11 @@

    twig.lib.js

    -

    12-hour format of an hour without leading zeros

    +

    24-hour format of an hour without leading zeros

    -
                        case "g": return me.getHours() % 12 != 0 ? me.getHours() % 12 : 12;
    +
                        case "G": return me.getHours();
    @@ -2778,11 +2776,11 @@

    twig.lib.js

    -

    24-hour format of an hour without leading zeros

    +

    12-hour format of an hour with leading zeros

    -
                        case "G": return me.getHours();
    +
                        case "h": return ("0" + (me.getHours() % 12 != 0 ? me.getHours() % 12 : 12)).replace(/^.+(..)$/, "$1");
    @@ -2793,11 +2791,11 @@

    twig.lib.js

    -

    12-hour format of an hour with leading zeros

    +

    24-hour format of an hour with leading zeros

    -
                        case "h": return ("0" + (me.getHours() % 12 != 0 ? me.getHours() % 12 : 12)).replace(/^.+(..)$/, "$1");
    +
                        case "H": return ("0" + me.getHours()).replace(/^.+(..)$/, "$1");
    @@ -2808,11 +2806,11 @@

    twig.lib.js

    -

    24-hour format of an hour with leading zeros

    +

    Minutes with leading zeros

    -
                        case "H": return ("0" + me.getHours()).replace(/^.+(..)$/, "$1");
    +
                        case "i": return ("0" + me.getMinutes()).replace(/^.+(..)$/, "$1");
    @@ -2823,11 +2821,11 @@

    twig.lib.js

    -

    Minutes with leading zeros

    +

    Seconds, with leading zeros

    -
                        case "i": return ("0" + me.getMinutes()).replace(/^.+(..)$/, "$1");
    +
                        case "s": return ("0" + me.getSeconds()).replace(/^.+(..)$/, "$1");
    @@ -2838,11 +2836,11 @@

    twig.lib.js

    -

    Seconds, with leading zeros

    +

    Milliseconds

    -
                        case "s": return ("0" + me.getSeconds()).replace(/^.+(..)$/, "$1");
    +
                        case "u": return me.getMilliseconds();
    @@ -2853,41 +2851,26 @@

    twig.lib.js

    -

    Milliseconds

    - - - -
                        case "u": return me.getMilliseconds();
    - - - - -
  • -
    - -
    - -

    Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)

    -
                        case "U": return me.getTime() / 1000;
    +            
                        case "U": return me.getTime() / 1000;
                     }
                 });
             };
         })();
     
    -    Twig.lib.strip_tags = function(input, allowed) {
    + Twig.lib.strip_tags = function(input, allowed) {
  • -
  • +
  • - +

    Strips HTML and PHP tags from a string

    version: 1109.2015 @@ -2910,42 +2893,42 @@

    twig.lib.js

  • bugfixed by: Tomasz Wesolowski
  • input by: Evertjan Garretsen
  • revised by: Rafał Kukawski (http://blog.kukawski.pl/)
  • -
  • example 1: strip_tags(‘

    Kevin

    van Zonneveld‘, ‘‘);
  • -
  • returns 1: ‘Kevin van Zonneveld
  • -
  • example 2: strip_tags(‘

    Kevin van Zonneveld

    ‘, ‘

    ‘);

  • -
  • returns 2: ‘

    Kevin van Zonneveld

  • -
  • example 3: strip_tags(“Kevin van Zonneveld“, ““);
  • -
  • returns 3: ‘Kevin van Zonneveld
  • -
  • example 4: strip_tags(‘1 < 5 5 > 1’);
  • -
  • returns 4: ‘1 < 5 5 > 1’
  • -
  • example 5: strip_tags(‘1
    1’);
  • -
  • returns 5: ‘1 1’
  • -
  • example 6: strip_tags(‘1
    1’, ‘
    ‘);
  • -
  • returns 6: ‘1 1’
  • -
  • example 7: strip_tags(‘1
    1’, ‘

    ‘);
  • -
  • returns 7: ‘1
    1’
  • +
  • example 1: strip_tags('

    Kevin

    van Zonneveld', '');
  • +
  • returns 1: 'Kevin van Zonneveld'
  • +
  • example 2: strip_tags('

    Kevin van Zonneveld

    ', '

    ');

  • +
  • returns 2: '

    Kevin van Zonneveld

    '
  • +
  • example 3: strip_tags("Kevin van Zonneveld", "");
  • +
  • returns 3: 'Kevin van Zonneveld'
  • +
  • example 4: strip_tags('1 < 5 5 > 1');
  • +
  • returns 4: '1 < 5 5 > 1'
  • +
  • example 5: strip_tags('1
    1');
  • +
  • returns 5: '1 1'
  • +
  • example 6: strip_tags('1
    1', '
    ');
  • +
  • returns 6: '1 1'
  • +
  • example 7: strip_tags('1
    1', '

    ');
  • +
  • returns 7: '1
    1'
  • -
            allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
    -        var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
    -            commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
    -        return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
    -            return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
    +            
            allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
    +        var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
    +            commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
    +        return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
    +            return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
             });
         }
     
    -    Twig.lib.parseISO8601Date = function (s){
    + Twig.lib.parseISO8601Date = function (s){
    -
  • +
  • - +

    Taken from http://n8v.enteuxis.org/2010/12/parsing-iso-8601-dates-in-javascript/ parenthese matches: @@ -2954,61 +2937,60 @@

    twig.lib.js

    -
            var re = /(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+-])(\d\d):(\d\d))/;
    +            
            var re = /(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+-])(\d\d):(\d\d))/;
     
    -        var d = [];
    +        var d = [];
             d = s.match(re);
  • -
  • +
  • - +
    -

    “2010-12-07T11:00:00.000-09:00” parses to: - [“2010-12-07T11:00:00.000-09:00”, “2010”, “12”, “07”, “11”, - “00”, “00”, “.000”, “-09:00”, “-“, “09”, “00”] -“2010-12-07T11:00:00.000Z” parses to: - [“2010-12-07T11:00:00.000Z”, “2010”, “12”, “07”, “11”, - “00”, “00”, “.000”, “Z”, undefined, undefined, undefined]

    +

    "2010-12-07T11:00:00.000-09:00" parses to: + ["2010-12-07T11:00:00.000-09:00", "2010", "12", "07", "11", + "00", "00", ".000", "-09:00", "-", "09", "00"] +"2010-12-07T11:00:00.000Z" parses to: + ["2010-12-07T11:00:00.000Z", "2010", "12", "07", "11", + "00", "00", ".000", "Z", undefined, undefined, undefined]

    -
    -        if (! d) {
    -            throw "Couldn't parse ISO 8601 date string '" + s + "'";
    +            
            if (! d) {
    +            throw "Couldn't parse ISO 8601 date string '" + s + "'";
             }
  • -
  • +
  • - +

    parse strings, leading zeros into proper ints

    -
            var a = [1,2,3,4,5,6,10,11];
    -        for (var i in a) {
    -            d[a[i]] = parseInt(d[a[i]], 10);
    +            
            var a = [1,2,3,4,5,6,10,11];
    +        for (var i in a) {
    +            d[a[i]] = parseInt(d[a[i]], 10);
             }
    -        d[7] = parseFloat(d[7]);
    + d[7] = parseFloat(d[7]);
  • -
  • +
  • - +

    Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]]) note that month is 0-11, not 1-12 @@ -3016,64 +2998,64 @@

    twig.lib.js

    -
            var ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]);
    +
            var ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]);
  • -
  • +
  • - +

    if there are milliseconds, add them

    -
            if (d[7] > 0) {  
    -            ms += Math.round(d[7] * 1000);
    +            
            if (d[7] > 0) {  
    +            ms += Math.round(d[7] * 1000);
             }
  • -
  • +
  • - +
    -

    if there’s a timezone, calculate it

    +

    if there's a timezone, calculate it

    -
            if (d[8] != "Z" && d[10]) {
    -            var offset = d[10] * 60 * 60 * 1000;
    -            if (d[11]) {
    -                offset += d[11] * 60 * 1000;
    +            
            if (d[8] != "Z" && d[10]) {
    +            var offset = d[10] * 60 * 60 * 1000;
    +            if (d[11]) {
    +                offset += d[11] * 60 * 1000;
                 }
    -            if (d[9] == "-") {
    +            if (d[9] == "-") {
                     ms -= offset;
                 }
    -            else {
    +            else {
                     ms += offset;
                 }
             }
     
    -        return new Date(ms);
    +        return new Date(ms);
         };
     
    -    Twig.lib.strtotime = function (str, now) {
    + Twig.lib.strtotime = function (str, now) {
  • -
  • +
  • - +

    http://kevin.vanzonneveld.net

      @@ -3085,252 +3067,279 @@

      twig.lib.js

    • bugfixed by: Wagner B. Soares
    • bugfixed by: Artur Tchernychev % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones)
    • -
    • example 1: strtotime(‘+1 day’, 1129633200);
    • +
    • example 1: strtotime('+1 day', 1129633200);
    • returns 1: 1129719600
    • -
    • example 2: strtotime(‘+1 week 2 days 4 hours 2 seconds’, 1129633200);
    • +
    • example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200);
    • returns 2: 1130425202
    • -
    • example 3: strtotime(‘last month’, 1129633200);
    • +
    • example 3: strtotime('last month', 1129633200);
    • returns 3: 1127041200
    • -
    • example 4: strtotime(‘2009-05-04 08:30:00’);
    • +
    • example 4: strtotime('2009-05-04 08:30:00');
    • returns 4: 1241418600
    -
            var i, l, match, s, parse = '';
    +            
            var i, l, match, s, parse = '';
     
    -        str = str.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces
    -        str = str.replace(/[\t\r\n]/g, ''); // unecessary chars
    -        if (str === 'now') {
    -            return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0;
    -        } else if (!isNaN(parse = Date.parse(str))) {
    -            return parse / 1000 | 0;
    -        } else if (now) {
    -            now = new Date(now * 1000); // Accept PHP-style seconds
    -        } else {
    -            now = new Date();
    +        str = str.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces
    +        str = str.replace(/[\t\r\n]/g, ''); // unecessary chars
    +        if (str === 'now') {
    +            return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0;
    +        } else if (!isNaN(parse = Date.parse(str))) {
    +            return parse / 1000 | 0;
    +        } else if (now) {
    +            now = new Date(now * 1000); // Accept PHP-style seconds
    +        } else {
    +            now = new Date();
             }
     
    -        var upperCaseStr = str;
    +        var upperCaseStr = str;
     
             str = str.toLowerCase();
     
    -        var __is = {
    +        var __is = {
                 day: {
    -                'sun': 0,
    -                'mon': 1,
    -                'tue': 2,
    -                'wed': 3,
    -                'thu': 4,
    -                'fri': 5,
    -                'sat': 6
    +                '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'
    +                'jan',
    +                'feb',
    +                'mar',
    +                'apr',
    +                'may',
    +                'jun',
    +                'jul',
    +                'aug',
    +                'sep',
    +                'oct',
    +                'nov',
    +                'dec'
                 ]
             };
     
    -        var process = function (m) {
    -            var ago = (m[2] && m[2] === 'ago');
    -            var num = (num = m[0] === 'last' ? -1 : 1) * (ago ? -1 : 1);
    +        var process = function (m) {
    +            var ago = (m[2] && m[2] === 'ago');
    +            var num = (num = m[0] === 'last' ? -1 : 1) * (ago ? -1 : 1);
     
    -            switch (m[0]) {
    -            case 'last':
    -            case 'next':
    -                switch (m[1].substring(0, 3)) {
    -                case 'yea':
    +            switch (m[0]) {
    +            case 'last':
    +            case 'next':
    +                switch (m[1].substring(0, 3)) {
    +                case 'yea':
                         now.setFullYear(now.getFullYear() + num);
    -                    break;
    -                case 'wee':
    -                    now.setDate(now.getDate() + (num * 7));
    -                    break;
    -                case 'day':
    +                    break;
    +                case 'wee':
    +                    now.setDate(now.getDate() + (num * 7));
    +                    break;
    +                case 'day':
                         now.setDate(now.getDate() + num);
    -                    break;
    -                case 'hou':
    +                    break;
    +                case 'hou':
                         now.setHours(now.getHours() + num);
    -                    break;
    -                case 'min':
    +                    break;
    +                case 'min':
                         now.setMinutes(now.getMinutes() + num);
    -                    break;
    -                case 'sec':
    +                    break;
    +                case 'sec':
                         now.setSeconds(now.getSeconds() + num);
    -                    break;
    -                case 'mon':
    -                    if (m[1] === "month") {
    +                    break;
    +                case 'mon':
    +                    if (m[1] === "month") {
                             now.setMonth(now.getMonth() + num);
    -                        break;
    +                        break;
                         }
  • -
  • +
  • - +

    fall through

    -
                    default:
    -                    var day = __is.day[m[1].substring(0, 3)];
    -                    if (typeof day !== 'undefined') {
    -                        var diff = day - now.getDay();
    -                        if (diff === 0) {
    -                            diff = 7 * num;
    -                        } else if (diff > 0) {
    -                            if (m[0] === 'last') {
    -                                diff -= 7;
    +            
                    default:
    +                    var day = __is.day[m[1].substring(0, 3)];
    +                    if (typeof day !== 'undefined') {
    +                        var diff = day - now.getDay();
    +                        if (diff === 0) {
    +                            diff = 7 * num;
    +                        } else if (diff > 0) {
    +                            if (m[0] === 'last') {
    +                                diff -= 7;
                                 }
    -                        } else {
    -                            if (m[0] === 'next') {
    -                                diff += 7;
    +                        } else {
    +                            if (m[0] === 'next') {
    +                                diff += 7;
                                 }
                             }
                             now.setDate(now.getDate() + diff);
    -                        now.setHours(0, 0, 0, 0); // when jumping to a specific last/previous day of week, PHP sets the time to 00:00:00
    +                        now.setHours(0, 0, 0, 0); // when jumping to a specific last/previous day of week, PHP sets the time to 00:00:00
                         }
                     }
    -                break;
    +                break;
     
    -            default:
    -                if (/\d+/.test(m[0])) {
    -                    num *= parseInt(m[0], 10);
    +            default:
    +                if (/\d+/.test(m[0])) {
    +                    num *= parseInt(m[0], 10);
     
    -                    switch (m[1].substring(0, 3)) {
    -                    case 'yea':
    +                    switch (m[1].substring(0, 3)) {
    +                    case 'yea':
                             now.setFullYear(now.getFullYear() + num);
    -                        break;
    -                    case 'mon':
    +                        break;
    +                    case 'mon':
                             now.setMonth(now.getMonth() + num);
    -                        break;
    -                    case 'wee':
    -                        now.setDate(now.getDate() + (num * 7));
    -                        break;
    -                    case 'day':
    +                        break;
    +                    case 'wee':
    +                        now.setDate(now.getDate() + (num * 7));
    +                        break;
    +                    case 'day':
                             now.setDate(now.getDate() + num);
    -                        break;
    -                    case 'hou':
    +                        break;
    +                    case 'hou':
                             now.setHours(now.getHours() + num);
    -                        break;
    -                    case 'min':
    +                        break;
    +                    case 'min':
                             now.setMinutes(now.getMinutes() + num);
    -                        break;
    -                    case 'sec':
    +                        break;
    +                    case 'sec':
                             now.setSeconds(now.getSeconds() + num);
    -                        break;
    +                        break;
                         }
    -                } else {
    -                    return false;
    +                } else {
    +                    return false;
                     }
    -                break;
    +                break;
                 }
    -            return true;
    +            return true;
             };
     
    -        match = str.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);
    -        if (match !== null) {
    -            if (!match[2]) {
    -                match[2] = '00:00:00';
    -            } else if (!match[3]) {
    -                match[2] += ':00';
    +        match = str.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);
    +        if (match !== null) {
    +            if (!match[2]) {
    +                match[2] = '00:00:00';
    +            } else if (!match[3]) {
    +                match[2] += ':00';
                 }
     
    -            s = match[1].split(/-/g);
    +            s = match[1].split(/-/g);
     
    -            s[1] = __is.mon[s[1] - 1] || s[1];
    -            s[0] = +s[0];
    +            s[1] = __is.mon[s[1] - 1] || s[1];
    +            s[0] = +s[0];
     
    -            s[0] = (s[0] >= 0 && s[0] <= 69) ? '20' + (s[0] < 10 ? '0' + s[0] : s[0] + '') : (s[0] >= 70 && s[0] <= 99) ? '19' + s[0] : s[0] + '';
    -            return parseInt(this.strtotime(s[2] + ' ' + s[1] + ' ' + s[0] + ' ' + match[2]) + (match[4] ? match[4] / 1000 : ''), 10);
    +            s[0] = (s[0] >= 0 && s[0] <= 69) ? '20' + (s[0] < 10 ? '0' + s[0] : s[0] + '') : (s[0] >= 70 && s[0] <= 99) ? '19' + s[0] : s[0] + '';
    +            return parseInt(this.strtotime(s[2] + ' ' + s[1] + ' ' + s[0] + ' ' + match[2]) + (match[4] ? match[4] / 1000 : ''), 10);
             }
     
    -        var regex = '([+-]?\\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)?';
    +        var regex = '([+-]?\\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)?';
     
    -        match = str.match(new RegExp(regex, 'gi')); // Brett: seems should be case insensitive per docs, so added 'i'
    -        if (match === null) {
    + match = str.match(new RegExp(regex, 'gi')); // Brett: seems should be case insensitive per docs, so added 'i' + if (match === null) {
  • -
  • +
  • - +

    Try to parse ISO8601 in IE8

    -
                try {
    +            
                try {
                     num = Twig.lib.parseISO8601Date(upperCaseStr);
    -                if (num) {
    -                    return num / 1000 | 0;
    +                if (num) {
    +                    return num / 1000 | 0;
                    }
    -            } catch (err) {
    -                return false;
    +            } catch (err) {
    +                return false;
                 }
    -            return false;
    +            return false;
             }
     
    -        for (i = 0, l = match.length; i < l; i++) {
    -            if (!process(match[i].split(' '))) {
    -                return false;
    +        for (i = 0, l = match.length; i < l; i++) {
    +            if (!process(match[i].split(' '))) {
    +                return false;
                 }
             }
     
    -        return now.getTime() / 1000 | 0;
    +        return now.getTime() / 1000 | 0;
         };
     
    -    Twig.lib.is = function(type, obj) {
    -        var clas = Object.prototype.toString.call(obj).slice(8, -1);
    -        return obj !== undefined && obj !== null && clas === type;
    +    Twig.lib.is = function(type, obj) {
    +        var clas = Object.prototype.toString.call(obj).slice(8, -1);
    +        return obj !== undefined && obj !== null && clas === type;
         };
  • -
  • +
  • - +

    shallow-copy an object

    -
        Twig.lib.copy = function(src) {
    -        var target = {},
    +            
        Twig.lib.copy = function(src) {
    +        var target = {},
                 key;
    -        for (key in src)
    +        for (key in src)
                 target[key] = src[key];
     
    -        return target;
    +        return target;
         };
     
    -    Twig.lib.replaceAll = function(string, search, replace) {
    -        return string.split(search).join(replace);
    -    };
    +    Twig.lib.replaceAll = function(string, search, replace) {
    +        return string.split(search).join(replace);
    +    };
    + +
  • + + +
  • +
    + +
    + +
    +

    chunk an array (arr) into arrays of (size) items, returns an array of arrays, or an empty array on invalid input

    + +
    + +
        Twig.lib.chunkArray = function (arr, size) {
    +        var returnVal = [],
    +            x = 0,
    +            len = arr.length;
     
    -    return Twig;
    +        if (size < 1 || !Twig.lib.is("Array", arr)) {
    +            return [];
    +        }
     
    -})(Twig || { });
    + while (x < len) { + returnVal.push(arr.slice(x, x += size)); + } + + return returnVal; + }; + + Twig.lib.round = function round(value, precision, mode) {
  • @@ -3341,12 +3350,64 @@

    twig.lib.js

    -
    Twig.js
    -Copyright (c) 2011-2013 John Roepke
    -Available under the BSD 2-Clause License
    -https://github.com/justjohn/twig.js
    -
    - +

    discuss at: http://phpjs.org/functions/round/ +original by: Philip Peterson + revised by: Onno Marsman + revised by: T.Wild + revised by: Rafał Kukawski (http://blog.kukawski.pl/) + input by: Greenseed + input by: meo + input by: William + input by: Josep Sanz (http://www.ws3.es/) +bugfixed by: Brett Zamir (http://brett-zamir.me) + note: Great work. Ideas for improvement: + note: - code more compliant with developer guidelines + note: - for implementing PHP constant arguments look at + note: the pathinfo() function, it offers the greatest + note: flexibility & compatibility possible + example 1: round(1241757, -3); + returns 1: 1242000 + example 2: round(3.6); + returns 2: 4 + example 3: round(2.835, 2); + returns 3: 2.84 + example 4: round(1.1749999999999, 2); + returns 4: 1.17 + example 5: round(58551.799999999996, 2); + returns 5: 58551.8

    + + + +
            var m, f, isHalf, sgn; // helper variables
    +        precision |= 0; // making sure precision is integer
    +        m = Math.pow(10, precision);
    +        value *= m;
    +        sgn = (value > 0) | -(value < 0); // sign of the number
    +        isHalf = value % 1 === 0.5 * sgn;
    +        f = Math.floor(value);
    +
    +        if (isHalf) {
    +            switch (mode) {
    +                case 'PHP_ROUND_HALF_DOWN':
    +                    value = f + (sgn < 0); // rounds .5 toward zero
    +                    break;
    +                case 'PHP_ROUND_HALF_EVEN':
    +                    value = f + (f % 2 * sgn); // rouds .5 towards the next even integer
    +                    break;
    +                case 'PHP_ROUND_HALF_ODD':
    +                    value = f + !(f % 2); // rounds .5 towards the next odd integer
    +                    break;
    +                default:
    +                    value = f + (sgn > 0); // rounds .5 away from zero
    +            }
    +        }
    +
    +        return (isHalf ? value : Math.round(value)) / m;
    +    }
    +
    +    return Twig;
    +
    +})(Twig || { });
    @@ -3357,45 +3418,49 @@

    twig.lib.js

    -

    twig.logic.js

    -

    This file handles tokenizing, compiling and parsing logic tokens. {% … %}

    +
    Twig.js
    +Copyright (c) 2011-2013 John Roepke
    +Available under the BSD 2-Clause License
    +https://github.com/justjohn/twig.js
    +

    twig.logic.js

    +

    This file handles tokenizing, compiling and parsing logic tokens. {% ... %}

    -
    var Twig = (function (Twig) {
    -    "use strict";
    +            
    var Twig = (function (Twig) {
    +    "use strict";
     
    -    /**
    +    /**
          * Namespace for logic handling.
          */
         Twig.logic = {};
     
    -    /**
    +    /**
          * Logic token types.
          */
         Twig.logic.type = {
    -        if_:       'Twig.logic.type.if',
    -        endif:     'Twig.logic.type.endif',
    -        for_:      'Twig.logic.type.for',
    -        endfor:    'Twig.logic.type.endfor',
    -        else_:     'Twig.logic.type.else',
    -        elseif:    'Twig.logic.type.elseif',
    -        set:       'Twig.logic.type.set',
    -        setcapture:'Twig.logic.type.setcapture',
    -        endset:    'Twig.logic.type.endset',
    -        filter:    'Twig.logic.type.filter',
    -        endfilter: 'Twig.logic.type.endfilter',
    -        block:     'Twig.logic.type.block',
    -        endblock:  'Twig.logic.type.endblock',
    -        extends_:  'Twig.logic.type.extends',
    -        use:       'Twig.logic.type.use',
    -        include:   'Twig.logic.type.include',
    -        spaceless: 'Twig.logic.type.spaceless',
    -        endspaceless: 'Twig.logic.type.endspaceless',
    -        macro:     'Twig.logic.type.macro',
    -        endmacro:  'Twig.logic.type.endmacro',
    -        import_:   'Twig.logic.type.import',
    -        from:      'Twig.logic.type.from'
    +        if_:       'Twig.logic.type.if',
    +        endif:     'Twig.logic.type.endif',
    +        for_:      'Twig.logic.type.for',
    +        endfor:    'Twig.logic.type.endfor',
    +        else_:     'Twig.logic.type.else',
    +        elseif:    'Twig.logic.type.elseif',
    +        set:       'Twig.logic.type.set',
    +        setcapture:'Twig.logic.type.setcapture',
    +        endset:    'Twig.logic.type.endset',
    +        filter:    'Twig.logic.type.filter',
    +        endfilter: 'Twig.logic.type.endfilter',
    +        block:     'Twig.logic.type.block',
    +        endblock:  'Twig.logic.type.endblock',
    +        extends_:  'Twig.logic.type.extends',
    +        use:       'Twig.logic.type.use',
    +        include:   'Twig.logic.type.include',
    +        spaceless: 'Twig.logic.type.spaceless',
    +        endspaceless: 'Twig.logic.type.endspaceless',
    +        macro:     'Twig.logic.type.macro',
    +        endmacro:  'Twig.logic.type.endmacro',
    +        import_:   'Twig.logic.type.import',
    +        from:      'Twig.logic.type.from'
         };
    @@ -3417,33 +3482,33 @@

    twig.logic.js

    logic token is assumed to not require an end tag and isn't push onto the stack. open: Does this tag open a logic expression or is it standalone. For example, - {% endif %} cannot exist without an opening {% if ... %} tag, so open = false. -

    Functions:

    -
     compile: A function that handles compiling the token into an output token ready for
    -          parsing with the parse function.
    +        {% endif %} cannot exist without an opening {% if ... %} tag, so open = false.
    +

    Functions:

    +
     compile: A function that handles compiling the token into an output token ready for
    +          parsing with the parse function.
    +
    + parse:   A function that parses the compiled token into output (HTML / whatever the
    +          template represents).
    - parse: A function that parses the compiled token into output (HTML / whatever the - template represents). -
        Twig.logic.definitions = [
             {
    -            /**
    +            /**
                  * If type logic tokens.
                  *
                  *  Format: {% if expression %}
                  */
                 type: Twig.logic.type.if_,
    -            regex: /^if\s+([^\s].+)$/,
    +            regex: /^if\s+([^\s].+)$/,
                 next: [
                     Twig.logic.type.else_,
                     Twig.logic.type.elseif,
                     Twig.logic.type.endif
                 ],
    -            open: true,
    -            compile: function (token) {
    -                var expression = token.match[1];
    + open: true, + compile: function (token) { + var expression = token.match[1];
    @@ -3458,15 +3523,15 @@

    twig.logic.js

    -
                    token.stack = Twig.expression.compile.apply(this, [{
    +            
                    token.stack = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
    -                delete token.match;
    -                return token;
    +                delete token.match;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    -                var output = '',
    + parse: function (token, context, chain) { + var output = '',
    @@ -3481,7 +3546,7 @@

    twig.logic.js

    -
                        result = Twig.expression.parse.apply(this, [token.stack, context]);
    +
                        result = Twig.expression.parse.apply(this, [token.stack, context]);
    @@ -3496,10 +3561,10 @@

    twig.logic.js

    -
                    chain = true;
    +            
                    chain = true;
     
    -                if (result) {
    -                    chain = false;
    + if (result) { + chain = false;
    @@ -3514,30 +3579,30 @@

    twig.logic.js

    -
                        output = Twig.parse.apply(this, [token.output, context]);
    +            
                        output = Twig.parse.apply(this, [token.output, context]);
                     }
    -                return {
    +                return {
                         chain: chain,
                         output: output
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * Else if type logic tokens.
                  *
                  *  Format: {% elseif expression %}
                  */
                 type: Twig.logic.type.elseif,
    -            regex: /^elseif\s+([^\s].*)$/,
    +            regex: /^elseif\s+([^\s].*)$/,
                 next: [
                     Twig.logic.type.else_,
                     Twig.logic.type.elseif,
                     Twig.logic.type.endif
                 ],
    -            open: false,
    -            compile: function (token) {
    -                var expression = token.match[1];
    + open: false, + compile: function (token) { + var expression = token.match[1];
    @@ -3552,18 +3617,18 @@

    twig.logic.js

    -
                    token.stack = Twig.expression.compile.apply(this, [{
    +            
                    token.stack = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
    -                delete token.match;
    -                return token;
    +                delete token.match;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    -                var output = '';
    +            parse: function (token, context, chain) {
    +                var output = '';
     
    -                if (chain && Twig.expression.parse.apply(this, [token.stack, context]) === true) {
    -                    chain = false;
    + if (chain && Twig.expression.parse.apply(this, [token.stack, context]) === true) { + chain = false;
    @@ -3578,81 +3643,81 @@

    twig.logic.js

    -
                        output = Twig.parse.apply(this, [token.output, context]);
    +            
                        output = Twig.parse.apply(this, [token.output, context]);
                     }
     
    -                return {
    +                return {
                         chain: chain,
                         output: output
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * Else if type logic tokens.
                  *
                  *  Format: {% elseif expression %}
                  */
                 type: Twig.logic.type.else_,
    -            regex: /^else$/,
    +            regex: /^else$/,
                 next: [
                     Twig.logic.type.endif,
                     Twig.logic.type.endfor
                 ],
    -            open: false,
    -            parse: function (token, context, chain) {
    -                var output = '';
    -                if (chain) {
    -                    output = Twig.parse.apply(this, [token.output, context]);
    +            open: false,
    +            parse: function (token, context, chain) {
    +                var output = '';
    +                if (chain) {
    +                    output = Twig.parse.apply(this, [token.output, context]);
                     }
    -                return {
    +                return {
                         chain: chain,
                         output: output
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * End if type logic tokens.
                  *
                  *  Format: {% endif %}
                  */
                 type: Twig.logic.type.endif,
    -            regex: /^endif$/,
    +            regex: /^endif$/,
                 next: [ ],
    -            open: false
    +            open: false
             },
             {
    -            /**
    +            /**
                  * For type logic tokens.
                  *
                  *  Format: {% for expression %}
                  */
                 type: Twig.logic.type.for_,
    -            regex: /^for\s+([a-zA-Z0-9_,\s]+)\s+in\s+([^\s].*?)(?:\s+if\s+([^\s].*))?$/,
    +            regex: /^for\s+([a-zA-Z0-9_,\s]+)\s+in\s+([^\s].*?)(?:\s+if\s+([^\s].*))?$/,
                 next: [
                     Twig.logic.type.else_,
                     Twig.logic.type.endfor
                 ],
    -            open: true,
    -            compile: function (token) {
    -                var key_value = token.match[1],
    -                    expression = token.match[2],
    -                    conditional = token.match[3],
    -                    kv_split = null;
    -
    -                token.key_var = null;
    -                token.value_var = null;
    -
    -                if (key_value.indexOf(",") >= 0) {
    -                    kv_split = key_value.split(',');
    -                    if (kv_split.length === 2) {
    -                        token.key_var = kv_split[0].trim();
    -                        token.value_var = kv_split[1].trim();
    -                    } else {
    -                        throw new Twig.Error("Invalid expression in for loop: " + key_value);
    +            open: true,
    +            compile: function (token) {
    +                var key_value = token.match[1],
    +                    expression = token.match[2],
    +                    conditional = token.match[3],
    +                    kv_split = null;
    +
    +                token.key_var = null;
    +                token.value_var = null;
    +
    +                if (key_value.indexOf(",") >= 0) {
    +                    kv_split = key_value.split(',');
    +                    if (kv_split.length === 2) {
    +                        token.key_var = kv_split[0].trim();
    +                        token.value_var = kv_split[1].trim();
    +                    } else {
    +                        throw new Twig.Error("Invalid expression in for loop: " + key_value);
                         }
    -                } else {
    +                } else {
                         token.value_var = key_value;
                     }
    @@ -3668,23 +3733,11 @@

    twig.logic.js

    Valid expressions for a for loop for item in expression for key,item in expression

    +

    Compile the expression.

    - - - -
  • -
    - -
    - -
    -

    Compile the expression.

    - -
    - -
                    token.expression = Twig.expression.compile.apply(this, [{
    +            
                    token.expression = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
    @@ -3692,77 +3745,77 @@

    twig.logic.js

  • -
  • +
  • - +

    Compile the conditional (if available)

    -
                    if (conditional) {
    -                    token.conditional = Twig.expression.compile.apply(this, [{
    +            
                    if (conditional) {
    +                    token.conditional = Twig.expression.compile.apply(this, [{
                             type:  Twig.expression.type.expression,
                             value: conditional
                         }]).stack;
                     }
     
    -                delete token.match;
    -                return token;
    +                delete token.match;
    +                return token;
                 },
    -            parse: function (token, context, continue_chain) {
    + parse: function (token, context, continue_chain) {
  • -
  • +
  • - +

    Parse expression

    -
                    var result = Twig.expression.parse.apply(this, [token.expression, context]),
    +            
                    var result = Twig.expression.parse.apply(this, [token.expression, context]),
                         output = [],
     					len,
    -					index = 0,
    +					index = 0,
                         keyset,
    -                    that = this,
    +                    that = this,
                         conditional = token.conditional,
    -                    buildLoop = function(index, len) {
    -                        var isConditional = conditional !== undefined;
    -                        return {
    -                            index: index+1,
    +                    buildLoop = function(index, len) {
    +                        var isConditional = conditional !== undefined;
    +                        return {
    +                            index: index+1,
                                 index0: index,
    -                            revindex: isConditional?undefined:len-index,
    -                            revindex0: isConditional?undefined:len-index-1,
    -                            first: (index === 0),
    -                            last: isConditional?undefined:(index === len-1),
    -                            length: isConditional?undefined:len,
    +                            revindex: isConditional?undefined:len-index,
    +                            revindex0: isConditional?undefined:len-index-1,
    +                            first: (index === 0),
    +                            last: isConditional?undefined:(index === len-1),
    +                            length: isConditional?undefined:len,
                                 parent: context
                             };
                         },
    -                    loop = function(key, value) {
    -                        var inner_context = Twig.lib.copy(context);
    +                    loop = function(key, value) {
    +                        var inner_context = Twig.lib.copy(context);
     
                             inner_context[token.value_var] = value;
    -                        if (token.key_var) {
    +                        if (token.key_var) {
                                 inner_context[token.key_var] = key;
                             }
  • -
  • +
  • - +

    Loop object

    @@ -3770,44 +3823,44 @@

    twig.logic.js

                            inner_context.loop = buildLoop(index, len);
     
    -                        if (conditional === undefined ||
    +                        if (conditional === undefined ||
                                 Twig.expression.parse.apply(that, [conditional, inner_context]))
                             {
                                 output.push(Twig.parse.apply(that, [token.output, inner_context]));
    -                            index += 1;
    +                            index += 1;
                             }
                         };
     
    -                if (result instanceof Array) {
    +                if (result instanceof Array) {
                         len = result.length;
    -                    Twig.forEach(result, function (value) {
    -                        var key = index;
    +                    Twig.forEach(result, function (value) {
    +                        var key = index;
     
                             loop(key, value);
                         });
    -                } else if (result instanceof Object) {
    -                    if (result._keys !== undefined) {
    +                } else if (result instanceof Object) {
    +                    if (result._keys !== undefined) {
                             keyset = result._keys;
    -                    } else {
    -                        keyset = Object.keys(result);
    +                    } else {
    +                        keyset = Object.keys(result);
                         }
     					len = keyset.length;
    -                    Twig.forEach(keyset, function(key) {
    + Twig.forEach(keyset, function(key) {
  • -
  • +
  • - +
    -

    Ignore the _keys property, it’s internal to twig.js

    +

    Ignore the _keys property, it's internal to twig.js

    -
                            if (key === "_keys") return;
    +            
                            if (key === "_keys") return;
     
                             loop(key,  result[key]);
                         });
    @@ -3816,63 +3869,63 @@ 

    twig.logic.js

  • -
  • +
  • - +

    Only allow else statements if no output was generated

    -
                    continue_chain = (output.length === 0);
    +            
                    continue_chain = (output.length === 0);
     
    -                return {
    +                return {
                         chain: continue_chain,
    -                    output: output.join("")
    +                    output: output.join("")
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * End if type logic tokens.
                  *
                  *  Format: {% endif %}
                  */
                 type: Twig.logic.type.endfor,
    -            regex: /^endfor$/,
    +            regex: /^endfor$/,
                 next: [ ],
    -            open: false
    +            open: false
             },
             {
    -            /**
    +            /**
                  * Set type logic tokens.
                  *
                  *  Format: {% set key = expression %}
                  */
                 type: Twig.logic.type.set,
    -            regex: /^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*(.+)$/,
    +            regex: /^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*(.+)$/,
                 next: [ ],
    -            open: true,
    -            compile: function (token) {
    -                var key = token.match[1].trim(),
    -                    expression = token.match[2],
    + open: true, + compile: function (token) { + var key = token.match[1].trim(), + expression = token.match[2],
  • -
  • +
  • - +

    Compile the expression.

    -
                        expression_stack  = Twig.expression.compile.apply(this, [{
    +            
                        expression_stack  = Twig.expression.compile.apply(this, [{
                             type:  Twig.expression.type.expression,
                             value: expression
                         }]).stack;
    @@ -3880,447 +3933,447 @@ 

    twig.logic.js

    token.key = key; token.expression = expression_stack; - delete token.match; - return token; + delete token.match; + return token; }, - parse: function (token, context, continue_chain) { - var value = Twig.expression.parse.apply(this, [token.expression, context]), + parse: function (token, context, continue_chain) { + var value = Twig.expression.parse.apply(this, [token.expression, context]), key = token.key;
  • -
  • +
  • - +

    set on both the global and local context

    -
                    this.context[key] = value;
    +            
                    this.context[key] = value;
                     context[key] = value;
     
    -                return {
    +                return {
                         chain: continue_chain,
                         context: context
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * Set capture type logic tokens.
                  *
                  *  Format: {% set key %}
                  */
                 type: Twig.logic.type.setcapture,
    -            regex: /^set\s+([a-zA-Z0-9_,\s]+)$/,
    +            regex: /^set\s+([a-zA-Z0-9_,\s]+)$/,
                 next: [
                     Twig.logic.type.endset
                 ],
    -            open: true,
    -            compile: function (token) {
    -                var key = token.match[1].trim();
    +            open: true,
    +            compile: function (token) {
    +                var key = token.match[1].trim();
     
                     token.key = key;
     
    -                delete token.match;
    -                return token;
    +                delete token.match;
    +                return token;
                 },
    -            parse: function (token, context, continue_chain) {
    +            parse: function (token, context, continue_chain) {
     
    -                var value = Twig.parse.apply(this, [token.output, context]),
    +                var value = Twig.parse.apply(this, [token.output, context]),
                         key = token.key;
  • -
  • +
  • - +

    set on both the global and local context

    -
                    this.context[key] = value;
    +            
                    this.context[key] = value;
                     context[key] = value;
     
    -                return {
    +                return {
                         chain: continue_chain,
                         context: context
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * End set type block logic tokens.
                  *
                  *  Format: {% endset %}
                  */
                 type: Twig.logic.type.endset,
    -            regex: /^endset$/,
    +            regex: /^endset$/,
                 next: [ ],
    -            open: false
    +            open: false
             },
             {
    -            /**
    +            /**
                  * Filter logic tokens.
                  *
                  *  Format: {% filter upper %} or {% filter lower|escape %}
                  */
                 type: Twig.logic.type.filter,
    -            regex: /^filter\s+(.+)$/,
    +            regex: /^filter\s+(.+)$/,
                 next: [
                     Twig.logic.type.endfilter
                 ],
    -            open: true,
    -            compile: function (token) {
    -                var expression = "|" + token.match[1].trim();
    + open: true, + compile: function (token) { + var expression = "|" + token.match[1].trim();
  • -
  • +
  • - +

    Compile the expression.

    -
                    token.stack = Twig.expression.compile.apply(this, [{
    +            
                    token.stack = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
    -                delete token.match;
    -                return token;
    +                delete token.match;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    -                var unfiltered = Twig.parse.apply(this, [token.output, context]),
    +            parse: function (token, context, chain) {
    +                var unfiltered = Twig.parse.apply(this, [token.output, context]),
                         stack = [{
                             type: Twig.expression.type.string,
                             value: unfiltered
                         }].concat(token.stack);
     
    -                var output = Twig.expression.parse.apply(this, [stack, context]);
    +                var output = Twig.expression.parse.apply(this, [stack, context]);
     
    -                return {
    +                return {
                         chain: chain,
                         output: output
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * End filter logic tokens.
                  *
                  *  Format: {% endfilter %}
                  */
                 type: Twig.logic.type.endfilter,
    -            regex: /^endfilter$/,
    +            regex: /^endfilter$/,
                 next: [ ],
    -            open: false
    +            open: false
             },
             {
    -            /**
    +            /**
                  * Block logic tokens.
                  *
                  *  Format: {% block title %}
                  */
                 type: Twig.logic.type.block,
    -            regex: /^block\s+([a-zA-Z0-9_]+)$/,
    +            regex: /^block\s+([a-zA-Z0-9_]+)$/,
                 next: [
                     Twig.logic.type.endblock
                 ],
    -            open: true,
    -            compile: function (token) {
    -                token.block = token.match[1].trim();
    -                delete token.match;
    -                return token;
    +            open: true,
    +            compile: function (token) {
    +                token.block = token.match[1].trim();
    +                delete token.match;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    -                var block_output = "",
    -                    output = "",
    -                    hasParent = this.blocks[token.block] && this.blocks[token.block].indexOf(Twig.placeholders.parent) > -1;
    + parse: function (token, context, chain) { + var block_output = "", + output = "", + hasParent = this.blocks[token.block] && this.blocks[token.block].indexOf(Twig.placeholders.parent) > -1;
  • -
  • +
  • - +
    -

    Don’t override previous blocks +

    Don't override previous blocks Loops should be exempted as well.

    -
                    if (this.blocks[token.block] === undefined || hasParent || context.loop) {
    -                    block_output = Twig.expression.parse.apply(this, [{
    +            
                    if (this.blocks[token.block] === undefined || hasParent || context.loop) {
    +                    block_output = Twig.expression.parse.apply(this, [{
                             type: Twig.expression.type.string,
    -                        value: Twig.parse.apply(this, [token.output, context])
    +                        value: Twig.parse.apply(this, [token.output, context])
                         }, context]);
     
    -                    if (hasParent) {
    -                        this.blocks[token.block] =  this.blocks[token.block].replace(Twig.placeholders.parent, block_output);
    -                    } else {
    -                        this.blocks[token.block] = block_output;
    +                    if (hasParent) {
    +                        this.blocks[token.block] =  this.blocks[token.block].replace(Twig.placeholders.parent, block_output);
    +                    } else {
    +                        this.blocks[token.block] = block_output;
                         }
                     }
  • -
  • +
  • - +

    Check if a child block has been set from a template extending this one.

    -
                    if (this.child.blocks[token.block]) {
    -                    output = this.child.blocks[token.block];
    -                } else {
    -                    output = this.blocks[token.block];
    +            
                    if (this.child.blocks[token.block]) {
    +                    output = this.child.blocks[token.block];
    +                } else {
    +                    output = this.blocks[token.block];
                     }
     
    -                return {
    +                return {
                         chain: chain,
                         output: output
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * End block logic tokens.
                  *
                  *  Format: {% endblock %}
                  */
                 type: Twig.logic.type.endblock,
    -            regex: /^endblock(?:\s+([a-zA-Z0-9_]+))?$/,
    +            regex: /^endblock(?:\s+([a-zA-Z0-9_]+))?$/,
                 next: [ ],
    -            open: false
    +            open: false
             },
             {
    -            /**
    +            /**
                  * Block logic tokens.
                  *
                  *  Format: {% extends "template.twig" %}
                  */
                 type: Twig.logic.type.extends_,
    -            regex: /^extends\s+(.+)$/,
    +            regex: /^extends\s+(.+)$/,
                 next: [ ],
    -            open: true,
    -            compile: function (token) {
    -                var expression = token.match[1].trim();
    -                delete token.match;
    +            open: true,
    +            compile: function (token) {
    +                var expression = token.match[1].trim();
    +                delete token.match;
     
    -                token.stack   = Twig.expression.compile.apply(this, [{
    +                token.stack   = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
     
    -                return token;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    + parse: function (token, context, chain) {
  • -
  • +
  • - +

    Resolve filename

    -
                    var file = Twig.expression.parse.apply(this, [token.stack, context]);
    +
                    var file = Twig.expression.parse.apply(this, [token.stack, context]);
  • -
  • +
  • - +

    Set parent template

    -
                    this.extend = file;
    +            
                    this.extend = file;
     
    -                return {
    +                return {
                         chain: chain,
    -                    output: ''
    +                    output: ''
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * Block logic tokens.
                  *
                  *  Format: {% extends "template.twig" %}
                  */
                 type: Twig.logic.type.use,
    -            regex: /^use\s+(.+)$/,
    +            regex: /^use\s+(.+)$/,
                 next: [ ],
    -            open: true,
    -            compile: function (token) {
    -                var expression = token.match[1].trim();
    -                delete token.match;
    +            open: true,
    +            compile: function (token) {
    +                var expression = token.match[1].trim();
    +                delete token.match;
     
    -                token.stack = Twig.expression.compile.apply(this, [{
    +                token.stack = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
     
    -                return token;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    + parse: function (token, context, chain) {
  • -
  • +
  • - +

    Resolve filename

    -
                    var file = Twig.expression.parse.apply(this, [token.stack, context]);
    +
                    var file = Twig.expression.parse.apply(this, [token.stack, context]);
  • -
  • +
  • - +

    Import blocks

    -
                    this.importBlocks(file);
    +            
                    this.importBlocks(file);
     
    -                return {
    +                return {
                         chain: chain,
    -                    output: ''
    +                    output: ''
                     };
                 }
             },
             {
    -            /**
    +            /**
                  * Block logic tokens.
                  *
                  *  Format: {% includes "template.twig" [with {some: 'values'} only] %}
                  */
                 type: Twig.logic.type.include,
    -            regex: /^include\s+(ignore missing\s+)?(.+?)\s*(?:with\s+(.+?))?\s*(only)?$/,
    +            regex: /^include\s+(ignore missing\s+)?(.+?)\s*(?:with\s+(.+?))?\s*(only)?$/,
                 next: [ ],
    -            open: true,
    -            compile: function (token) {
    -                var match = token.match,
    -                    includeMissing = match[1] !== undefined,
    -                    expression = match[2].trim(),
    -                    withContext = match[3],
    -                    only = ((match[4] !== undefined) && match[4].length);
    +            open: true,
    +            compile: function (token) {
    +                var match = token.match,
    +                    includeMissing = match[1] !== undefined,
    +                    expression = match[2].trim(),
    +                    withContext = match[3],
    +                    only = ((match[4] !== undefined) && match[4].length);
     
    -                delete token.match;
    +                delete token.match;
     
                     token.only = only;
                     token.includeMissing = includeMissing;
     
    -                token.stack = Twig.expression.compile.apply(this, [{
    +                token.stack = Twig.expression.compile.apply(this, [{
                         type:  Twig.expression.type.expression,
                         value: expression
                     }]).stack;
     
    -                if (withContext !== undefined) {
    -                    token.withStack = Twig.expression.compile.apply(this, [{
    +                if (withContext !== undefined) {
    +                    token.withStack = Twig.expression.compile.apply(this, [{
                             type:  Twig.expression.type.expression,
                             value: withContext.trim()
                         }]).stack;
                     }
     
    -                return token;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    + parse: function (token, context, chain) {
  • -
  • +
  • - +

    Resolve filename

    -
                    var innerContext = {},
    +            
                    var innerContext = {},
                         withContext,
                         i,
                         template;
     
    -                if (!token.only) {
    -                    for (i in context) {
    -                        if (context.hasOwnProperty(i))
    +                if (!token.only) {
    +                    for (i in context) {
    +                        if (context.hasOwnProperty(i))
                                 innerContext[i] = context[i];
                         }
                     }
     
    -                if (token.withStack !== undefined) {
    -                    withContext = Twig.expression.parse.apply(this, [token.withStack, context]);
    +                if (token.withStack !== undefined) {
    +                    withContext = Twig.expression.parse.apply(this, [token.withStack, context]);
     
    -                    for (i in withContext) {
    -                        if (withContext.hasOwnProperty(i))
    +                    for (i in withContext) {
    +                        if (withContext.hasOwnProperty(i))
                                 innerContext[i] = withContext[i];
                         }
                     }
     
    -                var file = Twig.expression.parse.apply(this, [token.stack, innerContext]);
    + var file = Twig.expression.parse.apply(this, [token.stack, innerContext]);
  • -
  • +
  • - +

    Import file

    -
                    template = this.importFile(file);
    +            
                    template = this.importFile(file);
     
    -                return {
    +                return {
                         chain: chain,
                         output: template.render(innerContext)
                     };
    @@ -4328,60 +4381,60 @@ 

    twig.logic.js

    }, { type: Twig.logic.type.spaceless, - regex: /^spaceless$/, + regex: /^spaceless$/, next: [ Twig.logic.type.endspaceless ], - open: true,
    + 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]),
    +
                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,
    +
                        rBetweenTagSpaces = />\s+</g,
  • -
  • +
  • - +

    Replace all space between closing and opening html tags

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

    twig.logic.js

  • -
  • +
  • - +

    Add the {% endspaceless %} token

    @@ -4403,44 +4456,44 @@

    twig.logic.js

            {
                 type: Twig.logic.type.endspaceless,
    -            regex: /^endspaceless$/,
    +            regex: /^endspaceless$/,
                 next: [ ],
    -            open: false
    +            open: false
             },
             {
    -            /**
    +            /**
                  * Macro logic tokens.
                  *
                  * Format: {% maro input(name, value, type, size) %}
                  *
                  */
                 type: Twig.logic.type.macro,
    -            regex: /^macro\s+([a-zA-Z0-9_]+)\s?\((([a-zA-Z0-9_]+(,\s?)?)*)\)$/,
    +            regex: /^macro\s+([a-zA-Z0-9_]+)\s?\((([a-zA-Z0-9_]+(,\s?)?)*)\)$/,
                 next: [
                     Twig.logic.type.endmacro
                 ],
    -            open: true,
    -            compile: function (token) {
    -                var macroName = token.match[1],
    -                    parameters = token.match[2].split(/[ ,]+/);
    + open: true, + compile: function (token) { + var macroName = token.match[1], + parameters = token.match[2].split(/[ ,]+/);
  • -
  • +
  • - +

    TODO: Clean up duplicate check

    -
                    for (var i=0; i<parameters.length; i++) {
    -                    for (var j=0; j<parameters.length; j++){
    -                        if (parameters[i] === parameters[j] && i !== j) {
    -                            throw new Twig.Error("Duplicate arguments for parameter: "+ parameters[i]);
    +            
                    for (var i=0; i<parameters.length; i++) {
    +                    for (var j=0; j<parameters.length; j++){
    +                        if (parameters[i] === parameters[j] && i !== j) {
    +                            throw new Twig.Error("Duplicate arguments for parameter: "+ parameters[i]);
                             }
                         }
                     }
    @@ -4448,172 +4501,176 @@ 

    twig.logic.js

    token.macroName = macroName; token.parameters = parameters; - delete token.match; - return token; + delete token.match; + return token; }, - parse: function (token, context, chain) { - var template = this; - this.macros[token.macroName] = function() {
    + parse: function (token, context, chain) { + var template = this; + this.macros[token.macroName] = function() {
  • -
  • +
  • - +

    Pass global context and other macros

    -
                        var macroContext = {
    +            
                        var macroContext = {
                             _self: template.macros
                         }
  • -
  • +
  • - +

    Add parameters from context to macroContext

    -
                        for (var i=0; i<token.parameters.length; i++) {
    -                        var prop = token.parameters[i];
    -                        macroContext[prop] = arguments[i] || undefined;
    +            
                        for (var i=0; i<token.parameters.length; i++) {
    +                        var prop = token.parameters[i];
    +                        if(typeof arguments[i] !== 'undefined') {
    +                            macroContext[prop] = arguments[i];
    +                        } else {
    +                            macroContext[prop] = undefined;
    +                        }
                         }
  • -
  • +
  • - +

    Render

    -
                        return Twig.parse.apply(template, [token.output, macroContext])
    +            
                        return Twig.parse.apply(template, [token.output, macroContext])
                     };
     
    -                return {
    +                return {
                         chain: chain,
    -                    output: ''
    +                    output: ''
                     };
     
                 }
             },
             {
    -            /**
    +            /**
                  * End macro logic tokens.
                  *
                  * Format: {% endmacro %}
                  */
                  type: Twig.logic.type.endmacro,
    -             regex: /^endmacro$/,
    +             regex: /^endmacro$/,
                  next: [ ],
    -             open: false
    +             open: false
             },
             {
    -            /*
    +            /*
                 * import logic tokens.
                 *
                 * Format: {% import "template.twig" as form %}
                 */
                 type: Twig.logic.type.import_,
    -            regex: /^import\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/,
    +            regex: /^import\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/,
                 next: [ ],
    -            open: true,
    -            compile: function (token) {
    -                var expression = token.match[1].trim(),
    -                    contextName = token.match[2].trim();
    -                delete token.match;
    +            open: true,
    +            compile: function (token) {
    +                var expression = token.match[1].trim(),
    +                    contextName = token.match[2].trim();
    +                delete token.match;
     
                     token.expression = expression;
                     token.contextName = contextName;
     
    -                token.stack = Twig.expression.compile.apply(this, [{
    +                token.stack = Twig.expression.compile.apply(this, [{
                         type: Twig.expression.type.expression,
                         value: expression
                     }]).stack;
     
    -                return token;
    +                return token;
                 },
    -            parse: function (token, context, chain) {
    -                if (token.expression !== "_self") {
    -                    var file = Twig.expression.parse.apply(this, [token.stack, context]);
    -                    var template = this.importMacros(file || token.expression);
    -                    context[token.contextName] = template.render({}, {output: 'macros'});
    +            parse: function (token, context, chain) {
    +                if (token.expression !== "_self") {
    +                    var file = Twig.expression.parse.apply(this, [token.stack, context]);
    +                    var template = this.importMacros(file || token.expression);
    +                    context[token.contextName] = template.render({}, {output: 'macros'});
                     }
    -                else {
    -                    context[token.contextName] = this.macros;
    +                else {
    +                    context[token.contextName] = this.macros;
                     }
     
    -                return {
    +                return {
                         chain: chain,
    -                    output: ''
    +                    output: ''
                     }
     
                 }
             },
             {
    -            /*
    +            /*
                 * from logic tokens.
                 *
                 * Format: {% from "template.twig" import func as form %}
                 */
                 type: Twig.logic.type.from,
    -            regex: /^from\s+(.+)\s+import\s+([a-zA-Z0-9_, ]+)$/,
    +            regex: /^from\s+(.+)\s+import\s+([a-zA-Z0-9_, ]+)$/,
                 next: [ ],
    -            open: true,
    -            compile: function (token) {
    -                var expression = token.match[1].trim(),
    -                    macroExpressions = token.match[2].trim().split(/[ ,]+/),
    +            open: true,
    +            compile: function (token) {
    +                var expression = token.match[1].trim(),
    +                    macroExpressions = token.match[2].trim().split(/[ ,]+/),
                         macroNames = {};
     
    -                for (var i=0; i<macroExpressions.length; i++) {
    -                    var res = macroExpressions[i];
    + for (var i=0; i<macroExpressions.length; i++) { + var res = macroExpressions[i];
  • -
  • +
  • - +

    match function as variable

    -
                        var macroMatch = res.match(/^([a-zA-Z0-9_]+)\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/);
    -                    if (macroMatch) {
    -                        macroNames[macroMatch[1].trim()] = macroMatch[2].trim();
    +            
                        var macroMatch = res.match(/^([a-zA-Z0-9_]+)\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/);
    +                    if (macroMatch) {
    +                        macroNames[macroMatch[1].trim()] = macroMatch[2].trim();
                         }
    -                    else if (res.match(/^([a-zA-Z0-9_]+)$/)) {
    +                    else if (res.match(/^([a-zA-Z0-9_]+)$/)) {
                             macroNames[res] = res;
                         }
    -                    else {
    + else {
  • -
  • +
  • - +

    ignore import

    @@ -4623,39 +4680,39 @@

    twig.logic.js

    } - delete token.match; + delete token.match; token.expression = expression; token.macroNames = macroNames; - token.stack = Twig.expression.compile.apply(this, [{ + token.stack = Twig.expression.compile.apply(this, [{ type: Twig.expression.type.expression, value: expression }]).stack; - return token; + return token; }, - parse: function (token, context, chain) { - var macros; + parse: function (token, context, chain) { + var macros; - if (token.expression !== "_self") { - var file = Twig.expression.parse.apply(this, [token.stack, context]); - var template = this.importMacros(file || token.expression); - macros = template.render({}, {output: 'macros'}); + if (token.expression !== "_self") { + var file = Twig.expression.parse.apply(this, [token.stack, context]); + var template = this.importMacros(file || token.expression); + macros = template.render({}, {output: 'macros'}); } - else { - macros = this.macros; + else { + macros = this.macros; } - for (var macroName in token.macroNames) { - if (macros.hasOwnProperty(macroName)) { + for (var macroName in token.macroNames) { + if (macros.hasOwnProperty(macroName)) { context[token.macroNames[macroName]] = macros[macroName]; } } - return { + return { chain: chain, - output: '' + output: '' } } @@ -4664,20 +4721,20 @@

    twig.logic.js

    ]; - /** + /** * Registry for logic handlers. */ Twig.logic.handler = {}; - /** + /** * Define a new token type, available at Twig.logic.type.{type} */ - Twig.logic.extendType = function (type, value) { - value = value || ("Twig.logic.type" + type); + Twig.logic.extendType = function (type, value) { + value = value || ("Twig.logic.type" + type); Twig.logic.type[type] = value; }; - /** + /** * Extend the logic parsing functionality with a new token definition. * * // Define a new tag @@ -4696,15 +4753,15 @@

    twig.logic.js

    * * @param {Object} definition The new logic expression. */
    - Twig.logic.extend = function (definition) { + Twig.logic.extend = function (definition) { - if (!definition.type) { - throw new Twig.Error("Unable to extend logic definition. No type provided for " + definition); + if (!definition.type) { + throw new Twig.Error("Unable to extend logic definition. No type provided for " + definition); } - if (Twig.logic.type[definition.type]) { - throw new Twig.Error("Unable to extend logic definitions. Type " + - definition.type + " is already defined."); - } else { + if (Twig.logic.type[definition.type]) { + throw new Twig.Error("Unable to extend logic definitions. Type " + + definition.type + " is already defined."); + } else { Twig.logic.extendType(definition.type); } Twig.logic.handler[definition.type] = definition; @@ -4713,54 +4770,54 @@

    twig.logic.js

  • -
  • +
  • - +

    Extend with built-in expressions

    -
        while (Twig.logic.definitions.length > 0) {
    +            
        while (Twig.logic.definitions.length > 0) {
             Twig.logic.extend(Twig.logic.definitions.shift());
         }
     
    -    /**
    +    /**
          * Compile a logic token into an object ready for parsing.
          *
          * @param {Object} raw_token An uncompiled logic token.
          *
          * @return {Object} A compiled logic token, ready for parsing.
          */
    -    Twig.logic.compile = function (raw_token) {
    -        var expression = raw_token.value.trim(),
    -            token = Twig.logic.tokenize.apply(this, [expression]),
    +    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 = token_template.compile.apply(this, [token]);
    -            Twig.log.trace("Twig.logic.compile: ", "Compiled logic token to ", token);
    +            
            if (token_template.compile) {
    +            token = token_template.compile.apply(this, [token]);
    +            Twig.log.trace("Twig.logic.compile: ", "Compiled logic token to ", token);
             }
     
    -        return token;
    +        return token;
         };
     
    -    /**
    +    /**
          * Tokenize logic expressions. This function matches token expressions against regular
          * expressions provided in token definitions provided with Twig.logic.extend.
          *
    @@ -4769,23 +4826,23 @@ 

    twig.logic.js

    * * @return {Object} The matched token with type set to the token type and match to the regex match. */
    - Twig.logic.tokenize = function (expression) { - var token = {}, - token_template_type = null, - token_type = null, - token_regex = null, - regex_array = null, - regex = null, - match = null;
    + Twig.logic.tokenize = function (expression) { + var token = {}, + token_template_type = null, + token_type = null, + token_regex = null, + regex_array = null, + regex = null, + match = null;
  • -
  • +
  • - +

    Ignore whitespace around expressions.

    @@ -4793,17 +4850,17 @@

    twig.logic.js

            expression = expression.trim();
     
    -        for (token_template_type in Twig.logic.handler) {
    -            if (Twig.logic.handler.hasOwnProperty(token_template_type)) {
    + 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

    @@ -4815,44 +4872,44 @@

    twig.logic.js

  • -
  • +
  • - +

    Handle multiple regular expressions per type.

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

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

    -
                    while (regex_array.length > 0) {
    +            
                    while (regex_array.length > 0) {
                         regex = regex_array.shift();
                         match = regex.exec(expression.trim());
    -                    if (match !== null) {
    +                    if (match !== null) {
                             token.type  = token_type;
                             token.match = match;
    -                        Twig.log.trace("Twig.logic.tokenize: ", "Matched a ", token_type, " regular expression of ", match);
    -                        return token;
    +                        Twig.log.trace("Twig.logic.tokenize: ", "Matched a ", token_type, " regular expression of ", match);
    +                        return token;
                         }
                     }
                 }
    @@ -4861,20 +4918,20 @@ 

    twig.logic.js

  • -
  • +
  • - +

    No regex matches

    -
            throw new Twig.Error("Unable to parse '" + expression.trim() + "'");
    +            
            throw new Twig.Error("Unable to parse '" + expression.trim() + "'");
         };
     
    -    /**
    +    /**
          * Parse a logic token within a given context.
          *
          * What are logic chains?
    @@ -4894,105 +4951,93 @@ 

    twig.logic.js

    * @param {boolean} chain Is this an open logic chain. If false, that means a * chain is closed and no further cases should be parsed. */
    - Twig.logic.parse = function (token, context, chain) { - var output = '', + Twig.logic.parse = function (token, context, chain) { + var output = '', token_template; context = context || { }; - Twig.log.debug("Twig.logic.parse: ", "Parsing logic token ", token); + Twig.log.debug("Twig.logic.parse: ", "Parsing logic token ", token); token_template = Twig.logic.handler[token.type]; - if (token_template.parse) { - output = token_template.parse.apply(this, [token, context, chain]); + if (token_template.parse) { + output = token_template.parse.apply(this, [token, context, chain]); } - return output; + return output; }; - return Twig; + return Twig; })(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

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.expression.js

    This file handles tokenizing, compiling and parsing expressions.

    -
    var Twig = (function (Twig) {
    -    "use strict";
    +            
    var Twig = (function (Twig) {
    +    "use strict";
     
    -    /**
    +    /**
          * Namespace for expression handling.
          */
         Twig.expression = { };
     
    -    /**
    +    /**
          * Reserved word that can't be used as variable names.
          */
         Twig.expression.reservedWords = [
    -        "true", "false", "null", "_context"
    +        "true", "false", "null", "_context"
         ];
     
    -    /**
    +    /**
          * The type of tokens used in expressions.
          */
         Twig.expression.type = {
    -        comma:      'Twig.expression.type.comma',
    +        comma:      'Twig.expression.type.comma',
             operator: {
    -            unary:  'Twig.expression.type.operator.unary',
    -            binary: 'Twig.expression.type.operator.binary'
    +            unary:  'Twig.expression.type.operator.unary',
    +            binary: 'Twig.expression.type.operator.binary'
             },
    -        string:     'Twig.expression.type.string',
    -        bool:       'Twig.expression.type.bool',
    +        string:     'Twig.expression.type.string',
    +        bool:       'Twig.expression.type.bool',
             array: {
    -            start:  'Twig.expression.type.array.start',
    -            end:    'Twig.expression.type.array.end'
    +            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'
    +            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'
    +            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'
    +            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',
    -        context:    'Twig.expression.type.context',
    -        test:       'Twig.expression.type.test'
    +        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',
    +        context:    'Twig.expression.type.context',
    +        test:       'Twig.expression.type.test'
         };
     
         Twig.expression.set = {
    @@ -5000,11 +5045,11 @@

    twig.expression.js

  • -
  • +
  • - +

    What can follow an expression (in general)

    @@ -5037,13 +5082,13 @@

    twig.expression.js

  • -
  • +
  • - +
    -

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

    +

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

    @@ -5054,11 +5099,11 @@

    twig.expression.js

  • -
  • +
  • - +

    Some commonly used compile and parse functions.

    @@ -5066,19 +5111,19 @@

    twig.expression.js

        Twig.expression.fn = {
             compile: {
    -            push: function(token, stack, output) {
    +            push: function(token, stack, output) {
                     output.push(token);
                 },
    -            push_both: function(token, stack, output) {
    +            push_both: function(token, stack, output) {
                     output.push(token);
                     stack.push(token);
                 }
             },
             parse: {
    -            push: function(token, stack, context) {
    +            push: function(token, stack, context) {
                     stack.push(token);
                 },
    -            push_value: function(token, stack, context) {
    +            push_value: function(token, stack, context) {
                     stack.push(token.value);
                 }
             }
    @@ -5087,46 +5132,46 @@ 

    twig.expression.js

  • -
  • +
  • - +

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

    Properties:

    -
     type:  The type of expression this matches
    +
     type:  The type of expression this matches
     
      regex: One or more regular expressions that matche the format of the token.
     
    - next:  Valid tokens that can occur next in the expression.
    -

    Functions:

    -
     compile: A function that compiles the raw regular expression match into a token.
    + next:  Valid tokens that can occur next in the expression.
    +

    Functions:

    +
     compile: A function that compiles the raw regular expression match into a token.
    +
    + parse:   A function that parses the compiled token into output.
    - parse: A function that parses the compiled token into output. -
        Twig.expression.definitions = [
             {
                 type: Twig.expression.type.test,
    -            regex: /^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*)/,
    +            regex: /^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*)/,
                 next: Twig.expression.set.operations.concat([Twig.expression.type.parameter.start]),
    -            compile: function(token, stack, output) {
    -                token.filter   = token.match[2];
    -                token.modifier = token.match[1];
    -                delete token.match;
    -                delete token.value;
    +            compile: function(token, stack, output) {
    +                token.filter   = token.match[2];
    +                token.modifier = token.match[1];
    +                delete token.match;
    +                delete token.value;
                     output.push(token);
                 },
    -            parse: function(token, stack, context) {
    -                var value = stack.pop(),
    -                    params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
    +            parse: function(token, stack, context) {
    +                var value = stack.pop(),
    +                    params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
                         result = Twig.test(token.filter, value, params);
     
    -                if (token.modifier == 'not') {
    +                if (token.modifier == 'not') {
                         stack.push(!result);
    -                } else {
    +                } else {
                         stack.push(result);
                     }
                 }
    @@ -5137,45 +5182,45 @@ 

    twig.expression.js

  • -
  • +
  • - +

    Match a comma

    -
                regex: /^,/,
    +            
                regex: /^,/,
                 next: Twig.expression.set.expressions.concat([Twig.expression.type.array.end, Twig.expression.type.object.end]),
    -            compile: function(token, stack, output) {
    -                var i = stack.length - 1,
    +            compile: function(token, stack, output) {
    +                var i = stack.length - 1,
                         stack_token;
     
    -                delete token.match;
    -                delete token.value;
    + delete token.match; + delete token.value;
  • -
  • +
  • - +

    pop tokens off the stack until the start of the object

    -
                    for(;i >= 0; i--) {
    +            
                    for(;i >= 0; i--) {
                         stack_token = stack.pop();
    -                    if (stack_token.type === Twig.expression.type.object.start
    +                    if (stack_token.type === Twig.expression.type.object.start
                                 || stack_token.type === Twig.expression.type.parameter.start
                                 || stack_token.type === Twig.expression.type.array.start) {
                             stack.push(stack_token);
    -                        break;
    +                        break;
                         }
                         output.push(stack_token);
                     }
    @@ -5188,123 +5233,123 @@ 

    twig.expression.js

  • -
  • +
  • - +

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

    -
                regex: /(^[\+\-~%\?\:]|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^and\s+|^or\s+|^in\s+|^not in\s+|^\.\.)/,
    +            
                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;
    +            compile: function(token, stack, output) {
    +                delete token.match;
     
                     token.value = token.value.trim();
    -                var value = token.value,
    +                var value = token.value,
                         operator = Twig.expression.operator.lookup(value, token);
     
    -                Twig.log.trace("Twig.expression.compile: ", "Operator: ", operator, " from ", value);
    +                Twig.log.trace("Twig.expression.compile: ", "Operator: ", operator, " from ", value);
     
    -                while (stack.length > 0 &&
    -                       (stack[stack.length-1].type == Twig.expression.type.operator.unary || stack[stack.length-1].type == Twig.expression.type.operator.binary) &&
    +                while (stack.length > 0 &&
    +                       (stack[stack.length-1].type == Twig.expression.type.operator.unary || stack[stack.length-1].type == Twig.expression.type.operator.binary) &&
                                 (
                                     (operator.associativity === Twig.expression.operator.leftToRight &&
    -                                 operator.precidence    >= stack[stack.length-1].precidence) ||
    +                                 operator.precidence    >= stack[stack.length-1].precidence) ||
     
                                     (operator.associativity === Twig.expression.operator.rightToLeft &&
    -                                 operator.precidence    >  stack[stack.length-1].precidence)
    +                                 operator.precidence    >  stack[stack.length-1].precidence)
                                 )
                            ) {
    -                     var temp = stack.pop();
    +                     var temp = stack.pop();
                          output.push(temp);
                     }
     
    -                if (value === ":") {
    + if (value === ":") {
  • -
  • +
  • - +

    Check if this is a ternary or object key being set

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

    Continue as normal for a ternary

    -
                        } else {
    +
                        } 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();
    +            
                            var key_token = output.pop();
     
    -                        if (key_token.type === Twig.expression.type.string ||
    +                        if (key_token.type === Twig.expression.type.string ||
                                     key_token.type === Twig.expression.type.variable ||
                                     key_token.type === Twig.expression.type.number) {
                                 token.key = key_token.value;
     
    -                        } else {
    -                            throw new Twig.Error("Unexpected value before ':' of " + key_token.type + " = " + key_token.value);
    +                        } else {
    +                            throw new Twig.Error("Unexpected value before ':' of " + key_token.type + " = " + key_token.value);
                             }
     
                             output.push(token);
    -                        return;
    +                        return;
                         }
    -                } else {
    +                } else {
                         stack.push(operator);
                     }
                 },
    -            parse: function(token, stack, context) {
    -                if (token.key) {
    + parse: function(token, stack, context) { + if (token.key) {
  • -
  • +
  • - +
    -

    handle ternary ‘:’ operator

    +

    handle ternary ':' operator

                        stack.push(token);
    -                } else {
    +                } else {
                         Twig.expression.operator.parse(token.value, stack);
                     }
                 }
    @@ -5315,49 +5360,49 @@ 

    twig.expression.js

  • -
  • +
  • - +

    Match any of not

    -
                regex: /(^not\s+)/,
    +            
                regex: /(^not\s+)/,
                 next: Twig.expression.set.expressions,
    -            compile: function(token, stack, output) {
    -                delete token.match;
    +            compile: function(token, stack, output) {
    +                delete token.match;
     
                     token.value = token.value.trim();
    -                var value = token.value,
    +                var value = token.value,
                         operator = Twig.expression.operator.lookup(value, token);
     
    -                Twig.log.trace("Twig.expression.compile: ", "Operator: ", operator, " from ", value);
    +                Twig.log.trace("Twig.expression.compile: ", "Operator: ", operator, " from ", value);
     
    -                while (stack.length > 0 &&
    -                       (stack[stack.length-1].type == Twig.expression.type.operator.unary || stack[stack.length-1].type == Twig.expression.type.operator.binary) &&
    +                while (stack.length > 0 &&
    +                       (stack[stack.length-1].type == Twig.expression.type.operator.unary || stack[stack.length-1].type == Twig.expression.type.operator.binary) &&
                                 (
                                     (operator.associativity === Twig.expression.operator.leftToRight &&
    -                                 operator.precidence    >= stack[stack.length-1].precidence) ||
    +                                 operator.precidence    >= stack[stack.length-1].precidence) ||
     
                                     (operator.associativity === Twig.expression.operator.rightToLeft &&
    -                                 operator.precidence    >  stack[stack.length-1].precidence)
    +                                 operator.precidence    >  stack[stack.length-1].precidence)
                                 )
                            ) {
    -                     var temp = stack.pop();
    +                     var temp = stack.pop();
                          output.push(temp);
                     }
     
                     stack.push(operator);
                 },
    -            parse: function(token, stack, context) {
    +            parse: function(token, stack, context) {
                     Twig.expression.operator.parse(token.value, stack);
                 }
             },
             {
    -            /**
    +            /**
                  * Match a string. This is anything between a pair of single or double quotes.
                  */
                 type: Twig.expression.type.string,
    @@ -5365,69 +5410,69 @@

    twig.expression.js

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

    Remove the quotes from the string

    -
                    if (value.substring(0, 1) === '"') {
    -                    value = value.replace('\\"', '"');
    -                } else {
    -                    value = value.replace("\\'", "'");
    +            
                    if (value.substring(0, 1) === '"') {
    +                    value = value.replace('\\"', '"');
    +                } else {
    +                    value = value.replace("\\'", "'");
                     }
    -                token.value = value.substring(1, value.length-1).replace( /\\n/g, "\n" ).replace( /\\r/g, "\r" );
    -                Twig.log.trace("Twig.expression.compile: ", "String value: ", token.value);
    +                token.value = value.substring(1, value.length-1).replace( /\\n/g, "\n" ).replace( /\\r/g, "\r" );
    +                Twig.log.trace("Twig.expression.compile: ", "String value: ", token.value);
                     output.push(token);
                 },
                 parse: Twig.expression.fn.parse.push_value
             },
             {
    -            /**
    +            /**
                  * Match a parameter set start.
                  */
                 type: Twig.expression.type.parameter.start,
    -            regex: /^\(/,
    +            regex: /^\(/,
                 next: Twig.expression.set.expressions.concat([Twig.expression.type.parameter.end]),
                 compile: Twig.expression.fn.compile.push_both,
                 parse: Twig.expression.fn.parse.push
             },
             {
    -            /**
    +            /**
                  * Match a parameter set end.
                  */
                 type: Twig.expression.type.parameter.end,
    -            regex: /^\)/,
    +            regex: /^\)/,
                 next: Twig.expression.set.operations_extended,
    -            compile: function(token, stack, output) {
    -                var stack_token,
    +            compile: function(token, stack, output) {
    +                var stack_token,
                         end_token = token;
     
                     stack_token = stack.pop();
    -                while(stack.length > 0 && stack_token.type != Twig.expression.type.parameter.start) {
    +                while(stack.length > 0 && stack_token.type != Twig.expression.type.parameter.start) {
                         output.push(stack_token);
                         stack_token = stack.pop();
                     }
    @@ -5435,27 +5480,27 @@

    twig.expression.js

  • -
  • +
  • - +

    Move contents of parens into preceding filter

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

    Add token to arguments stack

    @@ -5466,40 +5511,40 @@

    twig.expression.js

    } param_stack.unshift(token); - var is_expression = false;
    + var is_expression = false;
  • -
  • +
  • - +

    Get the token preceding the parameters

    -
                    token = output[output.length-1];
    +            
                    token = output[output.length-1];
     
    -                if (token === undefined ||
    +                if (token === undefined ||
                         (token.type !== Twig.expression.type._function &&
                         token.type !== Twig.expression.type.filter &&
                         token.type !== Twig.expression.type.test &&
                         token.type !== Twig.expression.type.key.brackets &&
                         token.type !== Twig.expression.type.key.period)) {
     
    -                    end_token.expression = true;
    + end_token.expression = true;
  • -
  • +
  • - +

    remove start and end token from stack

    @@ -5512,47 +5557,47 @@

    twig.expression.js

    output.push(end_token); - } else { - end_token.expression = false; + } else { + end_token.expression = false; token.params = param_stack; } }, - parse: function(token, stack, context) { - var new_array = [], - array_ended = false, - value = null; + parse: function(token, stack, context) { + var new_array = [], + array_ended = false, + value = null; - if (token.expression) { - value = Twig.expression.parse.apply(this, [token.params, context]) + if (token.expression) { + value = Twig.expression.parse.apply(this, [token.params, context]) stack.push(value); - } else { + } else { - while (stack.length > 0) { + 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) {
    -                            array_ended = true;
    -                            break;
    +            
                            if (value && value.type && value.type == Twig.expression.type.parameter.start) {
    +                            array_ended = true;
    +                            break;
                             }
                             new_array.unshift(value);
                         }
     
    -                    if (!array_ended) {
    -                        throw new Twig.Error("Expected end of parameter set.");
    +                    if (!array_ended) {
    +                        throw new Twig.Error("Expected end of parameter set.");
                         }
     
                         stack.push(new_array);
    @@ -5560,77 +5605,77 @@ 

    twig.expression.js

    } }, { - /** + /** * Match an array start. */ type: Twig.expression.type.array.start, - regex: /^\[/, + regex: /^\[/, next: Twig.expression.set.expressions.concat([Twig.expression.type.array.end]), compile: Twig.expression.fn.compile.push_both, parse: Twig.expression.fn.parse.push }, { - /** + /** * Match an array end. */ type: Twig.expression.type.array.end, - regex: /^\]/, + regex: /^\]/, next: Twig.expression.set.operations_extended, - compile: function(token, stack, output) { - var i = stack.length - 1, + 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--) {
    +            
                    for(;i >= 0; i--) {
                         stack_token = stack.pop();
    -                    if (stack_token.type === Twig.expression.type.array.start) {
    -                        break;
    +                    if (stack_token.type === Twig.expression.type.array.start) {
    +                        break;
                         }
                         output.push(stack_token);
                     }
                     output.push(token);
                 },
    -            parse: function(token, stack, context) {
    -                var new_array = [],
    -                    array_ended = false,
    -                    value = null;
    +            parse: function(token, stack, context) {
    +                var new_array = [],
    +                    array_ended = false,
    +                    value = null;
     
    -                while (stack.length > 0) {
    +                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) {
    -                        array_ended = true;
    -                        break;
    +            
                        if (value.type && value.type == Twig.expression.type.array.start) {
    +                        array_ended = true;
    +                        break;
                         }
                         new_array.unshift(value);
                     }
    -                if (!array_ended) {
    -                    throw new Twig.Error("Expected end of array.");
    +                if (!array_ended) {
    +                    throw new Twig.Error("Expected end of array.");
                     }
     
                     stack.push(new_array);
    @@ -5640,22 +5685,22 @@ 

    twig.expression.js

  • -
  • +
  • - +
    -

    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 }

    + { "key": 'value', "another_key": item }

    Keys must be quoted (either single or double) and values can be any expression.

            {
                 type: Twig.expression.type.object.start,
    -            regex: /^\{/,
    +            regex: /^\{/,
                 next: Twig.expression.set.expressions.concat([Twig.expression.type.object.end]),
                 compile: Twig.expression.fn.compile.push_both,
                 parse: Twig.expression.fn.parse.push
    @@ -5664,13 +5709,13 @@ 

    twig.expression.js

  • -
  • +
  • - +
    -

    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.

    @@ -5678,109 +5723,109 @@

    twig.expression.js

            {
                 type: Twig.expression.type.object.end,
    -            regex: /^\}/,
    +            regex: /^\}/,
                 next: Twig.expression.set.operations_extended,
    -            compile: function(token, stack, output) {
    -                var i = stack.length-1,
    +            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--) {
    +            
                    for(;i >= 0; i--) {
                         stack_token = stack.pop();
    -                    if (stack_token && stack_token.type === Twig.expression.type.object.start) {
    -                        break;
    +                    if (stack_token && stack_token.type === Twig.expression.type.object.start) {
    +                        break;
                         }
                         output.push(stack_token);
                     }
                     output.push(token);
                 },
    -            parse: function(end_token, stack, context) {
    -                var new_object = {},
    -                    object_ended = false,
    -                    token = null,
    -                    token_key = null,
    -                    has_value = false,
    -                    value = null;
    -
    -                while (stack.length > 0) {
    +            parse: function(end_token, stack, context) {
    +                var new_object = {},
    +                    object_ended = false,
    +                    token = null,
    +                    token_key = null,
    +                    has_value = false,
    +                    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) {
    -                        object_ended = true;
    -                        break;
    +            
                        if (token && token.type && token.type === Twig.expression.type.object.start) {
    +                        object_ended = true;
    +                        break;
                         }
    -                    if (token && token.type && (token.type === Twig.expression.type.operator.binary || token.type === Twig.expression.type.operator.unary) && token.key) {
    -                        if (!has_value) {
    -                            throw new Twig.Error("Missing value for key '" + token.key + "' in object definition.");
    +                    if (token && token.type && (token.type === Twig.expression.type.operator.binary || token.type === Twig.expression.type.operator.unary) && token.key) {
    +                        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 -This is necessary since JavaScript objects don’t +This is necessary since JavaScript objects don't guarantee the order of keys

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

    reset value check

    -
                            value = null;
    -                        has_value = false;
    +            
                            value = null;
    +                        has_value = false;
     
    -                    } else {
    -                        has_value = true;
    +                    } else {
    +                        has_value = true;
                             value = token;
                         }
                     }
    -                if (!object_ended) {
    -                    throw new Twig.Error("Unexpected end of object.");
    +                if (!object_ended) {
    +                    throw new Twig.Error("Unexpected end of object.");
                     }
     
                     stack.push(new_object);
    @@ -5790,11 +5835,11 @@ 

    twig.expression.js

  • -
  • +
  • - +

    Token representing a filter

    Filters can follow any expression and take the form: @@ -5809,28 +5854,28 @@

    twig.expression.js

  • -
  • +
  • - +

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

    -
                regex: /^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/,
    +            
                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) {
    -                token.value = token.match[1];
    +            compile: function(token, stack, output) {
    +                token.value = token.match[1];
                     output.push(token);
                 },
    -            parse: function(token, stack, context) {
    -                var input = stack.pop(),
    -                    params = token.params && Twig.expression.parse.apply(this, [token.params, context]);
    +            parse: function(token, stack, context) {
    +                var input = stack.pop(),
    +                    params = token.params && Twig.expression.parse.apply(this, [token.params, context]);
     
    -                stack.push(Twig.filter.apply(this, [token.value, input, params]));
    +                stack.push(Twig.filter.apply(this, [token.value, input, params]));
                 }
             },
             {
    @@ -5839,75 +5884,75 @@ 

    twig.expression.js

  • -
  • +
  • - +

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

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

    cleanup token

    -
                    delete token.match;
    -                delete token.value;
    +            
                    delete token.match;
    +                delete token.value;
     
                     output.push(token);
                 },
    -            parse: function(token, stack, context) {
    -                var params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
    +            parse: function(token, stack, context) {
    +                var params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
                         fn     = token.fn,
                         value;
     
    -                if (Twig.functions[fn]) {
    + if (Twig.functions[fn]) {
  • -
  • +
  • - +

    Get the function from the built-in functions

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

    Get the function from the user/context defined functions

    @@ -5915,8 +5960,8 @@

    twig.expression.js

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

    twig.expression.js

  • -
  • +
  • - +

    Token representing a variable.

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

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

    @@ -5947,113 +5992,113 @@

    twig.expression.js

  • -
  • +
  • - +

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

    -
                regex: /^[a-zA-Z_][a-zA-Z0-9_]*/,
    +            
                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.indexOf(Twig.expression.reservedWords, match[0]) < 0);
    +            validate: function(match, tokens) {
    +                return (Twig.indexOf(Twig.expression.reservedWords, match[0]) < 0);
                 },
    -            parse: function(token, stack, context) {
    + parse: function(token, stack, context) {
  • -
  • +
  • - +

    Get the variable from the context

    -
                    var value = Twig.expression.resolve(context[token.value], context);
    +            
                    var value = Twig.expression.resolve(context[token.value], context);
                     stack.push(value);
                 }
             },
             {
                 type: Twig.expression.type.key.period,
    -            regex: /^\.([a-zA-Z0-9_]+)/,
    +            regex: /^\.([a-zA-Z0-9_]+)/,
                 next: Twig.expression.set.operations_extended.concat([
                         Twig.expression.type.parameter.start]),
    -            compile: function(token, stack, output) {
    -                token.key = token.match[1];
    -                delete token.match;
    -                delete token.value;
    +            compile: function(token, stack, output) {
    +                token.key = token.match[1];
    +                delete token.match;
    +                delete token.value;
     
                     output.push(token);
                 },
    -            parse: function(token, stack, context) {
    -                var params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
    +            parse: function(token, stack, context) {
    +                var params = token.params && Twig.expression.parse.apply(this, [token.params, context]),
                         key = token.key,
                         object = stack.pop(),
                         value;
     
    -                if (object === null || object === undefined) {
    -                    if (this.options.strict_variables) {
    -                        throw new Twig.Error("Can't access a key " + key + " on an null or undefined object.");
    -                    } else {
    -                        return null;
    +                if (object === null || object === undefined) {
    +                    if (this.options.strict_variables) {
    +                        throw new Twig.Error("Can't access a key " + key + " on an null or undefined object.");
    +                    } else {
    +                        return null;
                         }
                     }
     
    -                var capitalize = function(value) {return value.substr(0, 1).toUpperCase() + value.substr(1);};
    + 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) {
    +            
                    if (typeof object === 'object' && key in object) {
                         value = object[key];
    -                } else if (object["get"+capitalize(key)] !== undefined) {
    -                    value = object["get"+capitalize(key)];
    -                } else if (object["is"+capitalize(key)] !== undefined) {
    -                    value = object["is"+capitalize(key)];
    -                } else {
    -                    value = null;
    +                } else if (object["get"+capitalize(key)] !== undefined) {
    +                    value = object["get"+capitalize(key)];
    +                } else if (object["is"+capitalize(key)] !== undefined) {
    +                    value = object["is"+capitalize(key)];
    +                } else {
    +                    value = null;
                     }
                     stack.push(Twig.expression.resolve(value, object, params));
                 }
             },
             {
                 type: Twig.expression.type.key.brackets,
    -            regex: /^\[([^\]]*)\]/,
    +            regex: /^\[([^\]]*)\]/,
                 next: Twig.expression.set.operations_extended.concat([
                         Twig.expression.type.parameter.start]),
    -            compile: function(token, stack, output) {
    -                var match = token.match[1];
    -                delete token.value;
    -                delete token.match;
    + compile: function(token, stack, output) { + var match = token.match[1]; + delete token.value; + delete token.match;
  • -
  • +
  • - +

    The expression stack for the key

    @@ -6065,57 +6110,57 @@

    twig.expression.js

    output.push(token); }, - parse: function(token, stack, 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]),
    +            
                    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;
     
    -                if (object === null || object === undefined) {
    -                    if (this.options.strict_variables) {
    -                        throw new Twig.Error("Can't access a key " + key + " on an null or undefined object.");
    -                    } else {
    -                        return null;
    +                if (object === null || object === undefined) {
    +                    if (this.options.strict_variables) {
    +                        throw new Twig.Error("Can't access a key " + key + " on an null or undefined object.");
    +                    } else {
    +                        return null;
                         }
                     }
  • -
  • +
  • - +

    Get the variable from the context

    -
                    if (typeof object === 'object' && key in object) {
    +            
                    if (typeof object === 'object' && key in object) {
                         value = object[key];
    -                } else {
    -                    value = null;
    +                } else {
    +                    value = null;
                     }
                     stack.push(Twig.expression.resolve(value, object, params));
                 }
             },
             {
    -            /**
    +            /**
                  * Match a null value.
                  */
                 type: Twig.expression.type._null,
    @@ -6123,40 +6168,40 @@

    twig.expression.js

  • -
  • +
  • - +

    match a number

    -
                regex: /^null/,
    +            
                regex: /^null/,
                 next: Twig.expression.set.operations,
    -            compile: function(token, stack, output) {
    -                delete token.match;
    -                token.value = null;
    +            compile: function(token, stack, output) {
    +                delete token.match;
    +                token.value = null;
                     output.push(token);
                 },
                 parse: Twig.expression.fn.parse.push_value
             },
             {
    -            /**
    +            /**
                  * Match the context
                  */
                 type: Twig.expression.type.context,
    -            regex: /^_context/,
    +            regex: /^_context/,
                 next: Twig.expression.set.operations_extended.concat([
                         Twig.expression.type.parameter.start]),
                 compile: Twig.expression.fn.compile.push,
    -            parse: function(token, stack, context) {
    +            parse: function(token, stack, context) {
                     stack.push(context);
                 }
             },
             {
    -            /**
    +            /**
                  * Match a number (integer or decimal)
                  */
                 type: Twig.expression.type.number,
    @@ -6164,41 +6209,41 @@

    twig.expression.js

  • -
  • +
  • - +

    match a number

    -
                regex: /^\-?\d+(\.\d+)?/,
    +            
                regex: /^\-?\d+(\.\d+)?/,
                 next: Twig.expression.set.operations,
    -            compile: function(token, stack, output) {
    -                token.value = Number(token.value);
    +            compile: function(token, stack, output) {
    +                token.value = Number(token.value);
                     output.push(token);
                 },
                 parse: Twig.expression.fn.parse.push_value
             },
             {
    -            /**
    +            /**
                  * Match a boolean
                  */
                 type: Twig.expression.type.bool,
    -            regex: /^(true|false)/,
    +            regex: /^(true|false)/,
                 next: Twig.expression.set.operations,
    -            compile: function(token, stack, output) {
    -                token.value = (token.match[0] == "true");
    -                delete token.match;
    +            compile: function(token, stack, output) {
    +                token.value = (token.match[0] == "true");
    +                delete token.match;
                     output.push(token);
                 },
                 parse: Twig.expression.fn.parse.push_value
             }
         ];
     
    -    /**
    +    /**
          * Resolve a context value.
          *
          * If the value is a function, it is executed with a context parameter.
    @@ -6206,29 +6251,29 @@ 

    twig.expression.js

    * @param {string} key The context object key. * @param {Object} context The render context. */
    - Twig.expression.resolve = function(value, context, params) { - if (typeof value == 'function') { - return value.apply(context, params || []); - } else { - return value; + Twig.expression.resolve = function(value, context, params) { + if (typeof value == 'function') { + return value.apply(context, params || []); + } else { + return value; } }; - /** + /** * Registry for logic handlers. */ Twig.expression.handler = {}; - /** + /** * Define a new expression type, available at Twig.logic.type.{type} * * @param {string} type The name of the new type. */ - Twig.expression.extendType = function (type) { - Twig.expression.type[type] = "Twig.expression.type." + type; + Twig.expression.extendType = function (type) { + Twig.expression.type[type] = "Twig.expression.type." + type; }; - /** + /** * Extend the expression parsing functionality with a new definition. * * Token definitions follow this format: @@ -6249,9 +6294,9 @@

    twig.expression.js

    * * @param {Object} definition A token definition. */
    - Twig.expression.extend = function (definition) { - if (!definition.type) { - throw new Twig.Error("Unable to extend logic definition. No type provided for " + definition); + Twig.expression.extend = function (definition) { + if (!definition.type) { + throw new Twig.Error("Unable to extend logic definition. No type provided for " + definition); } Twig.expression.handler[definition.type] = definition; };
    @@ -6259,68 +6304,68 @@

    twig.expression.js

  • -
  • +
  • - +

    Extend with built-in expressions

    -
        while (Twig.expression.definitions.length > 0) {
    +            
        while (Twig.expression.definitions.length > 0) {
             Twig.expression.extend(Twig.expression.definitions.shift());
         }
     
    -    /**
    +    /**
          * Break an expression into tokens defined in Twig.expression.definitions.
          *
          * @param {string} expression The string to tokenize.
          *
          * @return {Array} An array of tokens.
          */
    -    Twig.expression.tokenize = function (expression) {
    -        var tokens = [],
    + Twig.expression.tokenize = function (expression) { + var tokens = [],
  • -
  • +
  • - +

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

    -
                exp_offset = 0,
    +
                exp_offset = 0,
  • -
  • +
  • - +

    The valid next tokens of the previous token

    -
                next = null,
    +
                next = null,
  • -
  • +
  • - +

    Match information

    @@ -6331,11 +6376,11 @@

    twig.expression.js

  • -
  • +
  • - +

    The possible next token for the match

    @@ -6346,11 +6391,11 @@

    twig.expression.js

  • -
  • +
  • - +

    Has a match been found from the definitions

    @@ -6358,118 +6403,118 @@

    twig.expression.js

                match_found, invalid_matches = [], match_function;
     
    -        match_function = function () {
    -            var match = Array.prototype.slice.apply(arguments),
    +        match_function = function () {
    +            var match = Array.prototype.slice.apply(arguments),
                     string = match.pop(),
                     offset = match.pop();
     
    -            Twig.log.trace("Twig.expression.tokenize",
    -                           "Matched a ", type, " regular expression of ", match);
    +            Twig.log.trace("Twig.expression.tokenize",
    +                           "Matched a ", type, " regular expression of ", match);
     
    -            if (next && Twig.indexOf(next, type) < 0) {
    +            if (next && Twig.indexOf(next, type) < 0) {
                     invalid_matches.push(
    -                    type + " cannot follow a " + tokens[tokens.length - 1].type +
    -                           " at template:" + exp_offset + " near '" + match[0].substring(0, 20) +
    -                           "...'"
    +                    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

    +

    Not a match, don't change the expression

    -
                    return match[0];
    +            
                    return match[0];
                 }
  • -
  • +
  • - +

    Validate the token if a validation function is provided

    -
                if (Twig.expression.handler[type].validate &&
    +            
                if (Twig.expression.handler[type].validate &&
                         !Twig.expression.handler[type].validate(match, tokens)) {
    -                return match[0];
    +                return match[0];
                 }
     
                 invalid_matches = [];
     
                 tokens.push({
                     type:  type,
    -                value: match[0],
    +                value: match[0],
                     match: match
                 });
     
    -            match_found = true;
    +            match_found = true;
                 next = token_next;
    -            exp_offset += match[0].length;
    + 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 +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);
    +            
                if (Twig.expression.handler[type].transform) {
    +                return Twig.expression.handler[type].transform(match, tokens);
                 }
    -            return '';
    +            return '';
             };
     
    -        Twig.log.debug("Twig.expression.tokenize", "Tokenizing expression ", expression);
    +        Twig.log.debug("Twig.expression.tokenize", "Tokenizing expression ", expression);
     
    -        while (expression.length > 0) {
    +        while (expression.length > 0) {
                 expression = expression.trim();
    -            for (type in Twig.expression.handler) {
    -                if (Twig.expression.handler.hasOwnProperty(type)) {
    +            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);

    +

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

    -
                        if (regex instanceof Array) {
    +            
                        if (regex instanceof Array) {
                             regex_array = regex;
    -                    } else {
    +                    } else {
                             regex_array = [regex];
                         }
     
    -                    match_found = false;
    -                    while (regex_array.length > 0) {
    +                    match_found = false;
    +                    while (regex_array.length > 0) {
                             regex = regex_array.pop();
                             expression = expression.replace(regex, match_function);
                         }
    @@ -6477,95 +6522,94 @@

    twig.expression.js

  • -
  • +
  • - +

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

    + match the next template (if expression isn't empty.)

    -
                        if (match_found) {
    -                        break;
    +            
                        if (match_found) {
    +                        break;
                         }
                     }
                 }
    -            if (!match_found) {
    -                if (invalid_matches.length > 0) {
    -                    throw new Twig.Error(invalid_matches.join(" OR "));
    -                } else {
    -                    throw new Twig.Error("Unable to parse '" + expression + "' at template position" + exp_offset);
    +            if (!match_found) {
    +                if (invalid_matches.length > 0) {
    +                    throw new Twig.Error(invalid_matches.join(" OR "));
    +                } else {
    +                    throw new Twig.Error("Unable to parse '" + expression + "' at template position" + exp_offset);
                     }
                 }
             }
     
    -        Twig.log.trace("Twig.expression.tokenize", "Tokenized to ", tokens);
    -        return tokens;
    +        Twig.log.trace("Twig.expression.tokenize", "Tokenized to ", tokens);
    +        return tokens;
         };
     
    -    /**
    +    /**
          * Compile an expression token.
          *
          * @param {Object} raw_token The uncompiled token.
          *
          * @return {Object} The compiled token.
          */
    -    Twig.expression.compile = function (raw_token) {
    -        var expression = raw_token.value,
    + Twig.expression.compile = function (raw_token) { + var expression = raw_token.value,
  • -
  • +
  • - +

    Tokenize expression

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

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

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

    Compile the template

    @@ -6573,24 +6617,24 @@

    twig.expression.js

                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);
    +            Twig.log.trace("Twig.expression.compile: ", "Stack is", stack);
    +            Twig.log.trace("Twig.expression.compile: ", "Output is", output);
             }
     
    -        while(stack.length > 0) {
    +        while(stack.length > 0) {
                 output.push(stack.pop());
             }
     
    -        Twig.log.trace("Twig.expression.compile: ", "Final output is", output);
    +        Twig.log.trace("Twig.expression.compile: ", "Final output is", output);
     
             raw_token.stack = output;
    -        delete raw_token.value;
    +        delete raw_token.value;
     
    -        return raw_token;
    +        return raw_token;
         };
     
     
    -    /**
    +    /**
          * Parse an RPN expression stack within a context.
          *
          * @param {Array} tokens An array of compiled expression tokens.
    @@ -6600,43 +6644,43 @@ 

    twig.expression.js

    * can be anything, String, Array, Object, etc... based on * the given expression. */
    - Twig.expression.parse = function (tokens, context) { - var that = this;
    + Twig.expression.parse = function (tokens, context) { + var that = this;
  • -
  • +
  • - +
    -

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

    +

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

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

    The output stack

    -
            var stack = [],
    -            token_template = null;
    +            
            var stack = [],
    +            token_template = null;
     
    -        Twig.forEach(tokens, function (token) {
    +        Twig.forEach(tokens, function (token) {
                 token_template = Twig.expression.handler[token.type];
     
                 token_template.parse && token_template.parse.apply(that, [token, stack, context]);
    @@ -6645,410 +6689,386 @@ 

    twig.expression.js

  • -
  • +
  • - +

    Pop the final value off the stack

    -
            return stack.pop();
    +            
            return stack.pop();
         };
     
    -    return Twig;
    +    return Twig;
     
     })( 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

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.expression.operator.js

    This file handles operator lookups and parsing.

    -
    var Twig = (function (Twig) {
    -    "use strict";
    +            
    var Twig = (function (Twig) {
    +    "use strict";
     
    -    /**
    +    /**
          * Operator associativity constants.
          */
         Twig.expression.operator = {
    -        leftToRight: 'leftToRight',
    -        rightToLeft: 'rightToLeft'
    +        leftToRight: 'leftToRight',
    +        rightToLeft: 'rightToLeft'
         };
     
    -    var containment = function(a, b) {
    -        if (b.indexOf !== undefined) {
    + var containment = function(a, b) { + if (b.indexOf !== undefined) {
  • -
  • +
  • - +

    String

    -
                return a === b || a !== '' && b.indexOf(a) > -1;
    +            
                return a === b || a !== '' && b.indexOf(a) > -1;
     
    -        } else {
    -            var el;
    -            for (el in b) {
    -                if (b.hasOwnProperty(el) && b[el] === a) {
    -                    return true;
    +        } else {
    +            var el;
    +            for (el in b) {
    +                if (b.hasOwnProperty(el) && b[el] === a) {
    +                    return true;
                     }
                 }
    -            return false;
    +            return false;
             }
         };
     
    -    /**
    +    /**
          * Get the precidence and associativity of an operator. These follow the order that C/C++ use.
          * See http://en.wikipedia.org/wiki/Operators_in_C_and_C++ for the table of values.
          */
    -    Twig.expression.operator.lookup = function (operator, token) {
    -        switch (operator) {
    -            case "..":
    -            case 'not in':
    -            case 'in':
    -                token.precidence = 20;
    +    Twig.expression.operator.lookup = function (operator, token) {
    +        switch (operator) {
    +            case "..":
    +            case 'not in':
    +            case 'in':
    +                token.precidence = 20;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    +                break;
     
    -            case ',':
    -                token.precidence = 18;
    +            case ',':
    +                token.precidence = 18;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    + break;
  • -
  • +
  • - +

    Ternary

    -
                case '?':
    -            case ':':
    -                token.precidence = 16;
    +            
                case '?':
    +            case ':':
    +                token.precidence = 16;
                     token.associativity = Twig.expression.operator.rightToLeft;
    -                break;
    +                break;
     
    -            case 'or':
    -                token.precidence = 14;
    +            case 'or':
    +                token.precidence = 14;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    +                break;
     
    -            case 'and':
    -                token.precidence = 13;
    +            case 'and':
    +                token.precidence = 13;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    +                break;
     
    -            case '==':
    -            case '!=':
    -                token.precidence = 9;
    +            case '==':
    +            case '!=':
    +                token.precidence = 9;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    +                break;
     
    -            case '<':
    -            case '<=':
    -            case '>':
    -            case '>=':
    -                token.precidence = 8;
    +            case '<':
    +            case '<=':
    +            case '>':
    +            case '>=':
    +                token.precidence = 8;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    +                break;
     
     
    -            case '~': // String concatination
    -            case '+':
    -            case '-':
    -                token.precidence = 6;
    +            case '~': // String concatination
    +            case '+':
    +            case '-':
    +                token.precidence = 6;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    -
    -            case '//':
    -            case '**':
    -            case '*':
    -            case '/':
    -            case '%':
    -                token.precidence = 5;
    +                break;
    +
    +            case '//':
    +            case '**':
    +            case '*':
    +            case '/':
    +            case '%':
    +                token.precidence = 5;
                     token.associativity = Twig.expression.operator.leftToRight;
    -                break;
    +                break;
     
    -            case 'not':
    -                token.precidence = 3;
    +            case 'not':
    +                token.precidence = 3;
                     token.associativity = Twig.expression.operator.rightToLeft;
    -                break;
    +                break;
     
    -            default:
    -                throw new Twig.Error(operator + " is an unknown operator.");
    +            default:
    +                throw new Twig.Error(operator + " is an unknown operator.");
             }
             token.operator = operator;
    -        return token;
    +        return token;
         };
     
    -    /**
    +    /**
          * Handle operations on the RPN stack.
          *
          * Returns the updated stack.
          */
    -    Twig.expression.operator.parse = function (operator, stack) {
    -        Twig.log.trace("Twig.expression.operator.parse: ", "Handling ", operator);
    -        var a, b, c;
    -        switch (operator) {
    -            case ':':
    + Twig.expression.operator.parse = function (operator, stack) { + Twig.log.trace("Twig.expression.operator.parse: ", "Handling ", operator); + var a, b, c; + switch (operator) { + case ':':
  • -
  • +
  • - +

    Ignore

    -
                    break;
    +            
                    break;
     
    -            case '?':
    -                c = stack.pop(); // false expr
    -                b = stack.pop(); // true expr
    -                a = stack.pop(); // conditional
    -                if (a) {
    +            case '?':
    +                c = stack.pop(); // false expr
    +                b = stack.pop(); // true expr
    +                a = stack.pop(); // conditional
    +                if (a) {
                         stack.push(b);
    -                } else {
    +                } else {
                         stack.push(c);
                     }
    -                break;
    +                break;
     
    -            case '+':
    -                b = parseFloat(stack.pop());
    -                a = parseFloat(stack.pop());
    +            case '+':
    +                b = parseFloat(stack.pop());
    +                a = parseFloat(stack.pop());
                     stack.push(a + b);
    -                break;
    +                break;
     
    -            case '-':
    -                b = parseFloat(stack.pop());
    -                a = parseFloat(stack.pop());
    +            case '-':
    +                b = parseFloat(stack.pop());
    +                a = parseFloat(stack.pop());
                     stack.push(a - b);
    -                break;
    +                break;
     
    -            case '*':
    -                b = parseFloat(stack.pop());
    -                a = parseFloat(stack.pop());
    +            case '*':
    +                b = parseFloat(stack.pop());
    +                a = parseFloat(stack.pop());
                     stack.push(a * b);
    -                break;
    +                break;
     
    -            case '/':
    -                b = parseFloat(stack.pop());
    -                a = parseFloat(stack.pop());
    +            case '/':
    +                b = parseFloat(stack.pop());
    +                a = parseFloat(stack.pop());
                     stack.push(a / b);
    -                break;
    +                break;
     
    -            case '//':
    -                b = parseFloat(stack.pop());
    -                a = parseFloat(stack.pop());
    -                stack.push(parseInt(a / b));
    -                break;
    +            case '//':
    +                b = parseFloat(stack.pop());
    +                a = parseFloat(stack.pop());
    +                stack.push(parseInt(a / b));
    +                break;
     
    -            case '%':
    -                b = parseFloat(stack.pop());
    -                a = parseFloat(stack.pop());
    +            case '%':
    +                b = parseFloat(stack.pop());
    +                a = parseFloat(stack.pop());
                     stack.push(a % b);
    -                break;
    +                break;
     
    -            case '~':
    +            case '~':
                     b = stack.pop();
                     a = stack.pop();
    -                stack.push( (a !== undefined ? a.toString() : "")
    -                          + (b !== undefined ? b.toString() : "") );
    -                break;
    +                stack.push( (a !== undefined ? a.toString() : "")
    +                          + (b !== undefined ? b.toString() : "") );
    +                break;
     
    -            case 'not':
    -            case '!':
    +            case 'not':
    +            case '!':
                     stack.push(!stack.pop());
    -                break;
    +                break;
     
    -            case '<':
    +            case '<':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a < b);
    -                break;
    +                break;
     
    -            case '<=':
    +            case '<=':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a <= b);
    -                break;
    +                break;
     
    -            case '>':
    +            case '>':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a > b);
    -                break;
    +                break;
     
    -            case '>=':
    +            case '>=':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a >= b);
    -                break;
    +                break;
     
    -            case '===':
    +            case '===':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a === b);
    -                break;
    +                break;
     
    -            case '==':
    +            case '==':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a == b);
    -                break;
    +                break;
     
    -            case '!==':
    +            case '!==':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a !== b);
    -                break;
    +                break;
     
    -            case '!=':
    +            case '!=':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a != b);
    -                break;
    +                break;
     
    -            case 'or':
    +            case 'or':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a || b);
    -                break;
    +                break;
     
    -            case 'and':
    +            case 'and':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push(a && b);
    -                break;
    +                break;
     
    -            case '**':
    +            case '**':
                     b = stack.pop();
                     a = stack.pop();
    -                stack.push(Math.pow(a, b));
    -                break;
    +                stack.push(Math.pow(a, b));
    +                break;
     
     
    -            case 'not in':
    +            case 'not in':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push( !containment(a, b) );
    -                break;
    +                break;
     
    -            case 'in':
    +            case 'in':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push( containment(a, b) );
    -                break;
    +                break;
     
    -            case '..':
    +            case '..':
                     b = stack.pop();
                     a = stack.pop();
                     stack.push( Twig.functions.range(a, b) );
    -                break;
    +                break;
     
    -            default:
    -                throw new Twig.Error(operator + " is an unknown operator.");
    +            default:
    +                throw new Twig.Error(operator + " is an unknown operator.");
             }
         };
     
    -    return Twig;
    +    return Twig;
     
     })( 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

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.filters.js

    This file handles parsing filters.

    -
    var Twig = (function (Twig) {
    +
    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;
    +            
        function is(type, obj) {
    +        var clas = Object.prototype.toString.call(obj).slice(8, -1);
    +        return obj !== undefined && obj !== null && clas === type;
         }
     
         Twig.filters = {
    @@ -7056,260 +7076,259 @@

    twig.filters.js

  • -
  • +
  • - +

    String Filters

    -
            upper:  function(value) {
    -            if ( typeof value !== "string" ) {
    -               return value;
    +            
            upper:  function(value) {
    +            if ( typeof value !== "string" ) {
    +               return value;
                 }
     
    -            return value.toUpperCase();
    +            return value.toUpperCase();
             },
    -        lower: function(value) {
    -            if ( typeof value !== "string" ) {
    -               return value;
    +        lower: function(value) {
    +            if ( typeof value !== "string" ) {
    +               return value;
                 }
     
    -            return value.toLowerCase();
    +            return value.toLowerCase();
             },
    -        capitalize: function(value) {
    -            if ( typeof value !== "string" ) {
    -                 return value;
    +        capitalize: function(value) {
    +            if ( typeof value !== "string" ) {
    +                 return value;
                 }
     
    -            return value.substr(0, 1).toUpperCase() + value.toLowerCase().substr(1);
    +            return value.substr(0, 1).toUpperCase() + value.toLowerCase().substr(1);
             },
    -        title: function(value) {
    -            if ( typeof value !== "string" ) {
    -               return value;
    +        title: function(value) {
    +            if ( typeof value !== "string" ) {
    +               return value;
                 }
     
    -            return value.toLowerCase().replace( /(^|\s)([a-z])/g , function(m, p1, p2){
    -                return p1 + p2.toUpperCase();
    +            return value.toLowerCase().replace( /(^|\s)([a-z])/g , function(m, p1, p2){
    +                return p1 + p2.toUpperCase();
                 });
             },
    -        length: function(value) {
    -            if (value instanceof Array || typeof value === "string") {
    -                return value.length;
    -            } else if (value instanceof Object) {
    -                if (value._keys === undefined) {
    -                    return Object.keys(value).length;
    -                } else {
    -                    return value._keys.length;
    +        length: function(value) {
    +            if (Twig.lib.is("Array", value) || typeof value === "string") {
    +                return value.length;
    +            } else if (Twig.lib.is("Object", value)) {
    +                if (value._keys === undefined) {
    +                    return Object.keys(value).length;
    +                } else {
    +                    return value._keys.length;
                     }
    -            } else {
    -                return 0;
    +            } else {
    +                return 0;
                 }
             },
  • -
  • +
  • - +

    Array/Object Filters

    -
            reverse: function(value) {
    -            if (is("Array", value)) {
    -                return value.reverse();
    -            } else if (is("String", value)) {
    -                return value.split("").reverse().join("");
    -            } else if (value instanceof Object) {
    -                var keys = value._keys || Object.keys(value).reverse();
    +            
            reverse: function(value) {
    +            if (is("Array", value)) {
    +                return value.reverse();
    +            } else if (is("String", value)) {
    +                return value.split("").reverse().join("");
    +            } else if (value instanceof Object) {
    +                var keys = value._keys || Object.keys(value).reverse();
                     value._keys = keys;
    -                return value;
    +                return value;
                 }
             },
    -        sort: function(value) {
    -            if (is("Array", value)) {
    -                return value.sort();
    -            } else if (value instanceof Object) {
    + sort: function(value) { + if (is("Array", value)) { + return value.sort(); + } 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 +

    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;
    -                var keys = Object.keys(value),
    -                    sorted_keys = keys.sort(function(a, b) {
    -                        return value[a] > value[b];
    +            
                    delete value._keys;
    +                var keys = Object.keys(value),
    +                    sorted_keys = keys.sort(function(a, b) {
    +                        return value[a] > value[b];
                         });
                     value._keys = sorted_keys;
    -                return value;
    +                return value;
                 }
             },
    -        keys: function(value) {
    -            if (value === undefined || value === null){
    -                return;
    +        keys: function(value) {
    +            if (value === undefined || value === null){
    +                return;
                }
     
    -            var keyset = value._keys || Object.keys(value),
    +            var keyset = value._keys || Object.keys(value),
                     output = [];
     
    -            Twig.forEach(keyset, function(key) {
    -                if (key === "_keys") return; // Ignore the _keys property
    -                if (value.hasOwnProperty(key)) {
    +            Twig.forEach(keyset, function(key) {
    +                if (key === "_keys") return; // Ignore the _keys property
    +                if (value.hasOwnProperty(key)) {
                         output.push(key);
                     }
                 });
    -            return output;
    +            return output;
             },
    -        url_encode: function(value) {
    -            if (value === undefined || value === null){
    -                return;
    +        url_encode: function(value) {
    +            if (value === undefined || value === null){
    +                return;
                 }
     
    -            return encodeURIComponent(value);
    +            return encodeURIComponent(value);
             },
    -        join: function(value, params) {
    -            if (value === undefined || value === null){
    -                return;
    +        join: function(value, params) {
    +            if (value === undefined || value === null){
    +                return;
                 }
     
    -            var join_str = "",
    +            var join_str = "",
                     output = [],
    -                keyset = null;
    +                keyset = null;
     
    -            if (params && params[0]) {
    -                join_str = params[0];
    +            if (params && params[0]) {
    +                join_str = params[0];
                 }
    -            if (value instanceof Array) {
    +            if (value instanceof Array) {
                     output = value;
    -            } else {
    -                keyset = value._keys || Object.keys(value);
    -                Twig.forEach(keyset, function(key) {
    -                    if (key === "_keys") return; // Ignore the _keys property
    -                    if (value.hasOwnProperty(key)) {
    +            } else {
    +                keyset = value._keys || Object.keys(value);
    +                Twig.forEach(keyset, function(key) {
    +                    if (key === "_keys") return; // Ignore the _keys property
    +                    if (value.hasOwnProperty(key)) {
                             output.push(value[key]);
                         }
                     });
                 }
    -            return output.join(join_str);
    +            return output.join(join_str);
             },
    -        "default": function(value, params) {
    -            if (params === undefined || params.length !== 1) {
    -                throw new Twig.Error("default filter expects one argument");
    +        "default": function(value, params) {
    +            if (params === undefined || params.length !== 1) {
    +                throw new Twig.Error("default filter expects one argument");
                 }
    -            if (value === undefined || value === null || value === '' ) {
    -                return params[0];
    -            } else {
    -                return value;
    +            if (value === undefined || value === null || value === '' ) {
    +                return params[0];
    +            } else {
    +                return value;
                 }
             },
    -        json_encode: function(value) {
    -            if (value && value.hasOwnProperty( "_keys" ) ) {
    -                delete value._keys;
    +        json_encode: function(value) {
    +            if (value && value.hasOwnProperty( "_keys" ) ) {
    +                delete value._keys;
                 }
    -            if(value === undefined || value === null) {
    -                return "null";
    +            if(value === undefined || value === null) {
    +                return "null";
                 }
    -            return JSON.stringify(value);
    +            return JSON.stringify(value);
             },
    -        merge: function(value, params) {
    -            var obj = [],
    -                arr_index = 0,
    +        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)) {
    +
                if (!(value instanceof Array)) {
  • -
  • +
  • - +

    Create obj as an Object

                    obj = { };
    -            } else {
    -                Twig.forEach(params, function(param) {
    -                    if (!(param instanceof Array)) {
    +            } else {
    +                Twig.forEach(params, function(param) {
    +                    if (!(param instanceof Array)) {
                             obj = { };
                         }
                     });
                 }
    -            if (!(obj instanceof Array)) {
    +            if (!(obj instanceof Array)) {
                     obj._keys = [];
                 }
     
    -            if (value instanceof Array) {
    -                Twig.forEach(value, function(val) {
    -                    if (obj._keys) obj._keys.push(arr_index);
    +            if (value instanceof Array) {
    +                Twig.forEach(value, function(val) {
    +                    if (obj._keys) obj._keys.push(arr_index);
                         obj[arr_index] = val;
                         arr_index++;
                     });
    -            } else {
    -                keyset = value._keys || Object.keys(value);
    -                Twig.forEach(keyset, function(key) {
    +            } else {
    +                keyset = value._keys || Object.keys(value);
    +                Twig.forEach(keyset, function(key) {
                         obj[key] = value[key];
                         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.

    -

    Example {{ [“a”, “b”]|merge({“4”:”value”}, [“c”, “d”]) -Without this, d would have an index of “4” and overwrite the value - of “value”

    +

    Example {{ ["a", "b"]|merge({"4":"value"}, ["c", "d"]) +Without this, d would have an index of "4" and overwrite the value + of "value"

    -
                        var int_key = parseInt(key, 10);
    -                    if (!isNaN(int_key) && int_key >= arr_index) {
    -                        arr_index = int_key + 1;
    +            
                        var int_key = parseInt(key, 10);
    +                    if (!isNaN(int_key) && int_key >= arr_index) {
    +                        arr_index = int_key + 1;
                         }
                     });
                 }
    @@ -7317,488 +7336,528 @@

    twig.filters.js

  • -
  • +
  • - +

    mixin the merge arrays

    -
                Twig.forEach(params, function(param) {
    -                if (param instanceof Array) {
    -                    Twig.forEach(param, function(val) {
    -                        if (obj._keys) obj._keys.push(arr_index);
    +            
                Twig.forEach(params, function(param) {
    +                if (param instanceof Array) {
    +                    Twig.forEach(param, function(val) {
    +                        if (obj._keys) obj._keys.push(arr_index);
                             obj[arr_index] = val;
                             arr_index++;
                         });
    -                } else {
    -                    keyset = param._keys || Object.keys(param);
    -                    Twig.forEach(keyset, function(key) {
    -                        if (!obj[key]) obj._keys.push(key);
    +                } else {
    +                    keyset = param._keys || Object.keys(param);
    +                    Twig.forEach(keyset, function(key) {
    +                        if (!obj[key]) obj._keys.push(key);
                             obj[key] = param[key];
     
    -                        var int_key = parseInt(key, 10);
    -                        if (!isNaN(int_key) && int_key >= arr_index) {
    -                            arr_index = int_key + 1;
    +                        var int_key = parseInt(key, 10);
    +                        if (!isNaN(int_key) && int_key >= arr_index) {
    +                            arr_index = int_key + 1;
                             }
                         });
                     }
                 });
    -            if (params.length === 0) {
    -                throw new Twig.Error("Filter merge expects at least one parameter");
    +            if (params.length === 0) {
    +                throw new Twig.Error("Filter merge expects at least one parameter");
                 }
     
    -            return obj;
    +            return obj;
             },
    -        date: function(value, params) {
    -            if (value === undefined||value === null){
    -                return;
    +        date: function(value, params) {
    +            if (value === undefined||value === null){
    +                return;
                 }
     
    -            var date = Twig.functions.date(value);
    -            return Twig.lib.formatDate(date, params[0]);
    +            var date = Twig.functions.date(value);
    +            return Twig.lib.formatDate(date, params[0]);
             },
     
    -        date_modify: function(value, params) {
    -            if (value === undefined || value === null) {
    -                return;
    +        date_modify: function(value, params) {
    +            if (value === undefined || value === null) {
    +                return;
                 }
    -            if (params === undefined || params.length !== 1) {
    -                throw new Twig.Error("date_modify filter expects 1 argument");
    +            if (params === undefined || params.length !== 1) {
    +                throw new Twig.Error("date_modify filter expects 1 argument");
                 }
     
    -            var modifyText = params[0], time;
    +            var modifyText = params[0], time;
     
    -            if (Twig.lib.is("Date", value)) {
    -                time = Twig.lib.strtotime(modifyText, value.getTime() / 1000);
    +            if (Twig.lib.is("Date", value)) {
    +                time = Twig.lib.strtotime(modifyText, value.getTime() / 1000);
                 }
    -            if (Twig.lib.is("String", value)) {
    +            if (Twig.lib.is("String", value)) {
                     time = Twig.lib.strtotime(modifyText, Twig.lib.strtotime(value));
                 }
    -            if (Twig.lib.is("Number", value)) {
    +            if (Twig.lib.is("Number", value)) {
                     time = Twig.lib.strtotime(modifyText, value);
                 }
     
    -            return new Date(time * 1000);
    +            return new Date(time * 1000);
             },
     
    -        replace: function(value, params) {
    -            if (value === undefined||value === null){
    -                return;
    +        replace: function(value, params) {
    +            if (value === undefined||value === null){
    +                return;
                 }
     
    -            var pairs = params[0],
    +            var pairs = params[0],
                     tag;
    -            for (tag in pairs) {
    -                if (pairs.hasOwnProperty(tag) && tag !== "_keys") {
    +            for (tag in pairs) {
    +                if (pairs.hasOwnProperty(tag) && tag !== "_keys") {
                         value = Twig.lib.replaceAll(value, tag, pairs[tag]);
                     }
                 }
    -            return value;
    +            return value;
             },
     
    -        format: function(value, params) {
    -            if (value === undefined || value === null){
    -                return;
    +        format: function(value, params) {
    +            if (value === undefined || value === null){
    +                return;
                 }
     
    -            return Twig.lib.vsprintf(value, params);
    +            return Twig.lib.vsprintf(value, params);
             },
     
    -        striptags: function(value) {
    -            if (value === undefined || value === null){
    -                return;
    +        striptags: function(value) {
    +            if (value === undefined || value === null){
    +                return;
                 }
     
    -            return Twig.lib.strip_tags(value);
    +            return Twig.lib.strip_tags(value);
             },
     
    -        escape: function(value) {
    -            if (value === undefined|| value === null){
    -                return;
    +        escape: function(value) {
    +            if (value === undefined|| value === null){
    +                return;
                 }
    -            return value.toString().replace(/&/g, "&amp;")
    -                        .replace(/</g, "&lt;")
    -                        .replace(/>/g, "&gt;")
    -                        .replace(/"/g, "&quot;")
    -                        .replace(/'/g, "&#039;");
    +            return value.toString().replace(/&/g, "&amp;")
    +                        .replace(/</g, "&lt;")
    +                        .replace(/>/g, "&gt;")
    +                        .replace(/"/g, "&quot;")
    +                        .replace(/'/g, "&#039;");
             },
     
    -        /* Alias of escape */
    -        "e": function(value) {
    -            return Twig.filters.escape(value);
    +        /* Alias of escape */
    +        "e": function(value) {
    +            return Twig.filters.escape(value);
             },
     
    -        nl2br: function(value) {
    -            if (value === undefined || value === null){
    -                return;
    +        nl2br: function(value) {
    +            if (value === undefined || value === null){
    +                return;
                 }
    -            var linebreak_tag = "BACKSLASH_n_replace",
    -                br = "<br />" + linebreak_tag;
    +            var linebreak_tag = "BACKSLASH_n_replace",
    +                br = "<br />" + linebreak_tag;
     
                 value = Twig.filters.escape(value)
    -                        .replace(/\r\n/g, br)
    -                        .replace(/\r/g, br)
    -                        .replace(/\n/g, br);
    +                        .replace(/\r\n/g, br)
    +                        .replace(/\r/g, br)
    +                        .replace(/\n/g, br);
     
    -            return Twig.lib.replaceAll(value, linebreak_tag, "\n");
    +            return Twig.lib.replaceAll(value, linebreak_tag, "\n");
             },
     
    -        /**
    +        /**
              * Adapted from: http://phpjs.org/functions/number_format:481
              */
    -        number_format: function(value, params) {
    -            var number = value,
    -                decimals = (params && params[0]) ? params[0] : undefined,
    -                dec      = (params && params[1] !== undefined) ? params[1] : ".",
    -                sep      = (params && params[2] !== undefined) ? params[2] : ",";
    -
    -            number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
    -            var n = !isFinite(+number) ? 0 : +number,
    -                prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
    -                s = '',
    -                toFixedFix = function (n, prec) {
    -                    var k = Math.pow(10, prec);
    -                    return '' + Math.round(n * k) / k;
    +        number_format: function(value, params) {
    +            var number = value,
    +                decimals = (params && params[0]) ? params[0] : undefined,
    +                dec      = (params && params[1] !== undefined) ? params[1] : ".",
    +                sep      = (params && params[2] !== undefined) ? params[2] : ",";
    +
    +            number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
    +            var n = !isFinite(+number) ? 0 : +number,
    +                prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
    +                s = '',
    +                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('.');
    -            if (s[0].length > 3) {
    -                s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
    +            
                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);
                 }
    -            if ((s[1] || '').length < prec) {
    -                s[1] = s[1] || '';
    -                s[1] += new Array(prec - s[1].length + 1).join('0');
    +            if ((s[1] || '').length < prec) {
    +                s[1] = s[1] || '';
    +                s[1] += new Array(prec - s[1].length + 1).join('0');
                 }
    -            return s.join(dec);
    +            return s.join(dec);
             },
     
    -        trim: function(value, params) {
    -            if (value === undefined|| value === null){
    -                return;
    +        trim: function(value, params) {
    +            if (value === undefined|| value === null){
    +                return;
                 }
     
    -            var str = Twig.filters.escape( '' + value ),
    +            var str = Twig.filters.escape( '' + value ),
                     whitespace;
    -            if ( params && params[0] ) {
    -                whitespace = '' + params[0];
    -            } else {
    -                whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
    +            if ( params && params[0] ) {
    +                whitespace = '' + params[0];
    +            } else {
    +                whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
                 }
    -            for (var i = 0; i < str.length; i++) {
    -                if (whitespace.indexOf(str.charAt(i)) === -1) {
    +            for (var i = 0; i < str.length; i++) {
    +                if (whitespace.indexOf(str.charAt(i)) === -1) {
                         str = str.substring(i);
    -                    break;
    +                    break;
                     }
                 }
    -            for (i = str.length - 1; i >= 0; i--) {
    -                if (whitespace.indexOf(str.charAt(i)) === -1) {
    -                    str = str.substring(0, i + 1);
    -                    break;
    +            for (i = str.length - 1; i >= 0; i--) {
    +                if (whitespace.indexOf(str.charAt(i)) === -1) {
    +                    str = str.substring(0, i + 1);
    +                    break;
                     }
                 }
    -            return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
    +            return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
             },
     
    -        slice: function(value, params) {
    -            if (value === undefined || value === null) {
    -                return;
    +        slice: function(value, params) {
    +            if (value === undefined || value === null) {
    +                return;
                 }
    -            if (params === undefined || params.length < 1) {
    -                throw new Twig.Error("slice filter expects at least 1 argument");
    +            if (params === undefined || params.length < 1) {
    +                throw new Twig.Error("slice filter expects at least 1 argument");
                 }
  • -
  • +
  • - +

    default to start of string

    -
                var start = params[0] || 0;
    +
                var start = params[0] || 0;
  • -
  • +
  • - +

    default to length of string

    -
                var length = params.length > 1 ? params[1] : value.length;
    +
                var length = params.length > 1 ? params[1] : value.length;
  • -
  • +
  • - +

    handle negative start values

    -
                var startIndex = start >= 0 ? start : Math.max( value.length + start, 0 );
    +            
                var startIndex = start >= 0 ? start : Math.max( value.length + start, 0 );
     
    -            if (Twig.lib.is("Array", value)) {
    -                var output = [];
    -                for (var i = startIndex; i < startIndex + length && i < value.length; i++) {
    +            if (Twig.lib.is("Array", value)) {
    +                var output = [];
    +                for (var i = startIndex; i < startIndex + length && i < value.length; i++) {
                         output.push(value[i]);
                     }
    -                return output;
    -            } else if (Twig.lib.is("String", value)) {
    -                return value.substr(startIndex, length);
    -            } else {
    -                throw new Twig.Error("slice filter expects value to be an array or string");
    +                return output;
    +            } else if (Twig.lib.is("String", value)) {
    +                return value.substr(startIndex, length);
    +            } else {
    +                throw new Twig.Error("slice filter expects value to be an array or string");
                 }
             },
     
    -        abs: function(value) {
    -            if (value === undefined || value === null) {
    -                return;
    +        abs: function(value) {
    +            if (value === undefined || value === null) {
    +                return;
                 }
     
    -            return Math.abs(value);
    +            return Math.abs(value);
             },
     
    -        first: function(value) {
    -            if (value instanceof Array) {
    -                return value[0];
    -            } else if (value instanceof Object) {
    -                if ('_keys' in value) {
    -                    return value[value._keys[0]];
    +        first: function(value) {
    +            if (value instanceof Array) {
    +                return value[0];
    +            } else if (value instanceof Object) {
    +                if ('_keys' in value) {
    +                    return value[value._keys[0]];
                     }
    -            } else if ( typeof value === "string" ) {
    -                return value.substr(0, 1);
    +            } else if ( typeof value === "string" ) {
    +                return value.substr(0, 1);
                 }
     
    -            return;
    +            return;
             },
     
    -        split: function(value, params) {
    -            if (value === undefined || value === null) {
    -                return;
    +        split: function(value, params) {
    +            if (value === undefined || value === null) {
    +                return;
                 }
    -            if (params === undefined || params.length < 1 || params.length > 2) {
    -                throw new Twig.Error("split filter expects 1 or 2 argument");
    +            if (params === undefined || params.length < 1 || params.length > 2) {
    +                throw new Twig.Error("split filter expects 1 or 2 argument");
                 }
    -            if (Twig.lib.is("String", value)) {
    -                var delimiter = params[0],
    -                    limit = params[1],
    +            if (Twig.lib.is("String", value)) {
    +                var delimiter = params[0],
    +                    limit = params[1],
                         split = value.split(delimiter);
     
    -                if (limit === undefined) {
    +                if (limit === undefined) {
     
    -                    return split;
    +                    return split;
     
    -                } else if (limit < 0) {
    +                } else if (limit < 0) {
     
    -                    return value.split(delimiter, split.length + limit);
    +                    return value.split(delimiter, split.length + limit);
     
    -                } else {
    +                } else {
     
    -                    var limitedSplit = [];
    +                    var limitedSplit = [];
     
    -                    if (delimiter == '') {
    + if (delimiter == '') {
  • -
  • +
  • - +

    empty delimiter -“aabbcc”|split(‘’, 2) - -> [‘aa’, ‘bb’, ‘cc’]

    +"aabbcc"|split('', 2) + -> ['aa', 'bb', 'cc']

    -
    -                        while(split.length > 0) {
    -                            var temp = "";
    -                            for (var i=0; i<limit && split.length > 0; i++) {
    +            
                            while(split.length > 0) {
    +                            var temp = "";
    +                            for (var i=0; i<limit && split.length > 0; i++) {
                                     temp += split.shift();
                                 }
                                 limitedSplit.push(temp);
                             }
     
    -                    } else {
    + } else {
  • -
  • +
  • - +

    non-empty delimiter -“one,two,three,four,five”|split(‘,’, 3) - -> [‘one’, ‘two’, ‘three,four,five’]

    +"one,two,three,four,five"|split(',', 3) + -> ['one', 'two', 'three,four,five']

    -
    -                        for (var i=0; i<limit-1 && split.length > 0; i++) {
    +            
                            for (var i=0; i<limit-1 && split.length > 0; i++) {
                                 limitedSplit.push(split.shift());
                             }
     
    -                        if (split.length > 0) {
    +                        if (split.length > 0) {
                                 limitedSplit.push(split.join(delimiter));
                             }
                         }
     
    -                    return limitedSplit;
    +                    return limitedSplit;
                     }
     
    -            } else {
    -                throw new Twig.Error("split filter expects value to be a string");
    +            } else {
    +                throw new Twig.Error("split filter expects value to be a string");
                 }
             },
    -        last: function(value) {
    -            if (Twig.lib.is('Object', value)) {
    -                var keys;
    +        last: function(value) {
    +            if (Twig.lib.is('Object', value)) {
    +                var keys;
     
    -                if (value._keys === undefined) {
    -                    keys = Object.keys(value);
    -                } else {
    +                if (value._keys === undefined) {
    +                    keys = Object.keys(value);
    +                } else {
                         keys = value._keys;
                     }
     
    -                return value[keys[keys.length - 1]];
    +                return value[keys[keys.length - 1]];
                 }
  • -
  • +
  • - +

    string|array

    -
                return value[value.length - 1];
    +            
                return value[value.length - 1];
             },
    -        raw: function(value) {
    + raw: function(value) {
  • -
  • +
  • - +

    Raw filter shim

    -
                return value;
    +            
                return value;
    +        },
    +        batch: function(items, params) {
    +            var size = params.shift(),
    +                fill = params.shift(),
    +                result,
    +                last,
    +                missing;
    +
    +            if (!Twig.lib.is("Array", items)) {
    +                throw new Twig.Error("batch filter expects items to be an array");
    +            }
    +
    +            if (!Twig.lib.is("Number", size)) {
    +                throw new Twig.Error("batch filter expects size to be a number");
    +            }
    +
    +            size = Math.ceil(size);
    +
    +            result = Twig.lib.chunkArray(items, size);
    +
    +            if (fill && items.length % size != 0) {
    +                last = result.pop();
    +                missing = size - last.length;
    +
    +                while (missing--) {
    +                    last.push(fill);
    +                }
    +
    +                result.push(last);
    +            }
    +
    +            return result;
    +        },
    +        round: function(value, params) {
    +            params = params || [];
    +
    +            var precision = params.length > 0 ? params[0] : 0,
    +                method = params.length > 1 ? params[1] : "common";
    +
    +            value = parseFloat(value);
    +
    +            if(precision && !Twig.lib.is("Number", precision)) {
    +                throw new Twig.Error("round filter expects precision to be a number");
    +            }
    +
    +            if (method === "common") {
    +                return Twig.lib.round(value, precision);
    +            }
    +
    +            if(!Twig.lib.is("Function", Math[method])) {
    +                throw new Twig.Error("round filter expects method to be 'floor', 'ceil', or 'common'");
    +            }
    +
    +            return Math[method](value * Math.pow(10, precision)) / Math.pow(10, precision);
             }
         };
     
    -    Twig.filter = function(filter, value, params) {
    -        if (!Twig.filters[filter]) {
    -            throw "Unable to find filter " + filter;
    +    Twig.filter = function(filter, value, params) {
    +        if (!Twig.filters[filter]) {
    +            throw "Unable to find filter " + filter;
             }
    -        return Twig.filters[filter].apply(this, [value, params]);
    +        return Twig.filters[filter].apply(this, [value, params]);
         };
     
    -    Twig.filter.extend = function(filter, definition) {
    +    Twig.filter.extend = function(filter, definition) {
             Twig.filters[filter] = definition;
         };
     
    -    return Twig;
    +    return Twig;
     
     })(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

    +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

    This file handles parsing filters.

    -
    var Twig = (function (Twig) {
    +
    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;
    +            
        function is(type, obj) {
    +        var clas = Object.prototype.toString.call(obj).slice(8, -1);
    +        return obj !== undefined && obj !== null && clas === type;
         }
     
         Twig.functions = {
    @@ -7806,40 +7865,28 @@

    twig.functions.js

  • -
  • +
  • - +

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

    - -
    - -
  • - - -
  • -
    - -
    - -
    -

    Range function from http://phpjs.org/functions/range:499 +

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

    -
            range: function (low, high, step) {
    +
            range: function (low, high, step) {
  • -
  • +
  • - +

    http://kevin.vanzonneveld.net

      @@ -7848,103 +7895,103 @@

      twig.functions.js

    • returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    • example 2: range( 0, 100, 10 );
    • returns 2: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
    • -
    • example 3: range( ‘a’, ‘i’ );
    • -
    • returns 3: [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’]
    • -
    • example 4: range( ‘c’, ‘a’ );
    • -
    • returns 4: [‘c’, ‘b’, ‘a’]
    • +
    • example 3: range( 'a', 'i' );
    • +
    • returns 3: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
    • +
    • example 4: range( 'c', 'a' );
    • +
    • returns 4: ['c', 'b', 'a']
    -
                var matrix = [];
    -            var inival, endval, plus;
    -            var walker = step || 1;
    -            var chars = false;
    +            
                var matrix = [];
    +            var inival, endval, plus;
    +            var walker = step || 1;
    +            var chars = false;
     
    -            if (!isNaN(low) && !isNaN(high)) {
    -                inival = parseInt(low, 10);
    -                endval = parseInt(high, 10);
    -            } else if (isNaN(low) && isNaN(high)) {
    -                chars = true;
    -                inival = low.charCodeAt(0);
    -                endval = high.charCodeAt(0);
    -            } else {
    -                inival = (isNaN(low) ? 0 : low);
    -                endval = (isNaN(high) ? 0 : high);
    +            if (!isNaN(low) && !isNaN(high)) {
    +                inival = parseInt(low, 10);
    +                endval = parseInt(high, 10);
    +            } else if (isNaN(low) && isNaN(high)) {
    +                chars = true;
    +                inival = low.charCodeAt(0);
    +                endval = high.charCodeAt(0);
    +            } else {
    +                inival = (isNaN(low) ? 0 : low);
    +                endval = (isNaN(high) ? 0 : high);
                 }
     
    -            plus = ((inival > endval) ? false : true);
    -            if (plus) {
    -                while (inival <= endval) {
    -                    matrix.push(((chars) ? String.fromCharCode(inival) : inival));
    +            plus = ((inival > endval) ? false : true);
    +            if (plus) {
    +                while (inival <= endval) {
    +                    matrix.push(((chars) ? String.fromCharCode(inival) : inival));
                         inival += walker;
                     }
    -            } else {
    -                while (inival >= endval) {
    -                    matrix.push(((chars) ? String.fromCharCode(inival) : inival));
    +            } else {
    +                while (inival >= endval) {
    +                    matrix.push(((chars) ? String.fromCharCode(inival) : inival));
                         inival -= walker;
                     }
                 }
     
    -            return matrix;
    +            return matrix;
             },
    -        cycle: function(arr, i) {
    -            var pos = i % arr.length;
    -            return arr[pos];
    +        cycle: function(arr, i) {
    +            var pos = i % arr.length;
    +            return arr[pos];
             },
    -        dump: function() {
    -            var EOL = '\n',
    -            	indentChar = '  ',
    -            	indentTimes = 0,
    -            	out = '',
    -				args = Array.prototype.slice.call(arguments),
    -				indent = function(times) {
    -                	var ind	 = '';
    -                    while (times > 0) {
    +        dump: function() {
    +            var EOL = '\n',
    +            	indentChar = '  ',
    +            	indentTimes = 0,
    +            	out = '',
    +				args = Array.prototype.slice.call(arguments),
    +				indent = function(times) {
    +                	var ind	 = '';
    +                    while (times > 0) {
                             times--;
                             ind += indentChar;
                         }
    -                    return ind;
    +                    return ind;
                     },
    -				displayVar = function(variable) {
    +				displayVar = function(variable) {
                         out += indent(indentTimes);
    -                    if (typeof(variable) === 'object') {
    +                    if (typeof(variable) === 'object') {
                             dumpVar(variable);
    -                    } else if (typeof(variable) === 'function') {
    -                        out += 'function()' + EOL;
    -                    } else if (typeof(variable) === 'string') {
    -                        out += 'string(' + variable.length + ') "' + variable + '"' + EOL;
    -                    } else if (typeof(variable) === 'number') {
    -                        out += 'number(' + variable + ')' + EOL;
    -                    } else if (typeof(variable) === 'boolean') {
    -                        out += 'bool(' + variable + ')' + EOL;
    +                    } else if (typeof(variable) === 'function') {
    +                        out += 'function()' + EOL;
    +                    } else if (typeof(variable) === 'string') {
    +                        out += 'string(' + variable.length + ') "' + variable + '"' + EOL;
    +                    } else if (typeof(variable) === 'number') {
    +                        out += 'number(' + variable + ')' + EOL;
    +                    } else if (typeof(variable) === 'boolean') {
    +                        out += 'bool(' + variable + ')' + EOL;
                         }
                     },
    -             	dumpVar = function(variable) {
    -					var	i;
    -	                if (variable === null) {
    -	                    out += 'NULL' + EOL;
    -	                } else if (variable === undefined) {
    -	                    out += 'undefined' + EOL;
    -	                } else if (typeof variable === 'object') {
    -	                    out += indent(indentTimes) + typeof(variable);
    +             	dumpVar = function(variable) {
    +					var	i;
    +	                if (variable === null) {
    +	                    out += 'NULL' + EOL;
    +	                } else if (variable === undefined) {
    +	                    out += 'undefined' + EOL;
    +	                } else if (typeof variable === 'object') {
    +	                    out += indent(indentTimes) + typeof(variable);
     	                    indentTimes++;
    -	                    out += '(' + (function(obj) {
    -	                        var size = 0, key;
    -	                        for (key in obj) {
    -	                            if (obj.hasOwnProperty(key)) {
    +	                    out += '(' + (function(obj) {
    +	                        var size = 0, key;
    +	                        for (key in obj) {
    +	                            if (obj.hasOwnProperty(key)) {
     	                                size++;
     	                            }
     	                        }
    -	                        return size;
    -	                    })(variable) + ') {' + EOL;
    -	                    for (i in variable) {
    -	                        out += indent(indentTimes) + '[' + i + ']=> ' + EOL;
    +	                        return size;
    +	                    })(variable) + ') {' + EOL;
    +	                    for (i in variable) {
    +	                        out += indent(indentTimes) + '[' + i + ']=> ' + EOL;
     	                        displayVar(variable[i]);
     	                    }
     	                    indentTimes--;
    -	                    out += indent(indentTimes) + '}' + EOL;
    -	                } else {
    +	                    out += indent(indentTimes) + '}' + EOL;
    +	                } else {
     	                    displayVar(variable);
     	                }
     	            };
    @@ -7952,81 +7999,81 @@

    twig.functions.js

  • -
  • +
  • - +

    handle no argument case by dumping the entire render context

    -
    			if (args.length == 0) args.push(this.context);
    +            
    			if (args.length == 0) args.push(this.context);
     
    -			Twig.forEach(args, function(variable) {
    +			Twig.forEach(args, function(variable) {
     				dumpVar(variable);
     			});
     
    -            return out;
    +            return out;
             },
    -        date: function(date, time) {
    -            var dateObj;
    -            if (date === undefined) {
    -                dateObj = new Date();
    -            } else if (Twig.lib.is("Date", date)) {
    +        date: function(date, time) {
    +            var dateObj;
    +            if (date === undefined) {
    +                dateObj = new Date();
    +            } else if (Twig.lib.is("Date", date)) {
                     dateObj = date;
    -            } else if (Twig.lib.is("String", date)) {
    -                dateObj = new Date(Twig.lib.strtotime(date) * 1000);
    -            } else if (Twig.lib.is("Number", 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 {
    -                throw new Twig.Error("Unable to parse date " + date);
    +            
                    dateObj = new Date(date * 1000);
    +            } else {
    +                throw new Twig.Error("Unable to parse date " + date);
                 }
    -            return dateObj;
    +            return dateObj;
             },
    -        block: function(block) {
    -            return this.blocks[block];
    +        block: function(block) {
    +            return this.blocks[block];
             },
    -        parent: function() {
    + parent: function() {
  • -
  • +
  • - +

    Add a placeholder

    -
                return Twig.placeholders.parent;
    +            
                return Twig.placeholders.parent;
             },
    -        attribute: function(object, method, params) {
    -            if (object instanceof Object) {
    -                if (object.hasOwnProperty(method)) {
    -                    if (typeof object[method] === "function") {
    -                        return object[method].apply(undefined, params);
    +        attribute: function(object, method, params) {
    +            if (object instanceof Object) {
    +                if (object.hasOwnProperty(method)) {
    +                    if (typeof object[method] === "function") {
    +                        return object[method].apply(undefined, params);
                         }
    -                    else {
    -                        return object[method];
    +                    else {
    +                        return object[method];
                         }
                     }
                 }
    @@ -8034,241 +8081,216 @@

    twig.functions.js

  • -
  • +
  • - +

    Array will return element 0-index

    -
                return object[method] || undefined;
    +            
                return object[method] || undefined;
             }
         };
     
    -    Twig._function = function(_function, value, params) {
    -        if (!Twig.functions[_function]) {
    -            throw "Unable to find function " + _function;
    +    Twig._function = function(_function, value, params) {
    +        if (!Twig.functions[_function]) {
    +            throw "Unable to find function " + _function;
             }
    -        return Twig.functions[_function](value, params);
    +        return Twig.functions[_function](value, params);
         };
     
    -    Twig._function.extend = function(_function, definition) {
    +    Twig._function.extend = function(_function, definition) {
             Twig.functions[_function] = definition;
         };
     
    -    return Twig;
    +    return Twig;
     
     })(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

    -

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

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.tests.js

    +

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

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

    Handler numbers

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

    Handle strings and arrays

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

    Handle objects

    -
                for (var key in value) {
    -                if (value.hasOwnProperty(key)) return false;
    +            
                for (var key in value) {
    +                if (value.hasOwnProperty(key)) return false;
                 }
    -            return true;
    +            return true;
             },
    -        odd: function(value) {
    -            return value % 2 === 1;
    +        odd: function(value) {
    +            return value % 2 === 1;
             },
    -        even: function(value) {
    -            return value % 2 === 0;
    +        even: function(value) {
    +            return value % 2 === 0;
             },
    -        divisibleby: function(value, params) {
    -            return value % params[0] === 0;
    +        divisibleby: function(value, params) {
    +            return value % params[0] === 0;
             },
    -        defined: function(value) {
    -            return value !== undefined;
    +        defined: function(value) {
    +            return value !== undefined;
             },
    -        none: function(value) {
    -            return value === null;
    +        none: function(value) {
    +            return value === null;
             },
    -        'null': function(value) {
    -            return this.none(value); // Alias of none
    +        'null': function(value) {
    +            return this.none(value); // Alias of none
             },
    -        sameas: function(value, params) {
    -            return value === params[0];
    +        sameas: function(value, params) {
    +            return value === params[0];
             }
    -        /*
    +        /*
             constant ?
              */
         };
     
    -    Twig.test = function(test, value, params) {
    -        if (!Twig.tests[test]) {
    -            throw "Test " + test + " is not defined.";
    +    Twig.test = function(test, value, params) {
    +        if (!Twig.tests[test]) {
    +            throw "Test " + test + " is not defined.";
             }
    -        return Twig.tests[test](value, params);
    +        return Twig.tests[test](value, params);
         };
     
    -    Twig.test.extend = function(test, definition) {
    +    Twig.test.extend = function(test, definition) {
             Twig.tests[test] = definition;
         };
     
    -    return Twig;
    +    return Twig;
     })( Twig || { } );
  • -
  • +
  • - +
    Twig.js
    -Copyright (c) 2011-2013 John Roepke
    -Available under the BSD 2-Clause License
    -https://github.com/justjohn/twig.js
    -
    -
    - -
  • - - -
  • -
    - -
    - -
    -

    twig.exports.js

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.exports.js

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

    -
    -var Twig = (function (Twig) {
    -    "use strict";
    +            
    var Twig = (function (Twig) {
    +    "use strict";
         Twig.exports = {
             VERSION: Twig.VERSION
         };
     
    -    /**
    +    /**
          * Create and compile a twig.js template.
          *
          * @param {Object} param Paramteres for creating a Twig template.
          *
          * @return {Twig.Template} A Twig template ready for rendering.
          */
    -    Twig.exports.twig = function twig(params) {
    -        'use strict';
    -        var id = params.id,
    +    Twig.exports.twig = function twig(params) {
    +        'use strict';
    +        var id = params.id,
                 options = {
    -                strict_variables: params.strict_variables || false,
    -                allowInlineIncludes: params.allowInlineIncludes || false,
    -                rethrow: params.rethrow || false
    +                strict_variables: params.strict_variables || false,
    +                allowInlineIncludes: params.allowInlineIncludes || false,
    +                rethrow: params.rethrow || false
                 };
     
    -        if (id) {
    +        if (id) {
                 Twig.validateId(id);
             }
     
    -        if (params.debug !== undefined) {
    +        if (params.debug !== undefined) {
                 Twig.debug = params.debug;
             }
    -        if (params.trace !== undefined) {
    +        if (params.trace !== undefined) {
                 Twig.trace = params.trace;
             }
     
    -        if (params.data !== undefined) {
    -            return new Twig.Template({
    +        if (params.data !== undefined) {
    +            return new Twig.Template({
                     data: params.data,
                     module: params.module,
                     id:   id,
                     options: options
                 });
     
    -        } else if (params.ref !== undefined) {
    -            if (params.id !== undefined) {
    -                throw new Twig.Error("Both ref and id cannot be set on a twig.js template.");
    +        } else if (params.ref !== undefined) {
    +            if (params.id !== undefined) {
    +                throw new Twig.Error("Both ref and id cannot be set on a twig.js template.");
                 }
    -            return Twig.Templates.load(params.ref);
    +            return Twig.Templates.load(params.ref);
     
    -        } else if (params.href !== undefined) {
    -            return Twig.Templates.loadRemote(params.href, {
    +        } else if (params.href !== undefined) {
    +            return Twig.Templates.loadRemote(params.href, {
                     id: id,
    -                method: 'ajax',
    +                method: 'ajax',
                     base: params.base,
                     module: params.module,
                     precompiled: params.precompiled,
    @@ -8277,10 +8299,10 @@ 

    twig.exports.js

    }, params.load, params.error); - } else if (params.path !== undefined) { - return Twig.Templates.loadRemote(params.path, { + } else if (params.path !== undefined) { + return Twig.Templates.loadRemote(params.path, { id: id, - method: 'fs', + method: 'fs', base: params.base, module: params.module, precompiled: params.precompiled, @@ -8294,91 +8316,91 @@

    twig.exports.js

  • -
  • +
  • - +

    Extend Twig with a new filter.

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

    Extend Twig with a new function.

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

    Extend Twig with a new test.

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

    Extend Twig with a new definition.

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

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

    -
        Twig.exports.extend = function(fn) {
    +            
        Twig.exports.extend = function(fn) {
             fn(Twig);
         };
     
     
    -    /**
    +    /**
          * Provide an extension for use with express 2.
          *
          * @param {string} markup The template markup.
    @@ -8386,37 +8408,37 @@ 

    twig.exports.js

    * * @return {string} The rendered template. */
    - Twig.exports.compile = function(markup, options) { - var id = options.filename, + 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 = new Twig.Template({
                 data: markup,
                 path: path,
                 id: id,
    -            options: options.settings['twig options']
    -        }); // Twig.Templates.load(id) ||
    +            options: options.settings['twig options']
    +        }); // Twig.Templates.load(id) ||
     
    -        return function(context) {
    -            return template.render(context);
    +        return function(context) {
    +            return template.render(context);
             };
         };
     
    -    /**
    +    /**
          * Provide an extension for use with express 3.
          *
          * @param {string} path The location of the template file on disk.
    @@ -8424,67 +8446,67 @@ 

    twig.exports.js

    * @param {Function} fn callback. */
    - Twig.exports.renderFile = function(path, options, fn) {
    + Twig.exports.renderFile = function(path, options, fn) {
  • -
  • +
  • - +

    handle callback in options

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

    render and return template

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

    mixin any options provided to the express app.

    -
            var view_options = options.settings['twig options'];
    +            
            var view_options = options.settings['twig options'];
     
    -        if (view_options) {
    -            for (var option in view_options) if (view_options.hasOwnProperty(option)) {
    +        if (view_options) {
    +            for (var option in view_options) if (view_options.hasOwnProperty(option)) {
                     params[option] = view_options[option];
                 }
             }
    @@ -8495,11 +8517,11 @@ 

    twig.exports.js

  • -
  • +
  • - +

    Express 3 handler

    @@ -8507,52 +8529,40 @@

    twig.exports.js

        Twig.exports.__express = Twig.exports.renderFile;
     
    -    /**
    +    /**
          * Shoud Twig.js cache templates.
          * Disable during development to see changes to templates without
          * reloading, and disable in production to improve performance.
          *
          * @param {boolean} cache
          */
    -    Twig.exports.cache = function(cache) {
    +    Twig.exports.cache = function(cache) {
             Twig.cache = cache;
         }
     
    -    return Twig;
    +    return Twig;
     }) (Twig || { });
  • -
  • +
  • - +
    Twig.js
    -Copyright (c) 2011-2013 John Roepke
    -Available under the BSD 2-Clause License
    -https://github.com/justjohn/twig.js
    -
    -
    - -
  • - - -
  • -
    - -
    - -
    -

    twig.compiler.js

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.compiler.js

    This file handles compiling templates into JS

    -
    var Twig = (function (Twig) {
    -    /**
    +            
    var Twig = (function (Twig) {
    +    /**
          * Namespace for compilation.
          */
         Twig.compiler = {
    @@ -8562,166 +8572,153 @@ 

    twig.compiler.js

  • -
  • +
  • - +

    Compile a Twig Template to output.

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

    Get tokens

    -
            var tokens = JSON.stringify(template.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);
    +        if (options.module) {
    +            if (Twig.compiler.module[options.module] === undefined) {
    +                throw new Twig.Error("Unable to find module type " + options.module);
                 }
                 output = Twig.compiler.module[options.module](id, tokens, options.twig);
    -        } else {
    +        } else {
                 output = Twig.compiler.wrap(id, tokens);
             }
    -        return output;
    +        return output;
         };
     
         Twig.compiler.module = {
    -        amd: function(id, tokens, pathToTwig) {
    -            return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig, templates;\ntwig = Twig.twig;\ntemplates = ' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});';
    +        amd: function(id, tokens, pathToTwig) {
    +            return 'define(["' + pathToTwig + '"], function (Twig) {\n\tvar twig, templates;\ntwig = Twig.twig;\ntemplates = ' + Twig.compiler.wrap(id, tokens) + '\n\treturn templates;\n});';
             }
    -        , node: function(id, tokens) {
    -            return 'var twig = require("twig").twig;\n'
    -                + 'exports.template = ' + Twig.compiler.wrap(id, tokens)
    +        , node: function(id, tokens) {
    +            return 'var twig = require("twig").twig;\n'
    +                + 'exports.template = ' + Twig.compiler.wrap(id, tokens)
             }
    -        , cjs2: function(id, tokens, pathToTwig) {
    -            return 'module.declare([{ twig: "' + pathToTwig + '" }], function (require, exports, module) {\n'
    -                        + '\tvar twig = require("twig").twig;\n'
    -                        + '\texports.template = ' + Twig.compiler.wrap(id, tokens)
    -                    + '\n});'
    +        , cjs2: function(id, tokens, pathToTwig) {
    +            return 'module.declare([{ twig: "' + pathToTwig + '" }], function (require, exports, module) {\n'
    +                        + '\tvar twig = require("twig").twig;\n'
    +                        + '\texports.template = ' + Twig.compiler.wrap(id, tokens)
    +                    + '\n});'
             }
         };
     
    -    Twig.compiler.wrap = function(id, tokens) {
    -        return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n';
    +    Twig.compiler.wrap = function(id, tokens) {
    +        return 'twig({id:"'+id.replace('"', '\\"')+'", data:'+tokens+', precompiled: true});\n';
         };
     
    -    return Twig;
    +    return Twig;
     })(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

    +Copyright (c) 2011-2013 John Roepke +Available under the BSD 2-Clause License +https://github.com/justjohn/twig.js +

    twig.module.js

    Provide a CommonJS/AMD/Node module export.

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

    Provide a CommonJS Modules/2.0 draft 8 module

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

    Add exports from the Twig exports

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

    Provide a CommonJS Modules/1.1 module

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

    Export for browser use

    diff --git a/package.json b/package.json index 2967f655..53495143 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.7.1", + "version": "0.7.2", "homepage": "https://github.com/justjohn/twig.js", "licenses": [ { "type": "BSD-2-Clause", diff --git a/src/twig.header.js b/src/twig.header.js index 079fe01e..1f3a6949 100644 --- a/src/twig.header.js +++ b/src/twig.header.js @@ -1,5 +1,5 @@ /** - * Twig.js 0.7.1 + * Twig.js 0.7.2 * * @copyright 2011-2013 John Roepke * @license Available under the BSD 2-Clause License @@ -8,7 +8,7 @@ var Twig = (function (Twig) { - Twig.VERSION = "0.7.1"; + Twig.VERSION = "0.7.2"; return Twig; })(Twig || {}); diff --git a/twig.js b/twig.js index b06fa2f8..5b8298d7 100644 --- a/twig.js +++ b/twig.js @@ -1,5 +1,5 @@ /** - * Twig.js 0.7.1 + * Twig.js 0.7.2 * * @copyright 2011-2013 John Roepke * @license Available under the BSD 2-Clause License @@ -8,7 +8,7 @@ var Twig = (function (Twig) { - Twig.VERSION = "0.7.1"; + Twig.VERSION = "0.7.2"; return Twig; })(Twig || {}); diff --git a/twig.min.js b/twig.min.js index 08bf3bfc..53e1766c 100644 --- a/twig.min.js +++ b/twig.min.js @@ -1,11 +1,11 @@ /** - * Twig.js 0.7.1 + * Twig.js 0.7.2 * * @copyright 2011-2013 John Roepke * @license Available under the BSD 2-Clause License * @link https://github.com/justjohn/twig.js */ -var Twig=function(Twig){Twig.VERSION="0.7.1";return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.trace=false;Twig.debug=false;Twig.cache=true;Twig.placeholders={parent:"{{|PARENT|}}"};Twig.indexOf=function(arr,searchElement){if(Array.prototype.hasOwnProperty("indexOf")){return arr.indexOf(searchElement)}if(arr===void 0||arr===null){throw new TypeError}var t=Object(arr);var len=t.length>>>0;if(len===0){return-1}var n=0;if(arguments.length>0){n=Number(arguments[1]);if(n!==n){n=0}else if(n!==0&&n!==Infinity&&n!==-Infinity){n=(n>0||-1)*Math.floor(Math.abs(n))}}if(n>=len){return-1}var k=n>=0?n:Math.max(len-Math.abs(n),0);for(;k>>0;if({}.toString.call(callback)!="[object Function]"){throw new TypeError(callback+" is not a function")}if(thisArg){T=thisArg}k=0;while(k=0&&(output.position===null||first_key_position=0){end=pos;found=true}else{throw new Twig.Error("Unable to find closing bracket '"+token_def.close+"'"+" opened near template position "+start)}if(token_def.type===Twig.token.type.comment){break}l=Twig.token.strings.length;for(i=0;i0&&this_str_pos0){found_token=Twig.token.findStart(template);Twig.log.trace("Twig.tokenize: ","Found token: ",found_token);if(found_token.position!==null){if(found_token.position>0){tokens.push({type:Twig.token.type.raw,value:template.substring(0,found_token.position)})}template=template.substr(found_token.position+found_token.def.open.length);error_offset+=found_token.position+found_token.def.open.length;end=Twig.token.findEnd(template,found_token.def,error_offset);Twig.log.trace("Twig.tokenize: ","Token ends at ",end);tokens.push({type:found_token.def.type,value:template.substring(0,end).trim()});if(found_token.def.type==="logic"&&template.substr(end+found_token.def.close.length,1)==="\n"){end+=1}template=template.substr(end+found_token.def.close.length);error_offset+=end+found_token.def.close.length}else{tokens.push({type:Twig.token.type.raw,value:template});template=""}}return tokens};Twig.compile=function(tokens){try{var output=[],stack=[],intermediate_output=[],token=null,logic_token=null,unclosed_token=null,prev_token=null,prev_template=null,tok_output=null,type=null,open=null,next=null;while(tokens.length>0){token=tokens.shift();Twig.log.trace("Compiling token ",token);switch(token.type){case Twig.token.type.raw:if(stack.length>0){intermediate_output.push(token)}else{output.push(token)}break;case Twig.token.type.logic:logic_token=Twig.logic.compile.apply(this,[token]);type=logic_token.type;open=Twig.logic.handler[type].open;next=Twig.logic.handler[type].next;Twig.log.trace("Twig.compile: ","Compiled logic token to ",logic_token," next is: ",next," open is : ",open);if(open!==undefined&&!open){prev_token=stack.pop();prev_template=Twig.logic.handler[prev_token.type];if(Twig.indexOf(prev_template.next,type)<0){throw new Error(type+" not expected after a "+prev_token.type)}prev_token.output=prev_token.output||[];prev_token.output=prev_token.output.concat(intermediate_output);intermediate_output=[];tok_output={type:Twig.token.type.logic,token:prev_token};if(stack.length>0){intermediate_output.push(tok_output)}else{output.push(tok_output)}}if(next!==undefined&&next.length>0){Twig.log.trace("Twig.compile: ","Pushing ",logic_token," to logic stack.");if(stack.length>0){prev_token=stack.pop();prev_token.output=prev_token.output||[];prev_token.output=prev_token.output.concat(intermediate_output);stack.push(prev_token);intermediate_output=[]}stack.push(logic_token)}else if(open!==undefined&&open){tok_output={type:Twig.token.type.logic,token:logic_token};if(stack.length>0){intermediate_output.push(tok_output)}else{output.push(tok_output)}}break;case Twig.token.type.comment:break;case Twig.token.type.output:Twig.expression.compile.apply(this,[token]);if(stack.length>0){intermediate_output.push(token)}else{output.push(token)}break}Twig.log.trace("Twig.compile: "," Output: ",output," Logic Stack: ",stack," Pending Output: ",intermediate_output)}if(stack.length>0){unclosed_token=stack.pop();throw new Error("Unable to find an end tag for "+unclosed_token.type+", expecting one of "+unclosed_token.next)}return output}catch(ex){Twig.log.error("Error compiling twig template "+this.id+": ");if(ex.stack){Twig.log.error(ex.stack)}else{Twig.log.error(ex.toString())}if(this.options.rethrow)throw ex}};Twig.parse=function(tokens,context){try{var output=[],chain=true,that=this;context=context||{};Twig.forEach(tokens,function parseToken(token){Twig.log.debug("Twig.parse: ","Parsing token: ",token);switch(token.type){case Twig.token.type.raw:output.push(token.value);break;case Twig.token.type.logic:var logic_token=token.token,logic=Twig.logic.parse.apply(that,[logic_token,context,chain]);if(logic.chain!==undefined){chain=logic.chain}if(logic.context!==undefined){context=logic.context}if(logic.output!==undefined){output.push(logic.output)}break;case Twig.token.type.comment:break;case Twig.token.type.output:Twig.log.debug("Twig.parse: ","Output token: ",token.stack);output.push(Twig.expression.parse.apply(that,[token.stack,context]));break}});return output.join("")}catch(ex){Twig.log.error("Error parsing twig template "+this.id+": ");if(ex.stack){Twig.log.error(ex.stack)}else{Twig.log.error(ex.toString())}if(this.options.rethrow)throw ex;if(Twig.debug){return ex.toString()}}};Twig.prepare=function(data){var tokens,raw_tokens;Twig.log.debug("Twig.prepare: ","Tokenizing ",data);raw_tokens=Twig.tokenize.apply(this,[data]);Twig.log.debug("Twig.prepare: ","Compiling ",raw_tokens);tokens=Twig.compile.apply(this,[raw_tokens]);Twig.log.debug("Twig.prepare: ","Compiled ",tokens);return tokens};Twig.Templates={registry:{}};Twig.validateId=function(id){if(id==="prototype"){throw new Twig.Error(id+" is not a valid twig identifier")}else if(Twig.Templates.registry.hasOwnProperty(id)){throw new Twig.Error("There is already a template with the ID "+id)}return true};Twig.Templates.save=function(template){if(template.id===undefined){throw new Twig.Error("Unable to save template with no id")}Twig.Templates.registry[template.id]=template};Twig.Templates.load=function(id){if(!Twig.Templates.registry.hasOwnProperty(id)){return null}return Twig.Templates.registry[id]};Twig.Templates.loadRemote=function(location,params,callback,error_callback){var id=params.id,method=params.method,async=params.async,precompiled=params.precompiled,template=null;if(async===undefined)async=true;if(id===undefined){id=location}params.id=id;if(Twig.cache&&Twig.Templates.registry.hasOwnProperty(id)){if(callback){callback(Twig.Templates.registry[id])}return Twig.Templates.registry[id]}if(method=="ajax"){if(typeof XMLHttpRequest=="undefined"){throw new Twig.Error("Unsupported platform: Unable to do remote requests "+"because there is no XMLHTTPRequest implementation")}var xmlhttp=new XMLHttpRequest;xmlhttp.onreadystatechange=function(){var data=null;if(xmlhttp.readyState==4){if(xmlhttp.status==200){Twig.log.debug("Got template ",xmlhttp.responseText);if(precompiled===true){data=JSON.parse(xmlhttp.responseText)}else{data=xmlhttp.responseText}params.url=location;params.data=data;template=new Twig.Template(params);if(callback){callback(template)}}else{if(error_callback){error_callback(xmlhttp)}}}};xmlhttp.open("GET",location,async);xmlhttp.send()}else{(function(){var fs=require("fs"),path=require("path"),data=null,loadTemplateFn=function(err,data){if(err){if(error_callback){error_callback(err)}return}if(precompiled===true){data=JSON.parse(data)}params.data=data;params.path=location;template=new Twig.Template(params);if(callback){callback(template)}};if(async===true){fs.stat(location,function(err,stats){if(err||!stats.isFile())throw new Twig.Error("Unable to find template file "+location);fs.readFile(location,"utf8",loadTemplateFn)})}else{if(!fs.statSync(location).isFile())throw new Twig.Error("Unable to find template file "+location);data=fs.readFileSync(location,"utf8");loadTemplateFn(undefined,data)}})()}if(async===false){return template}else{return true}};function is(type,obj){var clas=Object.prototype.toString.call(obj).slice(8,-1);return obj!==undefined&&obj!==null&&clas===type}Twig.Template=function(params){var data=params.data,id=params.id,blocks=params.blocks,macros=params.macros||{},base=params.base,path=params.path,url=params.url,options=params.options;this.id=id;this.base=base;this.path=path;this.url=url;this.macros=macros;this.options=options;this.reset(blocks);if(is("String",data)){this.tokens=Twig.prepare.apply(this,[data])}else{this.tokens=data}if(id!==undefined){Twig.Templates.save(this)}};Twig.Template.prototype.reset=function(blocks){Twig.log.debug("Twig.Template.reset","Reseting template "+this.id);this.blocks={};this.child={blocks:blocks||{}};this.extend=null};Twig.Template.prototype.render=function(context,params){params=params||{};var output,url;this.context=context||{};this.reset();if(params.blocks){this.blocks=params.blocks}if(params.macros){this.macros=params.macros}output=Twig.parse.apply(this,[this.tokens,this.context]);if(this.extend){var ext_template;if(this.options.allowInlineIncludes){ext_template=Twig.Templates.load(this.extend);if(ext_template){ext_template.options=this.options}}if(!ext_template){url=relativePath(this,this.extend);ext_template=Twig.Templates.loadRemote(url,{method:this.url?"ajax":"fs",base:this.base,async:false,id:url,options:this.options})}this.parent=ext_template;return this.parent.render(this.context,{blocks:this.blocks})}if(params.output=="blocks"){return this.blocks}else if(params.output=="macros"){return this.macros}else{return output}};Twig.Template.prototype.importFile=function(file){var url,sub_template;if(!this.url&&!this.path&&this.options.allowInlineIncludes){sub_template=Twig.Templates.load(file);sub_template.options=this.options;if(sub_template){return sub_template}throw new Twig.Error("Didn't find the inline template by id")}url=relativePath(this,file);sub_template=Twig.Templates.loadRemote(url,{method:this.url?"ajax":"fs",base:this.base,async:false,options:this.options,id:url});return sub_template};Twig.Template.prototype.importBlocks=function(file,override){var sub_template=this.importFile(file),context=this.context,that=this,key;override=override||false;sub_template.render(context);Twig.forEach(Object.keys(sub_template.blocks),function(key){if(override||that.blocks[key]===undefined){that.blocks[key]=sub_template.blocks[key]}})};Twig.Template.prototype.importMacros=function(file){var url=relativePath(this,file);var remoteTemplate=Twig.Templates.loadRemote(url,{method:this.url?"ajax":"fs",async:false,id:url});return remoteTemplate};Twig.Template.prototype.compile=function(options){return Twig.compiler.compile(this,options)};function relativePath(template,file){var base,base_path,sep_chr="/",new_path=[],val;if(template.url){if(typeof template.base!=="undefined"){base=template.base+(template.base.charAt(template.base.length-1)==="/"?"":"/")}else{base=template.url}}else if(template.path){var path=require("path"),sep=path.sep||sep_chr,relative=new RegExp("^\\.{1,2}"+sep.replace("\\","\\\\"));file=file.replace(/\//g,sep);if(template.base!==undefined&&file.match(relative)==null){file=file.replace(template.base,"");base=template.base+sep}else{base=template.path}base=base.replace(sep+sep,sep);sep_chr=sep}else{throw new Twig.Error("Cannot extend an inline template.")}base_path=base.split(sep_chr);base_path.pop();base_path=base_path.concat(file.split(sep_chr));while(base_path.length>0){val=base_path.shift();if(val=="."){}else if(val==".."&&new_path.length>0&&new_path[new_path.length-1]!=".."){new_path.pop()}else{new_path.push(val)}}return new_path.join(sep_chr)}return Twig}(Twig||{});(function(){"use strict";if(!String.prototype.trim){String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}}if(!Object.keys)Object.keys=function(o){if(o!==Object(o)){throw new TypeError("Object.keys called on non-object")}var ret=[],p;for(p in o)if(Object.prototype.hasOwnProperty.call(o,p))ret.push(p);return ret}})();var Twig=function(Twig){Twig.lib={};var sprintf=function(){function get_type(variable){return Object.prototype.toString.call(variable).slice(8,-1).toLowerCase()}function str_repeat(input,multiplier){for(var output=[];multiplier>0;output[--multiplier]=input){}return output.join("")}var str_format=function(){if(!str_format.cache.hasOwnProperty(arguments[0])){str_format.cache[arguments[0]]=str_format.parse(arguments[0])}return str_format.format.call(null,str_format.cache[arguments[0]],arguments)};str_format.format=function(parse_tree,argv){var cursor=1,tree_length=parse_tree.length,node_type="",arg,output=[],i,k,match,pad,pad_character,pad_length;for(i=0;i=0?"+"+arg:arg;pad_character=match[4]?match[4]=="0"?"0":match[4].charAt(1):" ";pad_length=match[6]-String(arg).length;pad=match[6]?str_repeat(pad_character,pad_length):"";output.push(match[5]?arg+pad:pad+arg)}}return output.join("")};str_format.cache={};str_format.parse=function(fmt){var _fmt=fmt,match=[],parse_tree=[],arg_names=0;while(_fmt){if((match=/^[^\x25]+/.exec(_fmt))!==null){parse_tree.push(match[0])}else if((match=/^\x25{2}/.exec(_fmt))!==null){parse_tree.push("%")}else if((match=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt))!==null){if(match[2]){arg_names|=1;var field_list=[],replacement_field=match[2],field_match=[];if((field_match=/^([a-z_][a-z_\d]*)/i.exec(replacement_field))!==null){field_list.push(field_match[1]);while((replacement_field=replacement_field.substring(field_match[0].length))!==""){if((field_match=/^\.([a-z_][a-z_\d]*)/i.exec(replacement_field))!==null){field_list.push(field_match[1])}else if((field_match=/^\[(\d+)\]/.exec(replacement_field))!==null){field_list.push(field_match[1])}else{throw"[sprintf] huh?"}}}else{throw"[sprintf] huh?"}match[2]=field_list}else{arg_names|=2}if(arg_names===3){throw"[sprintf] mixing positional and named placeholders is not (yet) supported"}parse_tree.push(match)}else{throw"[sprintf] huh?"}_fmt=_fmt.substring(match[0].length)}return parse_tree};return str_format}();var vsprintf=function(fmt,argv){argv.unshift(fmt);return sprintf.apply(null,argv)};Twig.lib.sprintf=sprintf;Twig.lib.vsprintf=vsprintf;(function(){var shortDays="Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(",");var fullDays="Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(",");var shortMonths="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(",");var fullMonths="January,February,March,April,May,June,July,August,September,October,November,December".split(",");function getOrdinalFor(intNum){return(intNum=Math.abs(intNum)%100)%10==1&&intNum!=11?"st":intNum%10==2&&intNum!=12?"nd":intNum%10==3&&intNum!=13?"rd":"th"}function getISO8601Year(aDate){var d=new Date(aDate.getFullYear()+1,0,4);if((d-aDate)/864e5<7&&(aDate.getDay()+6)%7<(d.getDay()+6)%7)return d.getFullYear();if(aDate.getMonth()>0||aDate.getDate()>=4)return aDate.getFullYear();return aDate.getFullYear()-((aDate.getDay()+6)%7-aDate.getDate()>2?1:0)}function getISO8601Week(aDate){var d=new Date(getISO8601Year(aDate),0,4);d.setDate(d.getDate()-(d.getDay()+6)%7);return parseInt((aDate-d)/6048e5)+1}Twig.lib.formatDate=function(date,format){if(typeof format!=="string"||/^\s*$/.test(format))return date+"";var jan1st=new Date(date.getFullYear(),0,1);var me=date;return format.replace(/[dDjlNSwzWFmMntLoYyaABgGhHisuU]/g,function(option){switch(option){case"d":return("0"+me.getDate()).replace(/^.+(..)$/,"$1");case"D":return shortDays[me.getDay()];case"j":return me.getDate();case"l":return fullDays[me.getDay()];case"N":return(me.getDay()+6)%7+1;case"S":return getOrdinalFor(me.getDate());case"w":return me.getDay();case"z":return Math.ceil((jan1st-me)/864e5);case"W":return("0"+getISO8601Week(me)).replace(/^.(..)$/,"$1");case"F":return fullMonths[me.getMonth()];case"m":return("0"+(me.getMonth()+1)).replace(/^.+(..)$/,"$1");case"M":return shortMonths[me.getMonth()];case"n":return me.getMonth()+1;case"t":return new Date(me.getFullYear(),me.getMonth()+1,-1).getDate();case"L":return new Date(me.getFullYear(),1,29).getDate()==29?1:0;case"o":return getISO8601Year(me);case"Y":return me.getFullYear();case"y":return(me.getFullYear()+"").replace(/^.+(..)$/,"$1");case"a":return me.getHours()<12?"am":"pm";case"A":return me.getHours()<12?"AM":"PM";case"B":return Math.floor(((me.getUTCHours()+1)%24+me.getUTCMinutes()/60+me.getUTCSeconds()/3600)*1e3/24);case"g":return me.getHours()%12!=0?me.getHours()%12:12;case"G":return me.getHours();case"h":return("0"+(me.getHours()%12!=0?me.getHours()%12:12)).replace(/^.+(..)$/,"$1");case"H":return("0"+me.getHours()).replace(/^.+(..)$/,"$1");case"i":return("0"+me.getMinutes()).replace(/^.+(..)$/,"$1");case"s":return("0"+me.getSeconds()).replace(/^.+(..)$/,"$1");case"u":return me.getMilliseconds();case"U":return me.getTime()/1e3}})}})();Twig.lib.strip_tags=function(input,allowed){allowed=(((allowed||"")+"").toLowerCase().match(/<[a-z][a-z0-9]*>/g)||[]).join("");var tags=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,commentsAndPhpTags=/|<\?(?:php)?[\s\S]*?\?>/gi;return input.replace(commentsAndPhpTags,"").replace(tags,function($0,$1){return allowed.indexOf("<"+$1.toLowerCase()+">")>-1?$0:""})};Twig.lib.parseISO8601Date=function(s){var re=/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+-])(\d\d):(\d\d))/;var d=[];d=s.match(re);if(!d){throw"Couldn't parse ISO 8601 date string '"+s+"'"}var a=[1,2,3,4,5,6,10,11];for(var i in a){d[a[i]]=parseInt(d[a[i]],10)}d[7]=parseFloat(d[7]);var ms=Date.UTC(d[1],d[2]-1,d[3],d[4],d[5],d[6]);if(d[7]>0){ms+=Math.round(d[7]*1e3)}if(d[8]!="Z"&&d[10]){var offset=d[10]*60*60*1e3;if(d[11]){offset+=d[11]*60*1e3}if(d[9]=="-"){ms-=offset}else{ms+=offset}}return new Date(ms)};Twig.lib.strtotime=function(str,now){var i,l,match,s,parse="";str=str.replace(/\s{2,}|^\s|\s$/g," ");str=str.replace(/[\t\r\n]/g,"");if(str==="now"){return now===null||isNaN(now)?(new Date).getTime()/1e3|0:now|0}else if(!isNaN(parse=Date.parse(str))){return parse/1e3|0}else if(now){now=new Date(now*1e3)}else{now=new Date}var upperCaseStr=str;str=str.toLowerCase();var __is={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"]};var process=function(m){var ago=m[2]&&m[2]==="ago";var num=(num=m[0]==="last"?-1:1)*(ago?-1:1);switch(m[0]){case"last":case"next":switch(m[1].substring(0,3)){case"yea":now.setFullYear(now.getFullYear()+num);break;case"wee":now.setDate(now.getDate()+num*7);break;case"day":now.setDate(now.getDate()+num);break;case"hou":now.setHours(now.getHours()+num);break;case"min":now.setMinutes(now.getMinutes()+num);break;case"sec":now.setSeconds(now.getSeconds()+num);break;case"mon":if(m[1]==="month"){now.setMonth(now.getMonth()+num);break}default:var day=__is.day[m[1].substring(0,3)];if(typeof day!=="undefined"){var diff=day-now.getDay();if(diff===0){diff=7*num}else if(diff>0){if(m[0]==="last"){diff-=7}}else{if(m[0]==="next"){diff+=7}}now.setDate(now.getDate()+diff);now.setHours(0,0,0,0)}}break;default:if(/\d+/.test(m[0])){num*=parseInt(m[0],10);switch(m[1].substring(0,3)){case"yea":now.setFullYear(now.getFullYear()+num);break;case"mon":now.setMonth(now.getMonth()+num);break;case"wee":now.setDate(now.getDate()+num*7);break;case"day":now.setDate(now.getDate()+num);break;case"hou":now.setHours(now.getHours()+num);break;case"min":now.setMinutes(now.getMinutes()+num);break;case"sec":now.setSeconds(now.getSeconds()+num);break}}else{return false}break}return true};match=str.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);if(match!==null){if(!match[2]){match[2]="00:00:00"}else if(!match[3]){match[2]+=":00"}s=match[1].split(/-/g);s[1]=__is.mon[s[1]-1]||s[1];s[0]=+s[0];s[0]=s[0]>=0&&s[0]<=69?"20"+(s[0]<10?"0"+s[0]:s[0]+""):s[0]>=70&&s[0]<=99?"19"+s[0]:s[0]+"";return parseInt(this.strtotime(s[2]+" "+s[1]+" "+s[0]+" "+match[2])+(match[4]?match[4]/1e3:""),10)}var regex="([+-]?\\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)?";match=str.match(new RegExp(regex,"gi"));if(match===null){try{num=Twig.lib.parseISO8601Date(upperCaseStr);if(num){return num/1e3|0}}catch(err){return false}return false}for(i=0,l=match.length;i0|-(value<0);isHalf=value%1===.5*sgn;f=Math.floor(value);if(isHalf){switch(mode){case"PHP_ROUND_HALF_DOWN":value=f+(sgn<0);break;case"PHP_ROUND_HALF_EVEN":value=f+f%2*sgn;break;case"PHP_ROUND_HALF_ODD":value=f+!(f%2);break;default:value=f+(sgn>0)}}return(isHalf?value:Math.round(value))/m};return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.logic={};Twig.logic.type={if_:"Twig.logic.type.if",endif:"Twig.logic.type.endif",for_:"Twig.logic.type.for",endfor:"Twig.logic.type.endfor",else_:"Twig.logic.type.else",elseif:"Twig.logic.type.elseif",set:"Twig.logic.type.set",setcapture:"Twig.logic.type.setcapture",endset:"Twig.logic.type.endset",filter:"Twig.logic.type.filter",endfilter:"Twig.logic.type.endfilter",block:"Twig.logic.type.block",endblock:"Twig.logic.type.endblock",extends_:"Twig.logic.type.extends",use:"Twig.logic.type.use",include:"Twig.logic.type.include",spaceless:"Twig.logic.type.spaceless",endspaceless:"Twig.logic.type.endspaceless",macro:"Twig.logic.type.macro",endmacro:"Twig.logic.type.endmacro",import_:"Twig.logic.type.import",from:"Twig.logic.type.from"};Twig.logic.definitions=[{type:Twig.logic.type.if_,regex:/^if\s+([^\s].+)$/,next:[Twig.logic.type.else_,Twig.logic.type.elseif,Twig.logic.type.endif],open:true,compile:function(token){var expression=token.match[1];token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;delete token.match;return token},parse:function(token,context,chain){var output="",result=Twig.expression.parse.apply(this,[token.stack,context]);chain=true;if(result){chain=false;output=Twig.parse.apply(this,[token.output,context])}return{chain:chain,output:output}}},{type:Twig.logic.type.elseif,regex:/^elseif\s+([^\s].*)$/,next:[Twig.logic.type.else_,Twig.logic.type.elseif,Twig.logic.type.endif],open:false,compile:function(token){var expression=token.match[1];token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;delete token.match;return token},parse:function(token,context,chain){var output="";if(chain&&Twig.expression.parse.apply(this,[token.stack,context])===true){chain=false;output=Twig.parse.apply(this,[token.output,context])}return{chain:chain,output:output}}},{type:Twig.logic.type.else_,regex:/^else$/,next:[Twig.logic.type.endif,Twig.logic.type.endfor],open:false,parse:function(token,context,chain){var output="";if(chain){output=Twig.parse.apply(this,[token.output,context])}return{chain:chain,output:output}}},{type:Twig.logic.type.endif,regex:/^endif$/,next:[],open:false},{type:Twig.logic.type.for_,regex:/^for\s+([a-zA-Z0-9_,\s]+)\s+in\s+([^\s].*?)(?:\s+if\s+([^\s].*))?$/,next:[Twig.logic.type.else_,Twig.logic.type.endfor],open:true,compile:function(token){var key_value=token.match[1],expression=token.match[2],conditional=token.match[3],kv_split=null;token.key_var=null;token.value_var=null;if(key_value.indexOf(",")>=0){kv_split=key_value.split(",");if(kv_split.length===2){token.key_var=kv_split[0].trim();token.value_var=kv_split[1].trim()}else{throw new Twig.Error("Invalid expression in for loop: "+key_value)}}else{token.value_var=key_value}token.expression=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;if(conditional){token.conditional=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:conditional}]).stack}delete token.match;return token},parse:function(token,context,continue_chain){var result=Twig.expression.parse.apply(this,[token.expression,context]),output=[],len,index=0,keyset,that=this,conditional=token.conditional,buildLoop=function(index,len){var isConditional=conditional!==undefined;return{index:index+1,index0:index,revindex:isConditional?undefined:len-index,revindex0:isConditional?undefined:len-index-1,first:index===0,last:isConditional?undefined:index===len-1,length:isConditional?undefined:len,parent:context}},loop=function(key,value){var inner_context=Twig.lib.copy(context);inner_context[token.value_var]=value;if(token.key_var){inner_context[token.key_var]=key}inner_context.loop=buildLoop(index,len);if(conditional===undefined||Twig.expression.parse.apply(that,[conditional,inner_context])){output.push(Twig.parse.apply(that,[token.output,inner_context]));index+=1}};if(result instanceof Array){len=result.length;Twig.forEach(result,function(value){var key=index;loop(key,value)})}else if(result instanceof Object){if(result._keys!==undefined){keyset=result._keys}else{keyset=Object.keys(result)}len=keyset.length;Twig.forEach(keyset,function(key){if(key==="_keys")return;loop(key,result[key])})}continue_chain=output.length===0;return{chain:continue_chain,output:output.join("")}}},{type:Twig.logic.type.endfor,regex:/^endfor$/,next:[],open:false},{type:Twig.logic.type.set,regex:/^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*(.+)$/,next:[],open:true,compile:function(token){var key=token.match[1].trim(),expression=token.match[2],expression_stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;token.key=key;token.expression=expression_stack;delete token.match;return token},parse:function(token,context,continue_chain){var value=Twig.expression.parse.apply(this,[token.expression,context]),key=token.key;this.context[key]=value;context[key]=value;return{chain:continue_chain,context:context}}},{type:Twig.logic.type.setcapture,regex:/^set\s+([a-zA-Z0-9_,\s]+)$/,next:[Twig.logic.type.endset],open:true,compile:function(token){var key=token.match[1].trim();token.key=key;delete token.match;return token},parse:function(token,context,continue_chain){var value=Twig.parse.apply(this,[token.output,context]),key=token.key;this.context[key]=value;context[key]=value;return{chain:continue_chain,context:context}}},{type:Twig.logic.type.endset,regex:/^endset$/,next:[],open:false},{type:Twig.logic.type.filter,regex:/^filter\s+(.+)$/,next:[Twig.logic.type.endfilter],open:true,compile:function(token){var expression="|"+token.match[1].trim();token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;delete token.match;return token},parse:function(token,context,chain){var unfiltered=Twig.parse.apply(this,[token.output,context]),stack=[{type:Twig.expression.type.string,value:unfiltered}].concat(token.stack);var output=Twig.expression.parse.apply(this,[stack,context]);return{chain:chain,output:output}}},{type:Twig.logic.type.endfilter,regex:/^endfilter$/,next:[],open:false},{type:Twig.logic.type.block,regex:/^block\s+([a-zA-Z0-9_]+)$/,next:[Twig.logic.type.endblock],open:true,compile:function(token){token.block=token.match[1].trim();delete token.match;return token},parse:function(token,context,chain){var block_output="",output="",hasParent=this.blocks[token.block]&&this.blocks[token.block].indexOf(Twig.placeholders.parent)>-1;if(this.blocks[token.block]===undefined||hasParent||context.loop){block_output=Twig.expression.parse.apply(this,[{type:Twig.expression.type.string,value:Twig.parse.apply(this,[token.output,context])},context]); +var Twig=function(Twig){Twig.VERSION="0.7.2";return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.trace=false;Twig.debug=false;Twig.cache=true;Twig.placeholders={parent:"{{|PARENT|}}"};Twig.indexOf=function(arr,searchElement){if(Array.prototype.hasOwnProperty("indexOf")){return arr.indexOf(searchElement)}if(arr===void 0||arr===null){throw new TypeError}var t=Object(arr);var len=t.length>>>0;if(len===0){return-1}var n=0;if(arguments.length>0){n=Number(arguments[1]);if(n!==n){n=0}else if(n!==0&&n!==Infinity&&n!==-Infinity){n=(n>0||-1)*Math.floor(Math.abs(n))}}if(n>=len){return-1}var k=n>=0?n:Math.max(len-Math.abs(n),0);for(;k>>0;if({}.toString.call(callback)!="[object Function]"){throw new TypeError(callback+" is not a function")}if(thisArg){T=thisArg}k=0;while(k=0&&(output.position===null||first_key_position=0){end=pos;found=true}else{throw new Twig.Error("Unable to find closing bracket '"+token_def.close+"'"+" opened near template position "+start)}if(token_def.type===Twig.token.type.comment){break}l=Twig.token.strings.length;for(i=0;i0&&this_str_pos0){found_token=Twig.token.findStart(template);Twig.log.trace("Twig.tokenize: ","Found token: ",found_token);if(found_token.position!==null){if(found_token.position>0){tokens.push({type:Twig.token.type.raw,value:template.substring(0,found_token.position)})}template=template.substr(found_token.position+found_token.def.open.length);error_offset+=found_token.position+found_token.def.open.length;end=Twig.token.findEnd(template,found_token.def,error_offset);Twig.log.trace("Twig.tokenize: ","Token ends at ",end);tokens.push({type:found_token.def.type,value:template.substring(0,end).trim()});if(found_token.def.type==="logic"&&template.substr(end+found_token.def.close.length,1)==="\n"){end+=1}template=template.substr(end+found_token.def.close.length);error_offset+=end+found_token.def.close.length}else{tokens.push({type:Twig.token.type.raw,value:template});template=""}}return tokens};Twig.compile=function(tokens){try{var output=[],stack=[],intermediate_output=[],token=null,logic_token=null,unclosed_token=null,prev_token=null,prev_template=null,tok_output=null,type=null,open=null,next=null;while(tokens.length>0){token=tokens.shift();Twig.log.trace("Compiling token ",token);switch(token.type){case Twig.token.type.raw:if(stack.length>0){intermediate_output.push(token)}else{output.push(token)}break;case Twig.token.type.logic:logic_token=Twig.logic.compile.apply(this,[token]);type=logic_token.type;open=Twig.logic.handler[type].open;next=Twig.logic.handler[type].next;Twig.log.trace("Twig.compile: ","Compiled logic token to ",logic_token," next is: ",next," open is : ",open);if(open!==undefined&&!open){prev_token=stack.pop();prev_template=Twig.logic.handler[prev_token.type];if(Twig.indexOf(prev_template.next,type)<0){throw new Error(type+" not expected after a "+prev_token.type)}prev_token.output=prev_token.output||[];prev_token.output=prev_token.output.concat(intermediate_output);intermediate_output=[];tok_output={type:Twig.token.type.logic,token:prev_token};if(stack.length>0){intermediate_output.push(tok_output)}else{output.push(tok_output)}}if(next!==undefined&&next.length>0){Twig.log.trace("Twig.compile: ","Pushing ",logic_token," to logic stack.");if(stack.length>0){prev_token=stack.pop();prev_token.output=prev_token.output||[];prev_token.output=prev_token.output.concat(intermediate_output);stack.push(prev_token);intermediate_output=[]}stack.push(logic_token)}else if(open!==undefined&&open){tok_output={type:Twig.token.type.logic,token:logic_token};if(stack.length>0){intermediate_output.push(tok_output)}else{output.push(tok_output)}}break;case Twig.token.type.comment:break;case Twig.token.type.output:Twig.expression.compile.apply(this,[token]);if(stack.length>0){intermediate_output.push(token)}else{output.push(token)}break}Twig.log.trace("Twig.compile: "," Output: ",output," Logic Stack: ",stack," Pending Output: ",intermediate_output)}if(stack.length>0){unclosed_token=stack.pop();throw new Error("Unable to find an end tag for "+unclosed_token.type+", expecting one of "+unclosed_token.next)}return output}catch(ex){Twig.log.error("Error compiling twig template "+this.id+": ");if(ex.stack){Twig.log.error(ex.stack)}else{Twig.log.error(ex.toString())}if(this.options.rethrow)throw ex}};Twig.parse=function(tokens,context){try{var output=[],chain=true,that=this;context=context||{};Twig.forEach(tokens,function parseToken(token){Twig.log.debug("Twig.parse: ","Parsing token: ",token);switch(token.type){case Twig.token.type.raw:output.push(token.value);break;case Twig.token.type.logic:var logic_token=token.token,logic=Twig.logic.parse.apply(that,[logic_token,context,chain]);if(logic.chain!==undefined){chain=logic.chain}if(logic.context!==undefined){context=logic.context}if(logic.output!==undefined){output.push(logic.output)}break;case Twig.token.type.comment:break;case Twig.token.type.output:Twig.log.debug("Twig.parse: ","Output token: ",token.stack);output.push(Twig.expression.parse.apply(that,[token.stack,context]));break}});return output.join("")}catch(ex){Twig.log.error("Error parsing twig template "+this.id+": ");if(ex.stack){Twig.log.error(ex.stack)}else{Twig.log.error(ex.toString())}if(this.options.rethrow)throw ex;if(Twig.debug){return ex.toString()}}};Twig.prepare=function(data){var tokens,raw_tokens;Twig.log.debug("Twig.prepare: ","Tokenizing ",data);raw_tokens=Twig.tokenize.apply(this,[data]);Twig.log.debug("Twig.prepare: ","Compiling ",raw_tokens);tokens=Twig.compile.apply(this,[raw_tokens]);Twig.log.debug("Twig.prepare: ","Compiled ",tokens);return tokens};Twig.Templates={registry:{}};Twig.validateId=function(id){if(id==="prototype"){throw new Twig.Error(id+" is not a valid twig identifier")}else if(Twig.Templates.registry.hasOwnProperty(id)){throw new Twig.Error("There is already a template with the ID "+id)}return true};Twig.Templates.save=function(template){if(template.id===undefined){throw new Twig.Error("Unable to save template with no id")}Twig.Templates.registry[template.id]=template};Twig.Templates.load=function(id){if(!Twig.Templates.registry.hasOwnProperty(id)){return null}return Twig.Templates.registry[id]};Twig.Templates.loadRemote=function(location,params,callback,error_callback){var id=params.id,method=params.method,async=params.async,precompiled=params.precompiled,template=null;if(async===undefined)async=true;if(id===undefined){id=location}params.id=id;if(Twig.cache&&Twig.Templates.registry.hasOwnProperty(id)){if(callback){callback(Twig.Templates.registry[id])}return Twig.Templates.registry[id]}if(method=="ajax"){if(typeof XMLHttpRequest=="undefined"){throw new Twig.Error("Unsupported platform: Unable to do remote requests "+"because there is no XMLHTTPRequest implementation")}var xmlhttp=new XMLHttpRequest;xmlhttp.onreadystatechange=function(){var data=null;if(xmlhttp.readyState==4){if(xmlhttp.status==200){Twig.log.debug("Got template ",xmlhttp.responseText);if(precompiled===true){data=JSON.parse(xmlhttp.responseText)}else{data=xmlhttp.responseText}params.url=location;params.data=data;template=new Twig.Template(params);if(callback){callback(template)}}else{if(error_callback){error_callback(xmlhttp)}}}};xmlhttp.open("GET",location,async);xmlhttp.send()}else{(function(){var fs=require("fs"),path=require("path"),data=null,loadTemplateFn=function(err,data){if(err){if(error_callback){error_callback(err)}return}if(precompiled===true){data=JSON.parse(data)}params.data=data;params.path=location;template=new Twig.Template(params);if(callback){callback(template)}};if(async===true){fs.stat(location,function(err,stats){if(err||!stats.isFile())throw new Twig.Error("Unable to find template file "+location);fs.readFile(location,"utf8",loadTemplateFn)})}else{if(!fs.statSync(location).isFile())throw new Twig.Error("Unable to find template file "+location);data=fs.readFileSync(location,"utf8");loadTemplateFn(undefined,data)}})()}if(async===false){return template}else{return true}};function is(type,obj){var clas=Object.prototype.toString.call(obj).slice(8,-1);return obj!==undefined&&obj!==null&&clas===type}Twig.Template=function(params){var data=params.data,id=params.id,blocks=params.blocks,macros=params.macros||{},base=params.base,path=params.path,url=params.url,options=params.options;this.id=id;this.base=base;this.path=path;this.url=url;this.macros=macros;this.options=options;this.reset(blocks);if(is("String",data)){this.tokens=Twig.prepare.apply(this,[data])}else{this.tokens=data}if(id!==undefined){Twig.Templates.save(this)}};Twig.Template.prototype.reset=function(blocks){Twig.log.debug("Twig.Template.reset","Reseting template "+this.id);this.blocks={};this.child={blocks:blocks||{}};this.extend=null};Twig.Template.prototype.render=function(context,params){params=params||{};var output,url;this.context=context||{};this.reset();if(params.blocks){this.blocks=params.blocks}if(params.macros){this.macros=params.macros}output=Twig.parse.apply(this,[this.tokens,this.context]);if(this.extend){var ext_template;if(this.options.allowInlineIncludes){ext_template=Twig.Templates.load(this.extend);if(ext_template){ext_template.options=this.options}}if(!ext_template){url=relativePath(this,this.extend);ext_template=Twig.Templates.loadRemote(url,{method:this.url?"ajax":"fs",base:this.base,async:false,id:url,options:this.options})}this.parent=ext_template;return this.parent.render(this.context,{blocks:this.blocks})}if(params.output=="blocks"){return this.blocks}else if(params.output=="macros"){return this.macros}else{return output}};Twig.Template.prototype.importFile=function(file){var url,sub_template;if(!this.url&&!this.path&&this.options.allowInlineIncludes){sub_template=Twig.Templates.load(file);sub_template.options=this.options;if(sub_template){return sub_template}throw new Twig.Error("Didn't find the inline template by id")}url=relativePath(this,file);sub_template=Twig.Templates.loadRemote(url,{method:this.url?"ajax":"fs",base:this.base,async:false,options:this.options,id:url});return sub_template};Twig.Template.prototype.importBlocks=function(file,override){var sub_template=this.importFile(file),context=this.context,that=this,key;override=override||false;sub_template.render(context);Twig.forEach(Object.keys(sub_template.blocks),function(key){if(override||that.blocks[key]===undefined){that.blocks[key]=sub_template.blocks[key]}})};Twig.Template.prototype.importMacros=function(file){var url=relativePath(this,file);var remoteTemplate=Twig.Templates.loadRemote(url,{method:this.url?"ajax":"fs",async:false,id:url});return remoteTemplate};Twig.Template.prototype.compile=function(options){return Twig.compiler.compile(this,options)};function relativePath(template,file){var base,base_path,sep_chr="/",new_path=[],val;if(template.url){if(typeof template.base!=="undefined"){base=template.base+(template.base.charAt(template.base.length-1)==="/"?"":"/")}else{base=template.url}}else if(template.path){var path=require("path"),sep=path.sep||sep_chr,relative=new RegExp("^\\.{1,2}"+sep.replace("\\","\\\\"));file=file.replace(/\//g,sep);if(template.base!==undefined&&file.match(relative)==null){file=file.replace(template.base,"");base=template.base+sep}else{base=template.path}base=base.replace(sep+sep,sep);sep_chr=sep}else{throw new Twig.Error("Cannot extend an inline template.")}base_path=base.split(sep_chr);base_path.pop();base_path=base_path.concat(file.split(sep_chr));while(base_path.length>0){val=base_path.shift();if(val=="."){}else if(val==".."&&new_path.length>0&&new_path[new_path.length-1]!=".."){new_path.pop()}else{new_path.push(val)}}return new_path.join(sep_chr)}return Twig}(Twig||{});(function(){"use strict";if(!String.prototype.trim){String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}}if(!Object.keys)Object.keys=function(o){if(o!==Object(o)){throw new TypeError("Object.keys called on non-object")}var ret=[],p;for(p in o)if(Object.prototype.hasOwnProperty.call(o,p))ret.push(p);return ret}})();var Twig=function(Twig){Twig.lib={};var sprintf=function(){function get_type(variable){return Object.prototype.toString.call(variable).slice(8,-1).toLowerCase()}function str_repeat(input,multiplier){for(var output=[];multiplier>0;output[--multiplier]=input){}return output.join("")}var str_format=function(){if(!str_format.cache.hasOwnProperty(arguments[0])){str_format.cache[arguments[0]]=str_format.parse(arguments[0])}return str_format.format.call(null,str_format.cache[arguments[0]],arguments)};str_format.format=function(parse_tree,argv){var cursor=1,tree_length=parse_tree.length,node_type="",arg,output=[],i,k,match,pad,pad_character,pad_length;for(i=0;i=0?"+"+arg:arg;pad_character=match[4]?match[4]=="0"?"0":match[4].charAt(1):" ";pad_length=match[6]-String(arg).length;pad=match[6]?str_repeat(pad_character,pad_length):"";output.push(match[5]?arg+pad:pad+arg)}}return output.join("")};str_format.cache={};str_format.parse=function(fmt){var _fmt=fmt,match=[],parse_tree=[],arg_names=0;while(_fmt){if((match=/^[^\x25]+/.exec(_fmt))!==null){parse_tree.push(match[0])}else if((match=/^\x25{2}/.exec(_fmt))!==null){parse_tree.push("%")}else if((match=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt))!==null){if(match[2]){arg_names|=1;var field_list=[],replacement_field=match[2],field_match=[];if((field_match=/^([a-z_][a-z_\d]*)/i.exec(replacement_field))!==null){field_list.push(field_match[1]);while((replacement_field=replacement_field.substring(field_match[0].length))!==""){if((field_match=/^\.([a-z_][a-z_\d]*)/i.exec(replacement_field))!==null){field_list.push(field_match[1])}else if((field_match=/^\[(\d+)\]/.exec(replacement_field))!==null){field_list.push(field_match[1])}else{throw"[sprintf] huh?"}}}else{throw"[sprintf] huh?"}match[2]=field_list}else{arg_names|=2}if(arg_names===3){throw"[sprintf] mixing positional and named placeholders is not (yet) supported"}parse_tree.push(match)}else{throw"[sprintf] huh?"}_fmt=_fmt.substring(match[0].length)}return parse_tree};return str_format}();var vsprintf=function(fmt,argv){argv.unshift(fmt);return sprintf.apply(null,argv)};Twig.lib.sprintf=sprintf;Twig.lib.vsprintf=vsprintf;(function(){var shortDays="Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(",");var fullDays="Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(",");var shortMonths="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(",");var fullMonths="January,February,March,April,May,June,July,August,September,October,November,December".split(",");function getOrdinalFor(intNum){return(intNum=Math.abs(intNum)%100)%10==1&&intNum!=11?"st":intNum%10==2&&intNum!=12?"nd":intNum%10==3&&intNum!=13?"rd":"th"}function getISO8601Year(aDate){var d=new Date(aDate.getFullYear()+1,0,4);if((d-aDate)/864e5<7&&(aDate.getDay()+6)%7<(d.getDay()+6)%7)return d.getFullYear();if(aDate.getMonth()>0||aDate.getDate()>=4)return aDate.getFullYear();return aDate.getFullYear()-((aDate.getDay()+6)%7-aDate.getDate()>2?1:0)}function getISO8601Week(aDate){var d=new Date(getISO8601Year(aDate),0,4);d.setDate(d.getDate()-(d.getDay()+6)%7);return parseInt((aDate-d)/6048e5)+1}Twig.lib.formatDate=function(date,format){if(typeof format!=="string"||/^\s*$/.test(format))return date+"";var jan1st=new Date(date.getFullYear(),0,1);var me=date;return format.replace(/[dDjlNSwzWFmMntLoYyaABgGhHisuU]/g,function(option){switch(option){case"d":return("0"+me.getDate()).replace(/^.+(..)$/,"$1");case"D":return shortDays[me.getDay()];case"j":return me.getDate();case"l":return fullDays[me.getDay()];case"N":return(me.getDay()+6)%7+1;case"S":return getOrdinalFor(me.getDate());case"w":return me.getDay();case"z":return Math.ceil((jan1st-me)/864e5);case"W":return("0"+getISO8601Week(me)).replace(/^.(..)$/,"$1");case"F":return fullMonths[me.getMonth()];case"m":return("0"+(me.getMonth()+1)).replace(/^.+(..)$/,"$1");case"M":return shortMonths[me.getMonth()];case"n":return me.getMonth()+1;case"t":return new Date(me.getFullYear(),me.getMonth()+1,-1).getDate();case"L":return new Date(me.getFullYear(),1,29).getDate()==29?1:0;case"o":return getISO8601Year(me);case"Y":return me.getFullYear();case"y":return(me.getFullYear()+"").replace(/^.+(..)$/,"$1");case"a":return me.getHours()<12?"am":"pm";case"A":return me.getHours()<12?"AM":"PM";case"B":return Math.floor(((me.getUTCHours()+1)%24+me.getUTCMinutes()/60+me.getUTCSeconds()/3600)*1e3/24);case"g":return me.getHours()%12!=0?me.getHours()%12:12;case"G":return me.getHours();case"h":return("0"+(me.getHours()%12!=0?me.getHours()%12:12)).replace(/^.+(..)$/,"$1");case"H":return("0"+me.getHours()).replace(/^.+(..)$/,"$1");case"i":return("0"+me.getMinutes()).replace(/^.+(..)$/,"$1");case"s":return("0"+me.getSeconds()).replace(/^.+(..)$/,"$1");case"u":return me.getMilliseconds();case"U":return me.getTime()/1e3}})}})();Twig.lib.strip_tags=function(input,allowed){allowed=(((allowed||"")+"").toLowerCase().match(/<[a-z][a-z0-9]*>/g)||[]).join("");var tags=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,commentsAndPhpTags=/|<\?(?:php)?[\s\S]*?\?>/gi;return input.replace(commentsAndPhpTags,"").replace(tags,function($0,$1){return allowed.indexOf("<"+$1.toLowerCase()+">")>-1?$0:""})};Twig.lib.parseISO8601Date=function(s){var re=/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+-])(\d\d):(\d\d))/;var d=[];d=s.match(re);if(!d){throw"Couldn't parse ISO 8601 date string '"+s+"'"}var a=[1,2,3,4,5,6,10,11];for(var i in a){d[a[i]]=parseInt(d[a[i]],10)}d[7]=parseFloat(d[7]);var ms=Date.UTC(d[1],d[2]-1,d[3],d[4],d[5],d[6]);if(d[7]>0){ms+=Math.round(d[7]*1e3)}if(d[8]!="Z"&&d[10]){var offset=d[10]*60*60*1e3;if(d[11]){offset+=d[11]*60*1e3}if(d[9]=="-"){ms-=offset}else{ms+=offset}}return new Date(ms)};Twig.lib.strtotime=function(str,now){var i,l,match,s,parse="";str=str.replace(/\s{2,}|^\s|\s$/g," ");str=str.replace(/[\t\r\n]/g,"");if(str==="now"){return now===null||isNaN(now)?(new Date).getTime()/1e3|0:now|0}else if(!isNaN(parse=Date.parse(str))){return parse/1e3|0}else if(now){now=new Date(now*1e3)}else{now=new Date}var upperCaseStr=str;str=str.toLowerCase();var __is={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"]};var process=function(m){var ago=m[2]&&m[2]==="ago";var num=(num=m[0]==="last"?-1:1)*(ago?-1:1);switch(m[0]){case"last":case"next":switch(m[1].substring(0,3)){case"yea":now.setFullYear(now.getFullYear()+num);break;case"wee":now.setDate(now.getDate()+num*7);break;case"day":now.setDate(now.getDate()+num);break;case"hou":now.setHours(now.getHours()+num);break;case"min":now.setMinutes(now.getMinutes()+num);break;case"sec":now.setSeconds(now.getSeconds()+num);break;case"mon":if(m[1]==="month"){now.setMonth(now.getMonth()+num);break}default:var day=__is.day[m[1].substring(0,3)];if(typeof day!=="undefined"){var diff=day-now.getDay();if(diff===0){diff=7*num}else if(diff>0){if(m[0]==="last"){diff-=7}}else{if(m[0]==="next"){diff+=7}}now.setDate(now.getDate()+diff);now.setHours(0,0,0,0)}}break;default:if(/\d+/.test(m[0])){num*=parseInt(m[0],10);switch(m[1].substring(0,3)){case"yea":now.setFullYear(now.getFullYear()+num);break;case"mon":now.setMonth(now.getMonth()+num);break;case"wee":now.setDate(now.getDate()+num*7);break;case"day":now.setDate(now.getDate()+num);break;case"hou":now.setHours(now.getHours()+num);break;case"min":now.setMinutes(now.getMinutes()+num);break;case"sec":now.setSeconds(now.getSeconds()+num);break}}else{return false}break}return true};match=str.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);if(match!==null){if(!match[2]){match[2]="00:00:00"}else if(!match[3]){match[2]+=":00"}s=match[1].split(/-/g);s[1]=__is.mon[s[1]-1]||s[1];s[0]=+s[0];s[0]=s[0]>=0&&s[0]<=69?"20"+(s[0]<10?"0"+s[0]:s[0]+""):s[0]>=70&&s[0]<=99?"19"+s[0]:s[0]+"";return parseInt(this.strtotime(s[2]+" "+s[1]+" "+s[0]+" "+match[2])+(match[4]?match[4]/1e3:""),10)}var regex="([+-]?\\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)?";match=str.match(new RegExp(regex,"gi"));if(match===null){try{num=Twig.lib.parseISO8601Date(upperCaseStr);if(num){return num/1e3|0}}catch(err){return false}return false}for(i=0,l=match.length;i0|-(value<0);isHalf=value%1===.5*sgn;f=Math.floor(value);if(isHalf){switch(mode){case"PHP_ROUND_HALF_DOWN":value=f+(sgn<0);break;case"PHP_ROUND_HALF_EVEN":value=f+f%2*sgn;break;case"PHP_ROUND_HALF_ODD":value=f+!(f%2);break;default:value=f+(sgn>0)}}return(isHalf?value:Math.round(value))/m};return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.logic={};Twig.logic.type={if_:"Twig.logic.type.if",endif:"Twig.logic.type.endif",for_:"Twig.logic.type.for",endfor:"Twig.logic.type.endfor",else_:"Twig.logic.type.else",elseif:"Twig.logic.type.elseif",set:"Twig.logic.type.set",setcapture:"Twig.logic.type.setcapture",endset:"Twig.logic.type.endset",filter:"Twig.logic.type.filter",endfilter:"Twig.logic.type.endfilter",block:"Twig.logic.type.block",endblock:"Twig.logic.type.endblock",extends_:"Twig.logic.type.extends",use:"Twig.logic.type.use",include:"Twig.logic.type.include",spaceless:"Twig.logic.type.spaceless",endspaceless:"Twig.logic.type.endspaceless",macro:"Twig.logic.type.macro",endmacro:"Twig.logic.type.endmacro",import_:"Twig.logic.type.import",from:"Twig.logic.type.from"};Twig.logic.definitions=[{type:Twig.logic.type.if_,regex:/^if\s+([^\s].+)$/,next:[Twig.logic.type.else_,Twig.logic.type.elseif,Twig.logic.type.endif],open:true,compile:function(token){var expression=token.match[1];token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;delete token.match;return token},parse:function(token,context,chain){var output="",result=Twig.expression.parse.apply(this,[token.stack,context]);chain=true;if(result){chain=false;output=Twig.parse.apply(this,[token.output,context])}return{chain:chain,output:output}}},{type:Twig.logic.type.elseif,regex:/^elseif\s+([^\s].*)$/,next:[Twig.logic.type.else_,Twig.logic.type.elseif,Twig.logic.type.endif],open:false,compile:function(token){var expression=token.match[1];token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;delete token.match;return token},parse:function(token,context,chain){var output="";if(chain&&Twig.expression.parse.apply(this,[token.stack,context])===true){chain=false;output=Twig.parse.apply(this,[token.output,context])}return{chain:chain,output:output}}},{type:Twig.logic.type.else_,regex:/^else$/,next:[Twig.logic.type.endif,Twig.logic.type.endfor],open:false,parse:function(token,context,chain){var output="";if(chain){output=Twig.parse.apply(this,[token.output,context])}return{chain:chain,output:output}}},{type:Twig.logic.type.endif,regex:/^endif$/,next:[],open:false},{type:Twig.logic.type.for_,regex:/^for\s+([a-zA-Z0-9_,\s]+)\s+in\s+([^\s].*?)(?:\s+if\s+([^\s].*))?$/,next:[Twig.logic.type.else_,Twig.logic.type.endfor],open:true,compile:function(token){var key_value=token.match[1],expression=token.match[2],conditional=token.match[3],kv_split=null;token.key_var=null;token.value_var=null;if(key_value.indexOf(",")>=0){kv_split=key_value.split(",");if(kv_split.length===2){token.key_var=kv_split[0].trim();token.value_var=kv_split[1].trim()}else{throw new Twig.Error("Invalid expression in for loop: "+key_value)}}else{token.value_var=key_value}token.expression=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;if(conditional){token.conditional=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:conditional}]).stack}delete token.match;return token},parse:function(token,context,continue_chain){var result=Twig.expression.parse.apply(this,[token.expression,context]),output=[],len,index=0,keyset,that=this,conditional=token.conditional,buildLoop=function(index,len){var isConditional=conditional!==undefined;return{index:index+1,index0:index,revindex:isConditional?undefined:len-index,revindex0:isConditional?undefined:len-index-1,first:index===0,last:isConditional?undefined:index===len-1,length:isConditional?undefined:len,parent:context}},loop=function(key,value){var inner_context=Twig.lib.copy(context);inner_context[token.value_var]=value;if(token.key_var){inner_context[token.key_var]=key}inner_context.loop=buildLoop(index,len);if(conditional===undefined||Twig.expression.parse.apply(that,[conditional,inner_context])){output.push(Twig.parse.apply(that,[token.output,inner_context]));index+=1}};if(result instanceof Array){len=result.length;Twig.forEach(result,function(value){var key=index;loop(key,value)})}else if(result instanceof Object){if(result._keys!==undefined){keyset=result._keys}else{keyset=Object.keys(result)}len=keyset.length;Twig.forEach(keyset,function(key){if(key==="_keys")return;loop(key,result[key])})}continue_chain=output.length===0;return{chain:continue_chain,output:output.join("")}}},{type:Twig.logic.type.endfor,regex:/^endfor$/,next:[],open:false},{type:Twig.logic.type.set,regex:/^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*(.+)$/,next:[],open:true,compile:function(token){var key=token.match[1].trim(),expression=token.match[2],expression_stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;token.key=key;token.expression=expression_stack;delete token.match;return token},parse:function(token,context,continue_chain){var value=Twig.expression.parse.apply(this,[token.expression,context]),key=token.key;this.context[key]=value;context[key]=value;return{chain:continue_chain,context:context}}},{type:Twig.logic.type.setcapture,regex:/^set\s+([a-zA-Z0-9_,\s]+)$/,next:[Twig.logic.type.endset],open:true,compile:function(token){var key=token.match[1].trim();token.key=key;delete token.match;return token},parse:function(token,context,continue_chain){var value=Twig.parse.apply(this,[token.output,context]),key=token.key;this.context[key]=value;context[key]=value;return{chain:continue_chain,context:context}}},{type:Twig.logic.type.endset,regex:/^endset$/,next:[],open:false},{type:Twig.logic.type.filter,regex:/^filter\s+(.+)$/,next:[Twig.logic.type.endfilter],open:true,compile:function(token){var expression="|"+token.match[1].trim();token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;delete token.match;return token},parse:function(token,context,chain){var unfiltered=Twig.parse.apply(this,[token.output,context]),stack=[{type:Twig.expression.type.string,value:unfiltered}].concat(token.stack);var output=Twig.expression.parse.apply(this,[stack,context]);return{chain:chain,output:output}}},{type:Twig.logic.type.endfilter,regex:/^endfilter$/,next:[],open:false},{type:Twig.logic.type.block,regex:/^block\s+([a-zA-Z0-9_]+)$/,next:[Twig.logic.type.endblock],open:true,compile:function(token){token.block=token.match[1].trim();delete token.match;return token},parse:function(token,context,chain){var block_output="",output="",hasParent=this.blocks[token.block]&&this.blocks[token.block].indexOf(Twig.placeholders.parent)>-1;if(this.blocks[token.block]===undefined||hasParent||context.loop){block_output=Twig.expression.parse.apply(this,[{type:Twig.expression.type.string,value:Twig.parse.apply(this,[token.output,context])},context]); if(hasParent){this.blocks[token.block]=this.blocks[token.block].replace(Twig.placeholders.parent,block_output)}else{this.blocks[token.block]=block_output}}if(this.child.blocks[token.block]){output=this.child.blocks[token.block]}else{output=this.blocks[token.block]}return{chain:chain,output:output}}},{type:Twig.logic.type.endblock,regex:/^endblock(?:\s+([a-zA-Z0-9_]+))?$/,next:[],open:false},{type:Twig.logic.type.extends_,regex:/^extends\s+(.+)$/,next:[],open:true,compile:function(token){var expression=token.match[1].trim();delete token.match;token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;return token},parse:function(token,context,chain){var file=Twig.expression.parse.apply(this,[token.stack,context]);this.extend=file;return{chain:chain,output:""}}},{type:Twig.logic.type.use,regex:/^use\s+(.+)$/,next:[],open:true,compile:function(token){var expression=token.match[1].trim();delete token.match;token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;return token},parse:function(token,context,chain){var file=Twig.expression.parse.apply(this,[token.stack,context]);this.importBlocks(file);return{chain:chain,output:""}}},{type:Twig.logic.type.include,regex:/^include\s+(ignore missing\s+)?(.+?)\s*(?:with\s+(.+?))?\s*(only)?$/,next:[],open:true,compile:function(token){var match=token.match,includeMissing=match[1]!==undefined,expression=match[2].trim(),withContext=match[3],only=match[4]!==undefined&&match[4].length;delete token.match;token.only=only;token.includeMissing=includeMissing;token.stack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:expression}]).stack;if(withContext!==undefined){token.withStack=Twig.expression.compile.apply(this,[{type:Twig.expression.type.expression,value:withContext.trim()}]).stack}return token},parse:function(token,context,chain){var innerContext={},withContext,i,template;if(!token.only){for(i in context){if(context.hasOwnProperty(i))innerContext[i]=context[i]}}if(token.withStack!==undefined){withContext=Twig.expression.parse.apply(this,[token.withStack,context]);for(i in withContext){if(withContext.hasOwnProperty(i))innerContext[i]=withContext[i]}}var file=Twig.expression.parse.apply(this,[token.stack,innerContext]);template=this.importFile(file);return{chain:chain,output:template.render(innerContext)}}},{type:Twig.logic.type.spaceless,regex:/^spaceless$/,next:[Twig.logic.type.endspaceless],open:true,parse:function(token,context,chain){var unfiltered=Twig.parse.apply(this,[token.output,context]),rBetweenTagSpaces=/>\s+<").trim();return{chain:chain,output:output}}},{type:Twig.logic.type.endspaceless,regex:/^endspaceless$/,next:[],open:false},{type:Twig.logic.type.macro,regex:/^macro\s+([a-zA-Z0-9_]+)\s?\((([a-zA-Z0-9_]+(,\s?)?)*)\)$/,next:[Twig.logic.type.endmacro],open:true,compile:function(token){var macroName=token.match[1],parameters=token.match[2].split(/[ ,]+/);for(var i=0;i0){Twig.logic.extend(Twig.logic.definitions.shift())}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];if(token_template.compile){token=token_template.compile.apply(this,[token]);Twig.log.trace("Twig.logic.compile: ","Compiled logic token to ",token)}return token};Twig.logic.tokenize=function(expression){var token={},token_template_type=null,token_type=null,token_regex=null,regex_array=null,regex=null,match=null;expression=expression.trim();for(token_template_type in Twig.logic.handler){if(Twig.logic.handler.hasOwnProperty(token_template_type)){token_type=Twig.logic.handler[token_template_type].type;token_regex=Twig.logic.handler[token_template_type].regex;regex_array=[];if(token_regex instanceof Array){regex_array=token_regex}else{regex_array.push(token_regex)}while(regex_array.length>0){regex=regex_array.shift();match=regex.exec(expression.trim());if(match!==null){token.type=token_type;token.match=match;Twig.log.trace("Twig.logic.tokenize: ","Matched a ",token_type," regular expression of ",match);return token}}}}throw new Twig.Error("Unable to parse '"+expression.trim()+"'")};Twig.logic.parse=function(token,context,chain){var output="",token_template;context=context||{};Twig.log.debug("Twig.logic.parse: ","Parsing logic token ",token);token_template=Twig.logic.handler[token.type];if(token_template.parse){output=token_template.parse.apply(this,[token,context,chain])}return output};return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.expression={};Twig.expression.reservedWords=["true","false","null","_context"];Twig.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",context:"Twig.expression.type.context",test:"Twig.expression.type.test"};Twig.expression.set={operations:[Twig.expression.type.filter,Twig.expression.type.operator.unary,Twig.expression.type.operator.binary,Twig.expression.type.array.end,Twig.expression.type.object.end,Twig.expression.type.parameter.end,Twig.expression.type.comma,Twig.expression.type.test],expressions:[Twig.expression.type._function,Twig.expression.type.bool,Twig.expression.type.string,Twig.expression.type.variable,Twig.expression.type.number,Twig.expression.type._null,Twig.expression.type.context,Twig.expression.type.parameter.start,Twig.expression.type.array.start,Twig.expression.type.object.start]};Twig.expression.set.operations_extended=Twig.expression.set.operations.concat([Twig.expression.type.key.period,Twig.expression.type.key.brackets]);Twig.expression.fn={compile:{push:function(token,stack,output){output.push(token)},push_both:function(token,stack,output){output.push(token);stack.push(token)}},parse:{push:function(token,stack,context){stack.push(token)},push_value:function(token,stack,context){stack.push(token.value)}}};Twig.expression.definitions=[{type:Twig.expression.type.test,regex:/^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*)/,next:Twig.expression.set.operations.concat([Twig.expression.type.parameter.start]),compile:function(token,stack,output){token.filter=token.match[2];token.modifier=token.match[1];delete token.match;delete token.value;output.push(token)},parse:function(token,stack,context){var value=stack.pop(),params=token.params&&Twig.expression.parse.apply(this,[token.params,context]),result=Twig.test(token.filter,value,params);if(token.modifier=="not"){stack.push(!result)}else{stack.push(result)}}},{type:Twig.expression.type.comma,regex:/^,/,next:Twig.expression.set.expressions.concat([Twig.expression.type.array.end,Twig.expression.type.object.end]),compile:function(token,stack,output){var i=stack.length-1,stack_token;delete token.match;delete token.value;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||stack_token.type===Twig.expression.type.array.start){stack.push(stack_token);break}output.push(stack_token)}output.push(token)}},{type:Twig.expression.type.operator.binary,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;token.value=token.value.trim();var value=token.value,operator=Twig.expression.operator.lookup(value,token);Twig.log.trace("Twig.expression.compile: ","Operator: ",operator," from ",value);while(stack.length>0&&(stack[stack.length-1].type==Twig.expression.type.operator.unary||stack[stack.length-1].type==Twig.expression.type.operator.binary)&&(operator.associativity===Twig.expression.operator.leftToRight&&operator.precidence>=stack[stack.length-1].precidence||operator.associativity===Twig.expression.operator.rightToLeft&&operator.precidence>stack[stack.length-1].precidence)){var temp=stack.pop();output.push(temp)}if(value===":"){if(stack[stack.length-1]&&stack[stack.length-1].value==="?"){}else{var key_token=output.pop();if(key_token.type===Twig.expression.type.string||key_token.type===Twig.expression.type.variable||key_token.type===Twig.expression.type.number){token.key=key_token.value}else{throw new Twig.Error("Unexpected value before ':' of "+key_token.type+" = "+key_token.value)}output.push(token);return}}else{stack.push(operator)}},parse:function(token,stack,context){if(token.key){stack.push(token)}else{Twig.expression.operator.parse(token.value,stack)}}},{type:Twig.expression.type.operator.unary,regex:/(^not\s+)/,next:Twig.expression.set.expressions,compile:function(token,stack,output){delete token.match;token.value=token.value.trim();var value=token.value,operator=Twig.expression.operator.lookup(value,token);Twig.log.trace("Twig.expression.compile: ","Operator: ",operator," from ",value);while(stack.length>0&&(stack[stack.length-1].type==Twig.expression.type.operator.unary||stack[stack.length-1].type==Twig.expression.type.operator.binary)&&(operator.associativity===Twig.expression.operator.leftToRight&&operator.precidence>=stack[stack.length-1].precidence||operator.associativity===Twig.expression.operator.rightToLeft&&operator.precidence>stack[stack.length-1].precidence)){var temp=stack.pop();output.push(temp)}stack.push(operator)},parse:function(token,stack,context){Twig.expression.operator.parse(token.value,stack)}},{type:Twig.expression.type.string,regex:/^(["'])(?:(?=(\\?))\2.)*?\1/,next:Twig.expression.set.operations,compile:function(token,stack,output){var value=token.value;delete token.match;if(value.substring(0,1)==='"'){value=value.replace('\\"','"')}else{value=value.replace("\\'","'")}token.value=value.substring(1,value.length-1).replace(/\\n/g,"\n").replace(/\\r/g,"\r");Twig.log.trace("Twig.expression.compile: ","String value: ",token.value);output.push(token)},parse:Twig.expression.fn.parse.push_value},{type:Twig.expression.type.parameter.start,regex:/^\(/,next:Twig.expression.set.expressions.concat([Twig.expression.type.parameter.end]),compile:Twig.expression.fn.compile.push_both,parse:Twig.expression.fn.parse.push},{type:Twig.expression.type.parameter.end,regex:/^\)/,next:Twig.expression.set.operations_extended,compile:function(token,stack,output){var stack_token,end_token=token;stack_token=stack.pop();while(stack.length>0&&stack_token.type!=Twig.expression.type.parameter.start){output.push(stack_token);stack_token=stack.pop()}var param_stack=[];while(token.type!==Twig.expression.type.parameter.start){param_stack.unshift(token);token=output.pop()}param_stack.unshift(token);var is_expression=false;token=output[output.length-1];if(token===undefined||token.type!==Twig.expression.type._function&&token.type!==Twig.expression.type.filter&&token.type!==Twig.expression.type.test&&token.type!==Twig.expression.type.key.brackets&&token.type!==Twig.expression.type.key.period){end_token.expression=true;param_stack.pop();param_stack.shift();end_token.params=param_stack;output.push(end_token)}else{end_token.expression=false;token.params=param_stack}},parse:function(token,stack,context){var new_array=[],array_ended=false,value=null;if(token.expression){value=Twig.expression.parse.apply(this,[token.params,context]);stack.push(value)}else{while(stack.length>0){value=stack.pop();if(value&&value.type&&value.type==Twig.expression.type.parameter.start){array_ended=true;break}new_array.unshift(value)}if(!array_ended){throw new Twig.Error("Expected end of parameter set.")}stack.push(new_array)}}},{type:Twig.expression.type.array.start,regex:/^\[/,next:Twig.expression.set.expressions.concat([Twig.expression.type.array.end]),compile:Twig.expression.fn.compile.push_both,parse:Twig.expression.fn.parse.push},{type:Twig.expression.type.array.end,regex:/^\]/,next:Twig.expression.set.operations_extended,compile:function(token,stack,output){var i=stack.length-1,stack_token;for(;i>=0;i--){stack_token=stack.pop();if(stack_token.type===Twig.expression.type.array.start){break}output.push(stack_token)}output.push(token)},parse:function(token,stack,context){var new_array=[],array_ended=false,value=null;while(stack.length>0){value=stack.pop();if(value.type&&value.type==Twig.expression.type.array.start){array_ended=true;break}new_array.unshift(value)}if(!array_ended){throw new Twig.Error("Expected end of array.")}stack.push(new_array)}},{type:Twig.expression.type.object.start,regex:/^\{/,next:Twig.expression.set.expressions.concat([Twig.expression.type.object.end]),compile:Twig.expression.fn.compile.push_both,parse:Twig.expression.fn.parse.push},{type:Twig.expression.type.object.end,regex:/^\}/,next:Twig.expression.set.operations_extended,compile:function(token,stack,output){var i=stack.length-1,stack_token;for(;i>=0;i--){stack_token=stack.pop();if(stack_token&&stack_token.type===Twig.expression.type.object.start){break}output.push(stack_token)}output.push(token)},parse:function(end_token,stack,context){var new_object={},object_ended=false,token=null,token_key=null,has_value=false,value=null;while(stack.length>0){token=stack.pop();if(token&&token.type&&token.type===Twig.expression.type.object.start){object_ended=true;break}if(token&&token.type&&(token.type===Twig.expression.type.operator.binary||token.type===Twig.expression.type.operator.unary)&&token.key){if(!has_value){throw new Twig.Error("Missing value for key '"+token.key+"' in object definition.")}new_object[token.key]=value;if(new_object._keys===undefined)new_object._keys=[];new_object._keys.unshift(token.key);value=null;has_value=false}else{has_value=true;value=token}}if(!object_ended){throw new Twig.Error("Unexpected end of object.")}stack.push(new_object)}},{type:Twig.expression.type.filter,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){token.value=token.match[1];output.push(token)},parse:function(token,stack,context){var input=stack.pop(),params=token.params&&Twig.expression.parse.apply(this,[token.params,context]);stack.push(Twig.filter.apply(this,[token.value,input,params]))}},{type:Twig.expression.type._function,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;delete token.match;delete token.value;output.push(token)},parse:function(token,stack,context){var params=token.params&&Twig.expression.parse.apply(this,[token.params,context]),fn=token.fn,value;if(Twig.functions[fn]){value=Twig.functions[fn].apply(this,params)}else if(typeof context[fn]=="function"){value=context[fn].apply(context,params)}else{throw new Twig.Error(fn+" function does not exist and is not defined in the context")}stack.push(value)}},{type:Twig.expression.type.variable,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.indexOf(Twig.expression.reservedWords,match[0])<0},parse:function(token,stack,context){var value=Twig.expression.resolve(context[token.value],context);stack.push(value)}},{type:Twig.expression.type.key.period,regex:/^\.([a-zA-Z0-9_]+)/,next:Twig.expression.set.operations_extended.concat([Twig.expression.type.parameter.start]),compile:function(token,stack,output){token.key=token.match[1];delete token.match;delete token.value;output.push(token)},parse:function(token,stack,context){var params=token.params&&Twig.expression.parse.apply(this,[token.params,context]),key=token.key,object=stack.pop(),value;if(object===null||object===undefined){if(this.options.strict_variables){throw new Twig.Error("Can't access a key "+key+" on an null or undefined object.")}else{return null}}var capitalize=function(value){return value.substr(0,1).toUpperCase()+value.substr(1)};if(typeof object==="object"&&key in object){value=object[key]}else if(object["get"+capitalize(key)]!==undefined){value=object["get"+capitalize(key)]}else if(object["is"+capitalize(key)]!==undefined){value=object["is"+capitalize(key)]}else{value=null}stack.push(Twig.expression.resolve(value,object,params))}},{type:Twig.expression.type.key.brackets,regex:/^\[([^\]]*)\]/,next:Twig.expression.set.operations_extended.concat([Twig.expression.type.parameter.start]),compile:function(token,stack,output){var match=token.match[1];delete token.value;delete token.match;token.stack=Twig.expression.compile({value:match}).stack;output.push(token)},parse:function(token,stack,context){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;if(object===null||object===undefined){if(this.options.strict_variables){throw new Twig.Error("Can't access a key "+key+" on an null or undefined object.")}else{return null}}if(typeof object==="object"&&key in object){value=object[key]}else{value=null}stack.push(Twig.expression.resolve(value,object,params))}},{type:Twig.expression.type._null,regex:/^null/,next:Twig.expression.set.operations,compile:function(token,stack,output){delete token.match;token.value=null;output.push(token)},parse:Twig.expression.fn.parse.push_value},{type:Twig.expression.type.context,regex:/^_context/,next:Twig.expression.set.operations_extended.concat([Twig.expression.type.parameter.start]),compile:Twig.expression.fn.compile.push,parse:function(token,stack,context){stack.push(context)}},{type:Twig.expression.type.number,regex:/^\-?\d+(\.\d+)?/,next:Twig.expression.set.operations,compile:function(token,stack,output){token.value=Number(token.value);output.push(token)},parse:Twig.expression.fn.parse.push_value},{type:Twig.expression.type.bool,regex:/^(true|false)/,next:Twig.expression.set.operations,compile:function(token,stack,output){token.value=token.match[0]=="true";delete token.match;output.push(token)},parse:Twig.expression.fn.parse.push_value}];Twig.expression.resolve=function(value,context,params){if(typeof value=="function"){return value.apply(context,params||[])}else{return value}};Twig.expression.handler={};Twig.expression.extendType=function(type){Twig.expression.type[type]="Twig.expression.type."+type};Twig.expression.extend=function(definition){if(!definition.type){throw new Twig.Error("Unable to extend logic definition. No type provided for "+definition)}Twig.expression.handler[definition.type]=definition};while(Twig.expression.definitions.length>0){Twig.expression.extend(Twig.expression.definitions.shift())}Twig.expression.tokenize=function(expression){var tokens=[],exp_offset=0,next=null,type,regex,regex_array,token_next,match_found,invalid_matches=[],match_function;match_function=function(){var match=Array.prototype.slice.apply(arguments),string=match.pop(),offset=match.pop();Twig.log.trace("Twig.expression.tokenize","Matched a ",type," regular expression of ",match);if(next&&Twig.indexOf(next,type)<0){invalid_matches.push(type+" cannot follow a "+tokens[tokens.length-1].type+" at template:"+exp_offset+" near '"+match[0].substring(0,20)+"...'");return match[0]}if(Twig.expression.handler[type].validate&&!Twig.expression.handler[type].validate(match,tokens)){return match[0]}invalid_matches=[];tokens.push({type:type,value:match[0],match:match});match_found=true;next=token_next;exp_offset+=match[0].length;if(Twig.expression.handler[type].transform){return Twig.expression.handler[type].transform(match,tokens)}return""};Twig.log.debug("Twig.expression.tokenize","Tokenizing expression ",expression);while(expression.length>0){expression=expression.trim();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;if(regex instanceof Array){regex_array=regex}else{regex_array=[regex]}match_found=false;while(regex_array.length>0){regex=regex_array.pop();expression=expression.replace(regex,match_function)}if(match_found){break}}}if(!match_found){if(invalid_matches.length>0){throw new Twig.Error(invalid_matches.join(" OR "))}else{throw new Twig.Error("Unable to parse '"+expression+"' at template position"+exp_offset)}}}Twig.log.trace("Twig.expression.tokenize","Tokenized to ",tokens);return tokens};Twig.expression.compile=function(raw_token){var expression=raw_token.value,tokens=Twig.expression.tokenize(expression),token=null,output=[],stack=[],token_template=null;Twig.log.trace("Twig.expression.compile: ","Compiling ",expression);while(tokens.length>0){token=tokens.shift();token_template=Twig.expression.handler[token.type];Twig.log.trace("Twig.expression.compile: ","Compiling ",token);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)}while(stack.length>0){output.push(stack.pop())}Twig.log.trace("Twig.expression.compile: ","Final output is",output);raw_token.stack=output;delete raw_token.value;return raw_token};Twig.expression.parse=function(tokens,context){var that=this;if(!(tokens instanceof Array)){tokens=[tokens]}var stack=[],token_template=null;Twig.forEach(tokens,function(token){token_template=Twig.expression.handler[token.type];token_template.parse&&token_template.parse.apply(that,[token,stack,context])});return stack.pop()};return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.expression.operator={leftToRight:"leftToRight",rightToLeft:"rightToLeft"};var containment=function(a,b){if(b.indexOf!==undefined){return a===b||a!==""&&b.indexOf(a)>-1}else{var el;for(el in b){if(b.hasOwnProperty(el)&&b[el]===a){return true}}return false}};Twig.expression.operator.lookup=function(operator,token){switch(operator){case"..":case"not in":case"in":token.precidence=20;token.associativity=Twig.expression.operator.leftToRight;break;case",":token.precidence=18;token.associativity=Twig.expression.operator.leftToRight;break;case"?":case":":token.precidence=16;token.associativity=Twig.expression.operator.rightToLeft;break;case"or":token.precidence=14;token.associativity=Twig.expression.operator.leftToRight;break;case"and":token.precidence=13;token.associativity=Twig.expression.operator.leftToRight;break;case"==":case"!=":token.precidence=9;token.associativity=Twig.expression.operator.leftToRight;break;case"<":case"<=":case">":case">=":token.precidence=8;token.associativity=Twig.expression.operator.leftToRight;break;case"~":case"+":case"-":token.precidence=6;token.associativity=Twig.expression.operator.leftToRight;break;case"//":case"**":case"*":case"/":case"%":token.precidence=5;token.associativity=Twig.expression.operator.leftToRight;break;case"not":token.precidence=3;token.associativity=Twig.expression.operator.rightToLeft;break;default:throw new Twig.Error(operator+" is an unknown operator.")}token.operator=operator;return token};Twig.expression.operator.parse=function(operator,stack){Twig.log.trace("Twig.expression.operator.parse: ","Handling ",operator);var a,b,c;switch(operator){case":":break;case"?":c=stack.pop();b=stack.pop();a=stack.pop();if(a){stack.push(b)}else{stack.push(c)}break;case"+":b=parseFloat(stack.pop());a=parseFloat(stack.pop());stack.push(a+b);break;case"-":b=parseFloat(stack.pop());a=parseFloat(stack.pop());stack.push(a-b);break;case"*":b=parseFloat(stack.pop());a=parseFloat(stack.pop());stack.push(a*b);break;case"/":b=parseFloat(stack.pop());a=parseFloat(stack.pop());stack.push(a/b);break;case"//":b=parseFloat(stack.pop());a=parseFloat(stack.pop());stack.push(parseInt(a/b));break;case"%":b=parseFloat(stack.pop());a=parseFloat(stack.pop());stack.push(a%b);break;case"~":b=stack.pop();a=stack.pop();stack.push((a!==undefined?a.toString():"")+(b!==undefined?b.toString():""));break;case"not":case"!":stack.push(!stack.pop());break;case"<":b=stack.pop();a=stack.pop();stack.push(a":b=stack.pop();a=stack.pop();stack.push(a>b);break;case">=":b=stack.pop();a=stack.pop();stack.push(a>=b);break;case"===":b=stack.pop();a=stack.pop();stack.push(a===b);break;case"==":b=stack.pop();a=stack.pop();stack.push(a==b);break;case"!==":b=stack.pop();a=stack.pop();stack.push(a!==b);break;case"!=":b=stack.pop();a=stack.pop();stack.push(a!=b);break;case"or":b=stack.pop();a=stack.pop();stack.push(a||b);break;case"and":b=stack.pop();a=stack.pop();stack.push(a&&b);break;case"**":b=stack.pop();a=stack.pop();stack.push(Math.pow(a,b));break;case"not in":b=stack.pop();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.")}};return Twig}(Twig||{});var Twig=function(Twig){function is(type,obj){var clas=Object.prototype.toString.call(obj).slice(8,-1);return obj!==undefined&&obj!==null&&clas===type}Twig.filters={upper:function(value){if(typeof value!=="string"){return value}return value.toUpperCase()},lower:function(value){if(typeof value!=="string"){return value}return value.toLowerCase()},capitalize:function(value){if(typeof value!=="string"){return value}return value.substr(0,1).toUpperCase()+value.toLowerCase().substr(1)},title:function(value){if(typeof value!=="string"){return value}return value.toLowerCase().replace(/(^|\s)([a-z])/g,function(m,p1,p2){return p1+p2.toUpperCase()})},length:function(value){if(Twig.lib.is("Array",value)||typeof value==="string"){return value.length}else if(Twig.lib.is("Object",value)){if(value._keys===undefined){return Object.keys(value).length}else{return value._keys.length}}else{return 0}},reverse:function(value){if(is("Array",value)){return value.reverse()}else if(is("String",value)){return value.split("").reverse().join("")}else if(value instanceof Object){var keys=value._keys||Object.keys(value).reverse();value._keys=keys;return value}},sort:function(value){if(is("Array",value)){return value.sort()}else if(value instanceof Object){delete value._keys;var keys=Object.keys(value),sorted_keys=keys.sort(function(a,b){return value[a]>value[b]});value._keys=sorted_keys;return value}},keys:function(value){if(value===undefined||value===null){return}var keyset=value._keys||Object.keys(value),output=[];Twig.forEach(keyset,function(key){if(key==="_keys")return;if(value.hasOwnProperty(key)){output.push(key)}});return output},url_encode:function(value){if(value===undefined||value===null){return}return encodeURIComponent(value)},join:function(value,params){if(value===undefined||value===null){return}var join_str="",output=[],keyset=null;if(params&¶ms[0]){join_str=params[0]}if(value instanceof Array){output=value}else{keyset=value._keys||Object.keys(value);Twig.forEach(keyset,function(key){if(key==="_keys")return;if(value.hasOwnProperty(key)){output.push(value[key])}})}return output.join(join_str)},"default":function(value,params){if(params===undefined||params.length!==1){throw new Twig.Error("default filter expects one argument")}if(value===undefined||value===null||value===""){return params[0]}else{return value}},json_encode:function(value){if(value&&value.hasOwnProperty("_keys")){delete value._keys}if(value===undefined||value===null){return"null"}return JSON.stringify(value)},merge:function(value,params){var obj=[],arr_index=0,keyset=[];if(!(value instanceof Array)){obj={}}else{Twig.forEach(params,function(param){if(!(param instanceof Array)){obj={}}})}if(!(obj instanceof Array)){obj._keys=[]}if(value instanceof Array){Twig.forEach(value,function(val){if(obj._keys)obj._keys.push(arr_index);obj[arr_index]=val;arr_index++})}else{keyset=value._keys||Object.keys(value);Twig.forEach(keyset,function(key){obj[key]=value[key];obj._keys.push(key);var int_key=parseInt(key,10);if(!isNaN(int_key)&&int_key>=arr_index){arr_index=int_key+1}})}Twig.forEach(params,function(param){if(param instanceof Array){Twig.forEach(param,function(val){if(obj._keys)obj._keys.push(arr_index);obj[arr_index]=val;arr_index++})}else{keyset=param._keys||Object.keys(param);Twig.forEach(keyset,function(key){if(!obj[key])obj._keys.push(key); obj[key]=param[key];var int_key=parseInt(key,10);if(!isNaN(int_key)&&int_key>=arr_index){arr_index=int_key+1}})}});if(params.length===0){throw new Twig.Error("Filter merge expects at least one parameter")}return obj},date:function(value,params){if(value===undefined||value===null){return}var date=Twig.functions.date(value);return Twig.lib.formatDate(date,params[0])},date_modify:function(value,params){if(value===undefined||value===null){return}if(params===undefined||params.length!==1){throw new Twig.Error("date_modify filter expects 1 argument")}var modifyText=params[0],time;if(Twig.lib.is("Date",value)){time=Twig.lib.strtotime(modifyText,value.getTime()/1e3)}if(Twig.lib.is("String",value)){time=Twig.lib.strtotime(modifyText,Twig.lib.strtotime(value))}if(Twig.lib.is("Number",value)){time=Twig.lib.strtotime(modifyText,value)}return new Date(time*1e3)},replace:function(value,params){if(value===undefined||value===null){return}var pairs=params[0],tag;for(tag in pairs){if(pairs.hasOwnProperty(tag)&&tag!=="_keys"){value=Twig.lib.replaceAll(value,tag,pairs[tag])}}return value},format:function(value,params){if(value===undefined||value===null){return}return Twig.lib.vsprintf(value,params)},striptags:function(value){if(value===undefined||value===null){return}return Twig.lib.strip_tags(value)},escape:function(value){if(value===undefined||value===null){return}return value.toString().replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},e:function(value){return Twig.filters.escape(value)},nl2br:function(value){if(value===undefined||value===null){return}var linebreak_tag="BACKSLASH_n_replace",br="
    "+linebreak_tag;value=Twig.filters.escape(value).replace(/\r\n/g,br).replace(/\r/g,br).replace(/\n/g,br);return Twig.lib.replaceAll(value,linebreak_tag,"\n")},number_format:function(value,params){var number=value,decimals=params&¶ms[0]?params[0]:undefined,dec=params&¶ms[1]!==undefined?params[1]:".",sep=params&¶ms[2]!==undefined?params[2]:",";number=(number+"").replace(/[^0-9+\-Ee.]/g,"");var n=!isFinite(+number)?0:+number,prec=!isFinite(+decimals)?0:Math.abs(decimals),s="",toFixedFix=function(n,prec){var k=Math.pow(10,prec);return""+Math.round(n*k)/k};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)}if((s[1]||"").length=0;i--){if(whitespace.indexOf(str.charAt(i))===-1){str=str.substring(0,i+1);break}}return whitespace.indexOf(str.charAt(0))===-1?str:""},slice:function(value,params){if(value===undefined||value===null){return}if(params===undefined||params.length<1){throw new Twig.Error("slice filter expects at least 1 argument")}var start=params[0]||0;var length=params.length>1?params[1]:value.length;var startIndex=start>=0?start:Math.max(value.length+start,0);if(Twig.lib.is("Array",value)){var output=[];for(var i=startIndex;i2){throw new Twig.Error("split filter expects 1 or 2 argument")}if(Twig.lib.is("String",value)){var delimiter=params[0],limit=params[1],split=value.split(delimiter);if(limit===undefined){return split}else if(limit<0){return value.split(delimiter,split.length+limit)}else{var limitedSplit=[];if(delimiter==""){while(split.length>0){var temp="";for(var i=0;i0;i++){temp+=split.shift()}limitedSplit.push(temp)}}else{for(var i=0;i0;i++){limitedSplit.push(split.shift())}if(split.length>0){limitedSplit.push(split.join(delimiter))}}return limitedSplit}}else{throw new Twig.Error("split filter expects value to be a string")}},last:function(value){if(Twig.lib.is("Object",value)){var keys;if(value._keys===undefined){keys=Object.keys(value)}else{keys=value._keys}return value[keys[keys.length-1]]}return value[value.length-1]},raw:function(value){return value},batch:function(items,params){var size=params.shift(),fill=params.shift(),result,last,missing;if(!Twig.lib.is("Array",items)){throw new Twig.Error("batch filter expects items to be an array")}if(!Twig.lib.is("Number",size)){throw new Twig.Error("batch filter expects size to be a number")}size=Math.ceil(size);result=Twig.lib.chunkArray(items,size);if(fill&&items.length%size!=0){last=result.pop();missing=size-last.length;while(missing--){last.push(fill)}result.push(last)}return result},round:function(value,params){params=params||[];var precision=params.length>0?params[0]:0,method=params.length>1?params[1]:"common";value=parseFloat(value);if(precision&&!Twig.lib.is("Number",precision)){throw new Twig.Error("round filter expects precision to be a number")}if(method==="common"){return Twig.lib.round(value,precision)}if(!Twig.lib.is("Function",Math[method])){throw new Twig.Error("round filter expects method to be 'floor', 'ceil', or 'common'")}return Math[method](value*Math.pow(10,precision))/Math.pow(10,precision)}};Twig.filter=function(filter,value,params){if(!Twig.filters[filter]){throw"Unable to find filter "+filter}return Twig.filters[filter].apply(this,[value,params])};Twig.filter.extend=function(filter,definition){Twig.filters[filter]=definition};return Twig}(Twig||{});var Twig=function(Twig){function is(type,obj){var clas=Object.prototype.toString.call(obj).slice(8,-1);return obj!==undefined&&obj!==null&&clas===type}Twig.functions={range:function(low,high,step){var matrix=[];var inival,endval,plus;var walker=step||1;var chars=false;if(!isNaN(low)&&!isNaN(high)){inival=parseInt(low,10);endval=parseInt(high,10)}else if(isNaN(low)&&isNaN(high)){chars=true;inival=low.charCodeAt(0);endval=high.charCodeAt(0)}else{inival=isNaN(low)?0:low;endval=isNaN(high)?0:high}plus=inival>endval?false:true;if(plus){while(inival<=endval){matrix.push(chars?String.fromCharCode(inival):inival);inival+=walker}}else{while(inival>=endval){matrix.push(chars?String.fromCharCode(inival):inival);inival-=walker}}return matrix},cycle:function(arr,i){var pos=i%arr.length;return arr[pos]},dump:function(){var EOL="\n",indentChar=" ",indentTimes=0,out="",args=Array.prototype.slice.call(arguments),indent=function(times){var ind="";while(times>0){times--;ind+=indentChar}return ind},displayVar=function(variable){out+=indent(indentTimes);if(typeof variable==="object"){dumpVar(variable)}else if(typeof variable==="function"){out+="function()"+EOL}else if(typeof variable==="string"){out+="string("+variable.length+') "'+variable+'"'+EOL}else if(typeof variable==="number"){out+="number("+variable+")"+EOL}else if(typeof variable==="boolean"){out+="bool("+variable+")"+EOL}},dumpVar=function(variable){var i;if(variable===null){out+="NULL"+EOL}else if(variable===undefined){out+="undefined"+EOL}else if(typeof variable==="object"){out+=indent(indentTimes)+typeof variable;indentTimes++;out+="("+function(obj){var size=0,key;for(key in obj){if(obj.hasOwnProperty(key)){size++}}return size}(variable)+") {"+EOL;for(i in variable){out+=indent(indentTimes)+"["+i+"]=> "+EOL;displayVar(variable[i])}indentTimes--;out+=indent(indentTimes)+"}"+EOL}else{displayVar(variable)}};if(args.length==0)args.push(this.context);Twig.forEach(args,function(variable){dumpVar(variable)});return out},date:function(date,time){var dateObj;if(date===undefined){dateObj=new Date}else if(Twig.lib.is("Date",date)){dateObj=date}else if(Twig.lib.is("String",date)){dateObj=new Date(Twig.lib.strtotime(date)*1e3)}else if(Twig.lib.is("Number",date)){dateObj=new Date(date*1e3)}else{throw new Twig.Error("Unable to parse date "+date)}return dateObj},block:function(block){return this.blocks[block]},parent:function(){return Twig.placeholders.parent},attribute:function(object,method,params){if(object instanceof Object){if(object.hasOwnProperty(method)){if(typeof object[method]==="function"){return object[method].apply(undefined,params)}else{return object[method]}}}return object[method]||undefined}};Twig._function=function(_function,value,params){if(!Twig.functions[_function]){throw"Unable to find function "+_function}return Twig.functions[_function](value,params)};Twig._function.extend=function(_function,definition){Twig.functions[_function]=definition};return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.tests={empty:function(value){if(value===null||value===undefined)return true;if(typeof value==="number")return false;if(value.length&&value.length>0)return false;for(var key in value){if(value.hasOwnProperty(key))return false}return true},odd:function(value){return value%2===1},even:function(value){return value%2===0},divisibleby:function(value,params){return value%params[0]===0},defined:function(value){return value!==undefined},none:function(value){return value===null},"null":function(value){return this.none(value)},sameas:function(value,params){return value===params[0]}};Twig.test=function(test,value,params){if(!Twig.tests[test]){throw"Test "+test+" is not defined."}return Twig.tests[test](value,params)};Twig.test.extend=function(test,definition){Twig.tests[test]=definition};return Twig}(Twig||{});var Twig=function(Twig){"use strict";Twig.exports={VERSION:Twig.VERSION};Twig.exports.twig=function twig(params){"use strict";var id=params.id,options={strict_variables:params.strict_variables||false,allowInlineIncludes:params.allowInlineIncludes||false,rethrow:params.rethrow||false};if(id){Twig.validateId(id)}if(params.debug!==undefined){Twig.debug=params.debug}if(params.trace!==undefined){Twig.trace=params.trace}if(params.data!==undefined){return new Twig.Template({data:params.data,module:params.module,id:id,options:options})}else if(params.ref!==undefined){if(params.id!==undefined){throw new Twig.Error("Both ref and id cannot be set on a twig.js template.")}return Twig.Templates.load(params.ref)}else if(params.href!==undefined){return Twig.Templates.loadRemote(params.href,{id:id,method:"ajax",base:params.base,module:params.module,precompiled:params.precompiled,async:params.async,options:options},params.load,params.error)}else if(params.path!==undefined){return Twig.Templates.loadRemote(params.path,{id:id,method:"fs",base:params.base,module:params.module,precompiled:params.precompiled,async:params.async,options:options},params.load,params.error)}};Twig.exports.extendFilter=function(filter,definition){Twig.filter.extend(filter,definition)};Twig.exports.extendFunction=function(fn,definition){Twig._function.extend(fn,definition)};Twig.exports.extendTest=function(test,definition){Twig.test.extend(test,definition)};Twig.exports.extendTag=function(definition){Twig.logic.extend(definition)};Twig.exports.extend=function(fn){fn(Twig)};Twig.exports.compile=function(markup,options){var id=options.filename,path=options.filename,template;template=new Twig.Template({data:markup,path:path,id:id,options:options.settings["twig options"]});return function(context){return template.render(context)}};Twig.exports.renderFile=function(path,options,fn){if("function"==typeof options){fn=options;options={}}options=options||{};var params={path:path,base:options.settings["views"],load:function(template){fn(null,template.render(options))}};var view_options=options.settings["twig options"];if(view_options){for(var option in view_options)if(view_options.hasOwnProperty(option)){params[option]=view_options[option]}}Twig.exports.twig(params)};Twig.exports.__express=Twig.exports.renderFile;Twig.exports.cache=function(cache){Twig.cache=cache};return Twig}(Twig||{});var Twig=function(Twig){Twig.compiler={module:{}};Twig.compiler.compile=function(template,options){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)}output=Twig.compiler.module[options.module](id,tokens,options.twig)}else{output=Twig.compiler.wrap(id,tokens)}return output};Twig.compiler.module={amd:function(id,tokens,pathToTwig){return'define(["'+pathToTwig+'"], function (Twig) {\n var twig, templates;\ntwig = Twig.twig;\ntemplates = '+Twig.compiler.wrap(id,tokens)+"\n return templates;\n});"},node:function(id,tokens){return'var twig = require("twig").twig;\n'+"exports.template = "+Twig.compiler.wrap(id,tokens)},cjs2:function(id,tokens,pathToTwig){return'module.declare([{ twig: "'+pathToTwig+'" }], function (require, exports, module) {\n'+' var twig = require("twig").twig;\n'+" exports.template = "+Twig.compiler.wrap(id,tokens)+"\n});"}};Twig.compiler.wrap=function(id,tokens){return'twig({id:"'+id.replace('"','\\"')+'", data:'+tokens+", precompiled: true});\n"};return Twig}(Twig||{});if(typeof module!=="undefined"&&module.declare){module.declare([],function(require,exports,module){for(key in Twig.exports){if(Twig.exports.hasOwnProperty(key)){exports[key]=Twig.exports[key]}}})}else if(typeof define=="function"&&define.amd){define(function(){return Twig.exports})}else if(typeof module!=="undefined"&&module.exports){module.exports=Twig.exports}else{window.twig=Twig.exports.twig;window.Twig=Twig.exports} //# sourceMappingURL=twig.min.js.map \ No newline at end of file