diff options
Diffstat (limited to 'node_modules/twig')
28 files changed, 0 insertions, 24113 deletions
diff --git a/node_modules/twig/.gitmodules b/node_modules/twig/.gitmodules deleted file mode 100644 index 17a914c..0000000 --- a/node_modules/twig/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "docs/wiki"] - path = docs/wiki - url = git://github.com/justjohn/twig.js.wiki.git diff --git a/node_modules/twig/.npmignore b/node_modules/twig/.npmignore deleted file mode 100644 index 0989cb8..0000000 --- a/node_modules/twig/.npmignore +++ /dev/null @@ -1,12 +0,0 @@ -demos -tools -test -qtest -src -.hg -.git -.gitignore -.travis.yml -.idea -webpack.config.js -bower.json diff --git a/node_modules/twig/CHANGELOG.md b/node_modules/twig/CHANGELOG.md deleted file mode 100644 index 5784b3b..0000000 --- a/node_modules/twig/CHANGELOG.md +++ /dev/null @@ -1,155 +0,0 @@ -Version 0.10.3, released 2016-12-09 ------------------------------------ -Minor improvements: -* Spaceless tag no longer escapes Static values (#435) -* Inline includes now load (#433) -* Errors during async fs loading use error callback (#431) - -Version 0.10.2, released 2016-11-23 ------------------------------------ -Minor improvements: -* Support 'same as' (#429) -* Fix windows colon colon namespace (#430) - -Version 0.10.1, released 2016-11-18 ------------------------------------ - -Minor improvements: -* Fixed missing changelog updates -* Fixed incorrect versions in source -* Rethrow errors when option to do so is set (#422) - -Version 0.10.0, released 2016-10-28 ------------------------------------ -Bower is no longer supported - -Major improvements: -* Updated to locutus which replaces phpjs -* elseif now accepts truthy values (#370) -* Use PHP style falsy matching (#383) -* Fix 'not' after binary expressions (#385) -* Use current context when parsing an include (#395) -* Correct handling of 'ignore missing' in embed and include (#424) - -Minor improvements: -* Documentation updates -* Refreshed dependencies - -Version 0.9.5, released 2016-05-14 ------------------------------------ - -Minor improvements: -* Templates that are included via "extends" now populate the parent template context - -Version 0.9.4, released 2016-05-13 ------------------------------------ -Parentheses parsing has undergone quite a large refactoring, but nothing should have explicitly broken. - -Major improvements: -* Subexpressions are now supported and parsed differently from function parameters - -Version 0.9.3, released 2016-05-12 ------------------------------------ -Fix missing changelog updates - -Version 0.9.2, released 2016-05-12 ------------------------------------ -Minor improvements: -* Empty strings can now be passed to the date filter -* Twig.expression.resolve keeps the correct context for `this` - -Version 0.9.1, released 2016-05-10 ------------------------------------ -Fixed changelog versioning - -Version 0.9.0, released 2016-05-10 ------------------------------------ -Theoretically no breaking changes, but lots of things have changed so it is possible something has slipped through. - -Dependencies have been updated. You should run `npm install` to update these. - -Major improvements: -* Webpack is now used for builds -* phpjs is now a dependency and replaces our reimplementation of PHP functions (#343) -* Arrays are now cast to booleans unless accessing their contents -* in/not in operator precedence changed (#344) -* Expressions can now be keys (#350) -* The extended ternary operator is now supported (#354) -* Expressions can now appear after period accessor (#356) -* The slice shorthand is now supported (#362) - -Minor improvements: -* Twig.exports.renderFile now returns a string rather than a String (#348) -* The value of context is now cloned when setting a variable to context (#345) -* Negative numbers are now correctly parsed (#353) -* The // operator now works correctly (#353) - - -Version 0.8.9, released 2016-03-18 ------------------------------ -Dependencies have been updated to current versions. You should run `npm install` to update these. (#313) - -Major improvements: -* Twig's `source` function is now supported (#309) -* It is possible to add additional parsers using Twig.Templates.registerParser() (currently available: twig, source). If you are using a custom loader, please investigate src/twig.loader.fs.js how to call the requested parser. (#309) -* `undefined` and `null` values now supported in the `in` operator (#311) -* Namespaces can now be defined using the '@' symbol (#328) - -Minor improvements: -* Undefined object properties now have the value of `undefined` rather than `null` (#311) -* Improved browser tests (#325, #310) -* IE8 fix (#324) -* Path resolution has been refactored to its own module (#323) - -Version 0.8.8, released 2016-02-13 ----------------------------------- -Major improvements: -* Support for [block shortcuts](http://twig.sensiolabs.org/doc/tags/extends.html#block-shortcuts): `{% block title page_title|title %}` (#304) -* Define custom template loaders, by registering them via `Twig.Templates.registerLoader()` (#301) - -Minor improvements: -* Some mocha tests didn't work in browsers (#281) -* Fix Twig.renderFile (#303) - -[All issues of this milestone](https://github.com/justjohn/twig.js/issues?q=milestone%3A0.8.8) - -Version 0.8.7, released 2016-01-20 ----------------------------------- -Major improvements: -* The `autoescape` option now supports all strategies which are supported by the `escape` filter (#299) - -Minor improvements: -* The `date` filter now recognises unix timestamps as input, when they are passed as string (#296) -* The `default` filter now allows to be called without parameters (it will return an empty string in that case) (#295) -* Normalize provided template paths (this generated problems when using nodejs under Windows) (#252, #300) - -Version 0.8.6, released 2016-01-05 ----------------------------------- -Major improvements: -* The `escape` filter now supports the strategy parameter: `{{ var|escape('css') }}` with the following available strategies: html (default), js, css, url, html_attr. (#289) - -Minor improvements: -* The filter `url_encode` now also encodes apostrophe (as in Twig.php) (#288) -* Minor bugfixes (#290, #291) - -Version 0.8.5, released 2015-12-24 ----------------------------------- -From 0.8.5 on, a summary of changes between each version will be included in the CHANGELOG.md file. - -There were some changes to the [Contribution guidelines](https://github.com/justjohn/twig.js/wiki/Contributing): please commit only changes to source files, the files `twig.js` and `twig.min.js` will be rebuilt when a new version gets released. Therefore you need to run `make` after cloning resp. pulling (if you want to use the development version). - -Major improvements: -* Implement `min` and `max` functions (#164) -* Support for the whitespace control modifier: `{{- -}}` (#266) -* `sort` filter: try to cast values to match type (numeric values to number, string otherwise) (#278) -* Support for twig namespaces (#195, #251) -* Support for expressions as object keys: `{% set foo = { (1 + 1): 'bar' } %}` (#284) - -Minor improvements: -* Allow integer 0 as key in objects: `{ 0: "value" }` (#186) -* `json_encode` filter: always return objects in order of keys, also ignore the internal key `_keys` for nested objects (#279) -* `date` filter: update to current strtotime() function from phpjs: now support ISO8601 dates as input on Mozilla Firefox. (#276) -* Validate template IDs only when caching is enabled (#233, #259) -* Support xmlhttp.status==0 when using cordova (#240) -* Improved sub template file loading (#264) -* Ignore quotes between `{% raw %}` and `{% endraw %}` (#286) diff --git a/node_modules/twig/LICENSE b/node_modules/twig/LICENSE deleted file mode 100644 index f2911cd..0000000 --- a/node_modules/twig/LICENSE +++ /dev/null @@ -1,10 +0,0 @@ -Copyright (c) 2011-2015, John Roepke and the Twig.js Contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/node_modules/twig/README.md b/node_modules/twig/README.md deleted file mode 100644 index db7ed80..0000000 --- a/node_modules/twig/README.md +++ /dev/null @@ -1,136 +0,0 @@ -[](https://waffle.io/twigjs/twig.js) -[](https://snyk.io/test/github/twigjs/twig.js) -[](http://travis-ci.org/#!/twigjs/twig.js) -[](http://badge.fury.io/js/twig) -[](https://gitter.im/twigjs/twig.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -# About - -Twig.js is a pure JavaScript implementation of the Twig PHP templating language -(<http://twig.sensiolabs.org/>) - -The goal is to provide a library that is compatible with both browsers and server side JavaScript environments such as node.js. - -Twig.js is currently a work in progress and supports a limited subset of the Twig templating language (with more coming). - -### Docs - -Documentation is available in the [twig.js wiki](https://github.com/twigjs/twig.js/wiki) on Github. - -### Feature Support - -For a list of supported tags/filters/functions/tests see the [Implementation Notes](https://github.com/twigjs/twig.js/wiki/Implementation-Notes) page on the wiki. - -# Install - -Download the latest twig.js release from github: https://github.com/twigjs/twig.js/releases or via NPM: - - npm install twig --save - -## Browser Usage - -Include twig.js or twig.min.js in your page, then: - -```js -var template = twig({ - data: 'The {{ baked_good }} is a lie.' -}); - -console.log( - template.render({baked_good: 'cupcake'}) -); -// outputs: "The cupcake is a lie." -``` - -## Node Usage (npm) - -You can use twig in your app with - - var Twig = require('twig'), // Twig module - twig = Twig.twig; // Render function - -### Usage without Express - -If you don't want to use Express, you can render a template with the following method: - -```js -import Twig from 'twig'; -Twig.renderFile('./path/to/someFile.twig', {foo:'bar'}, (err, html) => { - html; // compiled string -}); -``` - -### Usage with Express - -Twig is compatible with express 2 and 3. You can create an express app using the twig.js templating language by setting the view engine to twig. - -### app.js - -**Express 3** - -```js -var Twig = require("twig"), - express = require('express'), - app = express(); - -// This section is optional and used to configure twig. -app.set("twig options", { - strict_variables: false -}); - -app.get('/', function(req, res){ - res.render('index.twig', { - message : "Hello World" - }); -}); - -app.listen(9999); -``` - -## views/index.twig - -```html -Message of the moment: <b>{{ message }}</b> -``` - -An [Express 2 Example](https://github.com/twigjs/twig.js/wiki/Express-2) is available on the wiki. - -# Contributing - -If you have a change you want to make to twig.js, feel free to fork this repository and submit a pull request on Github. The source files are located in src/*.js. - -twig.js is built by running `npm run build` - -For more details on getting setup, see the [contributing page](https://github.com/twigjs/twig.js/wiki/Contributing) on the wiki. - -## Tests - -The twig.js tests are written in [Mocha][mocha] and can be invoked with `npm test`. - -## License - -Twig.js is available under a [BSD 2-Clause License][bsd-2], see the LICENSE file for more information. - -## Acknowledgments - -See the LICENSES.md file for copies of the referenced licenses. - -1. The JavaScript Array fills in src/twig.fills.js are from <https://developer.mozilla.org/> and are available under the [MIT License][mit] or are [public domain][mdn-license]. - -2. The Date.format function in src/twig.lib.js is from <http://jpaq.org/> and used under a [MIT license][mit-jpaq]. - -3. The sprintf implementation in src/twig.lib.js used for the format filter is from <http://www.diveintojavascript.com/projects/javascript-sprintf> and used under a [BSD 3-Clause License][bsd-3]. - -4. The strip_tags implementation in src/twig.lib.js used for the striptags filter is from <http://phpjs.org/functions/strip_tags> and used under and [MIT License][mit-phpjs]. - -[mit-jpaq]: http://jpaq.org/license/ -[mit-phpjs]: http://phpjs.org/pages/license/#MIT -[mit]: http://www.opensource.org/licenses/mit-license.php -[mdn-license]: https://developer.mozilla.org/Project:Copyrights - -[bsd-2]: http://www.opensource.org/licenses/BSD-2-Clause -[bsd-3]: http://www.opensource.org/licenses/BSD-3-Clause -[cc-by-sa-2.5]: http://creativecommons.org/licenses/by-sa/2.5/ "Creative Commons Attribution-ShareAlike 2.5 License" - -[mocha]: http://visionmedia.github.com/mocha/ -[qunit]: http://docs.jquery.com/QUnit diff --git a/node_modules/twig/bin/twigjs b/node_modules/twig/bin/twigjs deleted file mode 100755 index cfacb85..0000000 --- a/node_modules/twig/bin/twigjs +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env node - -var PATHS = require("../lib/paths") - , COMPILE = require("../lib/compile") - , options = COMPILE.defaults - - , args = process.argv - , node = args.shift() - , thisPath = args.shift().split("/") - , thisFile = thisPath[thisPath.length-1] - , files = [] - , arg; - -if (args.length == 0) { - process.stderr.write("ERR: No input files provided\n\n"); - printUsage(process.stderr); -} - -while (args.length > 0) { - arg = args.shift(); - switch (arg) { - case "--help": - printUsage(process.stdout); - return; - case "--output": - case "-o": - options.output = PATHS.strip_slash(args.shift()); - break; - case "--pattern": - case "-p": - options.pattern = args.shift(); - break; - case "--module": - case "-m": - options.module = args.shift(); - break; - case "--twig": - case "-t": - options.twig = args.shift(); - break; - default: - files.push(arg); - } -} - -COMPILE.compile(options, files); - -function printUsage(stream) { - stream.write("Usage:\n\t"); - stream.write(thisFile + " [options] input.twig | directory ...\n"); - stream.write("\t_______________________________________________________________________________\n\n"); - stream.write("\t" + thisFile + " can take a list of files and/or a directories as input. If a file is\n"); - stream.write("\tprovided, it is compiled, if a directory is provided, all files matching *.twig\n"); - stream.write("\tin the directory are compiled. The pattern can be overridden with --pattern\n\n") - stream.write("\t--help Print this help message.\n\n"); - stream.write("\t--output ... What directory should twigjs output to. By default twigjs will\n"); - stream.write("\t write to the same directory as the input file.\n\n"); - stream.write("\t--module ... Should the output be written in module format. Supported formats:\n"); - stream.write("\t node: Node.js / CommonJS 1.1 modules\n"); - stream.write("\t amd: RequireJS / Asynchronous modules (requires --twig)\n"); - stream.write("\t cjs2: CommonJS 2.0 draft8 modules (requires --twig)\n\n"); - stream.write("\t--twig ... Used with --module. The location relative to the output directory\n"); - stream.write("\t of twig.js. (used for module dependency resolution).\n\n"); - stream.write("\t--pattern ... If parsing a directory of files, what files should be compiled.\n"); - stream.write("\t Defaults to *.twig.\n\n"); - stream.write("NOTE: This is currently very rough, incomplete and under development.\n\n"); -} diff --git a/node_modules/twig/docs/docco.css b/node_modules/twig/docs/docco.css deleted file mode 100644 index b60f6fa..0000000 --- a/node_modules/twig/docs/docco.css +++ /dev/null @@ -1,518 +0,0 @@ -/*--------------------- Typography ----------------------------*/ - -@font-face { - font-family: 'aller-light'; - src: url('public/fonts/aller-light.eot'); - src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'), - url('public/fonts/aller-light.woff') format('woff'), - url('public/fonts/aller-light.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'aller-bold'; - src: url('public/fonts/aller-bold.eot'); - src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'), - url('public/fonts/aller-bold.woff') format('woff'), - url('public/fonts/aller-bold.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'roboto-black'; - src: url('public/fonts/roboto-black.eot'); - src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'), - url('public/fonts/roboto-black.woff') format('woff'), - url('public/fonts/roboto-black.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -/*--------------------- Layout ----------------------------*/ -html { height: 100%; } -body { - font-family: "aller-light"; - font-size: 14px; - line-height: 18px; - color: #30404f; - margin: 0; padding: 0; - height:100%; -} -#container { min-height: 100%; } - -a { - color: #000; -} - -b, strong { - font-weight: normal; - font-family: "aller-bold"; -} - -p { - 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; - line-height: 1em; - font-weight: normal; - font-family: "roboto-black"; - text-transform: uppercase; - margin: 30px 0 15px 0; -} - -h1 { - margin-top: 40px; -} -h2 { - font-size: 1.26em; -} - -hr { - border: 0; - background: 1px #ddd; - height: 1px; - margin: 20px 0; -} - -pre, tt, code { - font-size: 12px; line-height: 16px; - font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; - margin: 0; padding: 0; -} - .annotation pre { - display: block; - margin: 0; - padding: 7px 10px; - background: #fcfcfc; - -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); - -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); - box-shadow: inset 0 0 10px rgba(0,0,0,0.1); - overflow-x: auto; - } - .annotation pre code { - border: 0; - padding: 0; - background: transparent; - } - - -blockquote { - border-left: 5px solid #ccc; - margin: 0; - padding: 1px 0 1px 1em; -} - .sections blockquote p { - font-family: Menlo, Consolas, Monaco, monospace; - font-size: 12px; line-height: 16px; - color: #999; - margin: 10px 0 0; - white-space: pre-wrap; - } - -ul.sections { - list-style: none; - padding:0 0 5px 0;; - margin:0; -} - -/* - Force border-box so that % widths fit the parent - container without overlap because of margin/padding. - - More Info : http://www.quirksmode.org/css/box.html -*/ -ul.sections > li > div { - -moz-box-sizing: border-box; /* firefox */ - -ms-box-sizing: border-box; /* ie */ - -webkit-box-sizing: border-box; /* webkit */ - -khtml-box-sizing: border-box; /* konqueror */ - box-sizing: border-box; /* css3 */ -} - - -/*---------------------- Jump Page -----------------------------*/ -#jump_to, #jump_page { - margin: 0; - background: white; - -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; - -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; - font: 16px Arial; - cursor: pointer; - text-align: right; - list-style: none; -} - -#jump_to a { - text-decoration: none; -} - -#jump_to a.large { - display: none; -} -#jump_to a.small { - font-size: 22px; - font-weight: bold; - color: #676767; -} - -#jump_to, #jump_wrapper { - position: fixed; - right: 0; top: 0; - padding: 10px 15px; - margin:0; -} - -#jump_wrapper { - display: none; - padding:0; -} - -#jump_to:hover #jump_wrapper { - display: block; -} - -#jump_page_wrapper{ - position: fixed; - right: 0; - top: 0; - bottom: 0; -} - -#jump_page { - padding: 5px 0 3px; - margin: 0 0 25px 25px; - max-height: 100%; - overflow: auto; -} - -#jump_page .source { - display: block; - padding: 15px; - text-decoration: none; - border-top: 1px solid #eee; -} - -#jump_page .source:hover { - background: #f5f5ff; -} - -#jump_page .source:first-child { -} - -/*---------------------- Low resolutions (> 320px) ---------------------*/ -@media only screen and (min-width: 320px) { - .pilwrap { display: none; } - - ul.sections > li > div { - display: block; - padding:5px 10px 0 10px; - } - - ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { - padding-left: 30px; - } - - ul.sections > li > div.content { - overflow-x:auto; - -webkit-box-shadow: inset 0 0 5px #e5e5ee; - box-shadow: inset 0 0 5px #e5e5ee; - border: 1px solid #dedede; - margin:5px 10px 5px 10px; - padding-bottom: 5px; - } - - ul.sections > li > div.annotation pre { - margin: 7px 0 7px; - padding-left: 15px; - } - - ul.sections > li > div.annotation p tt, .annotation code { - background: #f8f8ff; - border: 1px solid #dedede; - font-size: 12px; - padding: 0 0.2em; - } -} - -/*---------------------- (> 481px) ---------------------*/ -@media only screen and (min-width: 481px) { - #container { - position: relative; - } - body { - background-color: #F5F5FF; - font-size: 15px; - line-height: 21px; - } - pre, tt, code { - line-height: 18px; - } - p, ul, ol { - margin: 0 0 15px; - } - - - #jump_to { - padding: 5px 10px; - } - #jump_wrapper { - padding: 0; - } - #jump_to, #jump_page { - font: 10px Arial; - text-transform: uppercase; - } - #jump_page .source { - padding: 5px 10px; - } - #jump_to a.large { - display: inline-block; - } - #jump_to a.small { - display: none; - } - - - - #background { - position: absolute; - top: 0; bottom: 0; - width: 350px; - background: #fff; - border-right: 1px solid #e5e5ee; - z-index: -1; - } - - ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { - padding-left: 40px; - } - - ul.sections > li { - white-space: nowrap; - } - - ul.sections > li > div { - display: inline-block; - } - - ul.sections > li > div.annotation { - max-width: 350px; - min-width: 350px; - min-height: 5px; - padding: 13px; - overflow-x: hidden; - white-space: normal; - vertical-align: top; - text-align: left; - } - ul.sections > li > div.annotation pre { - margin: 15px 0 15px; - padding-left: 15px; - } - - ul.sections > li > div.content { - padding: 13px; - vertical-align: top; - border: none; - -webkit-box-shadow: none; - box-shadow: none; - } - - .pilwrap { - position: relative; - display: inline; - } - - .pilcrow { - font: 12px Arial; - text-decoration: none; - color: #454545; - position: absolute; - top: 3px; left: -20px; - padding: 1px 2px; - opacity: 0; - -webkit-transition: opacity 0.2s linear; - } - .for-h1 .pilcrow { - top: 47px; - } - .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow { - top: 35px; - } - - ul.sections > li > div.annotation:hover .pilcrow { - opacity: 1; - } -} - -/*---------------------- (> 1025px) ---------------------*/ -@media only screen and (min-width: 1025px) { - - body { - font-size: 16px; - line-height: 24px; - } - - #background { - width: 525px; - } - ul.sections > li > div.annotation { - max-width: 525px; - min-width: 525px; - padding: 10px 25px 1px 50px; - } - ul.sections > li > div.content { - padding: 9px 15px 16px 25px; - } -} - -/*---------------------- Syntax Highlighting -----------------------------*/ - -td.linenos { background-color: #f0f0f0; padding-right: 10px; } -span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } -/* - -github.com style (c) Vasily Polovnyov <vast@whiteants.net> - -*/ - -pre code { - display: block; padding: 0.5em; - color: #000; - background: #f8f8ff -} - -pre .hljs-comment, -pre .hljs-template_comment, -pre .hljs-diff .hljs-header, -pre .hljs-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 { - color: #954121; - /*font-weight: bold*/ -} - -pre .hljs-number, -pre .hljs-hexcolor { - color: #40a070 -} - -pre .hljs-string, -pre .hljs-tag .hljs-value, -pre .hljs-phpdoc, -pre .hljs-tex .hljs-formula { - color: #219161; -} - -pre .hljs-title, -pre .hljs-id { - color: #19469D; -} -pre .hljs-params { - color: #00F; -} - -pre .hljs-javascript .hljs-title, -pre .hljs-lisp .hljs-title, -pre .hljs-subst { - font-weight: normal -} - -pre .hljs-class .hljs-title, -pre .hljs-haskell .hljs-label, -pre .hljs-tex .hljs-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 { - color: #000080; - font-weight: normal -} - -pre .hljs-attribute, -pre .hljs-variable, -pre .hljs-instancevar, -pre .hljs-lisp .hljs-body { - color: #008080 -} - -pre .hljs-regexp { - color: #B68 -} - -pre .hljs-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 { - color: #990073 -} - -pre .hljs-builtin, -pre .hljs-constructor, -pre .hljs-built_in, -pre .hljs-lisp .hljs-title { - color: #0086b3 -} - -pre .hljs-preprocessor, -pre .hljs-pi, -pre .hljs-doctype, -pre .hljs-shebang, -pre .hljs-cdata { - color: #999; - font-weight: bold -} - -pre .hljs-deletion { - background: #fdd -} - -pre .hljs-addition { - background: #dfd -} - -pre .hljs-diff .hljs-change { - background: #0086b3 -} - -pre .hljs-chunk { - color: #aaa -} - -pre .hljs-tex .hljs-formula { - opacity: 0.5; -} diff --git a/node_modules/twig/docs/licenses.md b/node_modules/twig/docs/licenses.md deleted file mode 100644 index 949a254..0000000 --- a/node_modules/twig/docs/licenses.md +++ /dev/null @@ -1,177 +0,0 @@ -## LICENSE for the sprintf and vsprintf functions is src/lib/twig.lib.js - - - Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com> - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of sprintf() for JavaScript nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -## LICENSE for the Date.format function is src/lib/twig.lib.js - - Copyright (c) 2010 Christopher West - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - - -## LICENSE for the strip_tags function in src/twig.lib.js and range function -## in src/twig.functions.js - - This is version: 3.26 - php.js is copyright 2011 Kevin van Zonneveld. - - Portions copyright Brett Zamir (http://brett-zamir.me), Kevin van Zonneveld - (http://kevin.vanzonneveld.net), Onno Marsman, Theriault, Michael White - (http://getsprink.com), Waldo Malqui Silva, Paulo Freitas, Jack, Jonas - Raoni Soares Silva (http://www.jsfromhell.com), Philip Peterson, Legaev - Andrey, Ates Goral (http://magnetiq.com), Alex, Ratheous, Martijn Wieringa, - Rafał Kukawski (http://blog.kukawski.pl), lmeyrick - (https://sourceforge.net/projects/bcmath-js/), Nate, Philippe Baumann, - Enrique Gonzalez, Webtoolkit.info (http://www.webtoolkit.info/), Carlos R. - L. Rodrigues (http://www.jsfromhell.com), Ash Searle - (http://hexmen.com/blog/), Jani Hartikainen, travc, Ole Vrijenhoek, - Erkekjetter, Michael Grier, Rafał Kukawski (http://kukawski.pl), Johnny - Mast (http://www.phpvrouwen.nl), T.Wild, d3x, - http://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript, - Rafał Kukawski (http://blog.kukawski.pl/), stag019, pilus, WebDevHobo - (http://webdevhobo.blogspot.com/), marrtins, GeekFG - (http://geekfg.blogspot.com), Andrea Giammarchi - (http://webreflection.blogspot.com), Arpad Ray (mailto:arpad@php.net), - gorthaur, Paul Smith, Tim de Koning (http://www.kingsquare.nl), Joris, Oleg - Eremeev, Steve Hilder, majak, gettimeofday, KELAN, Josh Fraser - (http://onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/), - Marc Palau, Kevin van Zonneveld (http://kevin.vanzonneveld.net/), Martin - (http://www.erlenwiese.de/), Breaking Par Consulting Inc - (http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256CFB006C45F7), - Chris, Mirek Slugen, saulius, Alfonso Jimenez - (http://www.alfonsojimenez.com), Diplom@t (http://difane.com/), felix, - Mailfaker (http://www.weedem.fr/), Tyler Akins (http://rumkin.com), Caio - Ariede (http://caioariede.com), Robin, Kankrelune - (http://www.webfaktory.info/), Karol Kowalski, Imgen Tata - (http://www.myipdf.com/), mdsjack (http://www.mdsjack.bo.it), Dreamer, - Felix Geisendoerfer (http://www.debuggable.com/felix), Lars Fischer, AJ, - David, Aman Gupta, Michael White, Public Domain - (http://www.json.org/json2.js), Steven Levithan - (http://blog.stevenlevithan.com), Sakimori, Pellentesque Malesuada, - Thunder.m, Dj (http://phpjs.org/functions/htmlentities:425#comment_134018), - Steve Clay, David James, Francois, class_exists, nobbler, T. Wild, Itsacon - (http://www.itsacon.net/), date, Ole Vrijenhoek (http://www.nervous.nl/), - Fox, Raphael (Ao RUDLER), Marco, noname, Mateusz "loonquawl" Zalega, Frank - Forte, Arno, ger, mktime, john (http://www.jd-tech.net), Nick Kolosov - (http://sammy.ru), marc andreu, Scott Cariss, Douglas Crockford - (http://javascript.crockford.com), madipta, Slawomir Kaniecki, - ReverseSyntax, Nathan, Alex Wilson, kenneth, Bayron Guevara, Adam Wallner - (http://web2.bitbaro.hu/), paulo kuong, jmweb, Lincoln Ramsay, djmix, - Pyerre, Jon Hohle, Thiago Mata (http://thiagomata.blog.com), lmeyrick - (https://sourceforge.net/projects/bcmath-js/this.), Linuxworld, duncan, - Gilbert, Sanjoy Roy, Shingo, sankai, Oskar Larsson Högfeldt - (http://oskar-lh.name/), Denny Wardhana, 0m3r, Everlasto, Subhasis Deb, - josh, jd, Pier Paolo Ramon (http://www.mastersoup.com/), P, merabi, Soren - Hansen, Eugene Bulkin (http://doubleaw.com/), Der Simon - (http://innerdom.sourceforge.net/), echo is bad, Ozh, XoraX - (http://www.xorax.info), EdorFaus, JB, J A R, Marc Jansen, Francesco, LH, - Stoyan Kyosev (http://www.svest.org/), nord_ua, omid - (http://phpjs.org/functions/380:380#comment_137122), Brad Touesnard, MeEtc - (http://yass.meetcweb.com), Peter-Paul Koch - (http://www.quirksmode.org/js/beat.html), Olivier Louvignes - (http://mg-crea.com/), T0bsn, Tim Wiel, Bryan Elliott, Jalal Berrami, - Martin, JT, David Randall, Thomas Beaucourt (http://www.webapp.fr), taith, - vlado houba, Pierre-Luc Paour, Kristof Coomans (SCK-CEN Belgian Nucleair - Research Centre), Martin Pool, Kirk Strobeck, Rick Waldron, Brant Messenger - (http://www.brantmessenger.com/), Devan Penner-Woelk, Saulo Vallory, Wagner - B. Soares, Artur Tchernychev, Valentina De Rosa, Jason Wong - (http://carrot.org/), Christoph, Daniel Esteban, strftime, Mick@el, rezna, - Simon Willison (http://simonwillison.net), Anton Ongson, Gabriel Paderni, - Marco van Oort, penutbutterjelly, Philipp Lenssen, Bjorn Roesbeke - (http://www.bjornroesbeke.be/), Bug?, Eric Nagel, Tomasz Wesolowski, - Evertjan Garretsen, Bobby Drake, Blues (http://tech.bluesmoon.info/), Luke - Godfrey, Pul, uestla, Alan C, Ulrich, Rafal Kukawski, Yves Sucaet, - sowberry, Norman "zEh" Fuchs, hitwork, Zahlii, johnrembo, Nick Callen, - Steven Levithan (stevenlevithan.com), ejsanders, Scott Baker, Brian Tafoya - (http://www.premasolutions.com/), Philippe Jausions - (http://pear.php.net/user/jausions), Aidan Lister - (http://aidanlister.com/), Rob, e-mike, HKM, ChaosNo1, metjay, strcasecmp, - strcmp, Taras Bogach, jpfle, Alexander Ermolaev - (http://snippets.dzone.com/user/AlexanderErmolaev), DxGx, kilops, Orlando, - dptr1988, Le Torbi, James (http://www.james-bell.co.uk/), Pedro Tainha - (http://www.pedrotainha.com), James, Arnout Kazemier - (http://www.3rd-Eden.com), Chris McMacken, gabriel paderni, Yannoo, - FGFEmperor, baris ozdil, Tod Gentille, Greg Frazier, jakes, 3D-GRAF, Allan - Jensen (http://www.winternet.no), Howard Yeend, Benjamin Lupton, davook, - daniel airton wermann (http://wermann.com.br), Atli Þór, Maximusya, Ryan - W Tenney (http://ryan.10e.us), Alexander M Beedie, fearphage - (http://http/my.opera.com/fearphage/), Nathan Sepulveda, Victor, Matteo, - Billy, stensi, Cord, Manish, T.J. Leahy, Riddler - (http://www.frontierwebdev.com/), Rafał Kukawski, FremyCompany, Matt - Bradley, Tim de Koning, Luis Salazar (http://www.freaky-media.com/), Diogo - Resende, Rival, Andrej Pavlovic, Garagoth, Le Torbi - (http://www.letorbi.de/), Dino, Josep Sanz (http://www.ws3.es/), rem, - Russell Walker (http://www.nbill.co.uk/), Jamie Beck - (http://www.terabit.ca/), setcookie, Michael, YUI Library: - http://developer.yahoo.com/yui/docs/YAHOO.util.DateLocale.html, Blues at - http://hacks.bluesmoon.info/strftime/strftime.js, Ben - (http://benblume.co.uk/), DtTvB - (http://dt.in.th/2008-09-16.string-length-in-bytes.html), Andreas, William, - meo, incidence, Cagri Ekin, Amirouche, Amir Habibi - (http://www.residence-mixte.com/), Luke Smith (http://lucassmith.name), - Kheang Hok Chin (http://www.distantia.ca/), Jay Klehr, Lorenzo Pisani, - Tony, Yen-Wei Liu, Greenseed, mk.keck, Leslie Hoare, dude, booeyOH, Ben - Bryan - - Dual licensed under the MIT (MIT-LICENSE.txt) - and GPL (GPL-LICENSE.txt) licenses. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL KEVIN VAN ZONNEVELD BE LIABLE FOR ANY CLAIM, DAMAGES - OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/twig/docs/public/fonts/aller-bold.eot b/node_modules/twig/docs/public/fonts/aller-bold.eot Binary files differdeleted file mode 100644 index 1b32532..0000000 --- a/node_modules/twig/docs/public/fonts/aller-bold.eot +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/aller-bold.ttf b/node_modules/twig/docs/public/fonts/aller-bold.ttf Binary files differdeleted file mode 100644 index dc4cc9c..0000000 --- a/node_modules/twig/docs/public/fonts/aller-bold.ttf +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/aller-bold.woff b/node_modules/twig/docs/public/fonts/aller-bold.woff Binary files differdeleted file mode 100644 index fa16fd0..0000000 --- a/node_modules/twig/docs/public/fonts/aller-bold.woff +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/aller-light.eot b/node_modules/twig/docs/public/fonts/aller-light.eot Binary files differdeleted file mode 100644 index 40bd654..0000000 --- a/node_modules/twig/docs/public/fonts/aller-light.eot +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/aller-light.ttf b/node_modules/twig/docs/public/fonts/aller-light.ttf Binary files differdeleted file mode 100644 index c2c7290..0000000 --- a/node_modules/twig/docs/public/fonts/aller-light.ttf +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/aller-light.woff b/node_modules/twig/docs/public/fonts/aller-light.woff Binary files differdeleted file mode 100644 index 81a09d1..0000000 --- a/node_modules/twig/docs/public/fonts/aller-light.woff +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/novecento-bold.eot b/node_modules/twig/docs/public/fonts/novecento-bold.eot Binary files differdeleted file mode 100644 index 98a9a7f..0000000 --- a/node_modules/twig/docs/public/fonts/novecento-bold.eot +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/novecento-bold.ttf b/node_modules/twig/docs/public/fonts/novecento-bold.ttf Binary files differdeleted file mode 100644 index 2af39b0..0000000 --- a/node_modules/twig/docs/public/fonts/novecento-bold.ttf +++ /dev/null diff --git a/node_modules/twig/docs/public/fonts/novecento-bold.woff b/node_modules/twig/docs/public/fonts/novecento-bold.woff Binary files differdeleted file mode 100644 index de558b5..0000000 --- a/node_modules/twig/docs/public/fonts/novecento-bold.woff +++ /dev/null diff --git a/node_modules/twig/docs/public/stylesheets/normalize.css b/node_modules/twig/docs/public/stylesheets/normalize.css deleted file mode 100644 index 73abb76..0000000 --- a/node_modules/twig/docs/public/stylesheets/normalize.css +++ /dev/null @@ -1,375 +0,0 @@ -/*! normalize.css v2.0.1 | MIT License | git.io/normalize */ - -/* ========================================================================== - HTML5 display definitions - ========================================================================== */ - -/* - * Corrects `block` display not defined in IE 8/9. - */ - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section, -summary { - display: block; -} - -/* - * Corrects `inline-block` display not defined in IE 8/9. - */ - -audio, -canvas, -video { - display: inline-block; -} - -/* - * Prevents modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/* - * Addresses styling for `hidden` attribute not present in IE 8/9. - */ - -[hidden] { - display: none; -} - -/* ========================================================================== - Base - ========================================================================== */ - -/* - * 1. Sets default font family to sans-serif. - * 2. Prevents iOS text size adjust after orientation change, without disabling - * user zoom. - */ - -html { - font-family: sans-serif; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ - -ms-text-size-adjust: 100%; /* 2 */ -} - -/* - * Removes default margin. - */ - -body { - margin: 0; -} - -/* ========================================================================== - Links - ========================================================================== */ - -/* - * Addresses `outline` inconsistency between Chrome and other browsers. - */ - -a:focus { - outline: thin dotted; -} - -/* - * Improves readability when focused and also mouse hovered in all browsers. - */ - -a:active, -a:hover { - outline: 0; -} - -/* ========================================================================== - Typography - ========================================================================== */ - -/* - * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, - * Safari 5, and Chrome. - */ - -h1 { - font-size: 2em; -} - -/* - * Addresses styling not present in IE 8/9, Safari 5, and Chrome. - */ - -abbr[title] { - border-bottom: 1px dotted; -} - -/* - * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. - */ - -b, -strong { - font-weight: bold; -} - -/* - * Addresses styling not present in Safari 5 and Chrome. - */ - -dfn { - font-style: italic; -} - -/* - * Addresses styling not present in IE 8/9. - */ - -mark { - background: #ff0; - color: #000; -} - - -/* - * Corrects font family set oddly in Safari 5 and Chrome. - */ - -code, -kbd, -pre, -samp { - font-family: monospace, serif; - font-size: 1em; -} - -/* - * Improves readability of pre-formatted text in all browsers. - */ - -pre { - white-space: pre; - white-space: pre-wrap; - word-wrap: break-word; -} - -/* - * Sets consistent quote types. - */ - -q { - quotes: "\201C" "\201D" "\2018" "\2019"; -} - -/* - * Addresses inconsistent and variable font size in all browsers. - */ - -small { - font-size: 80%; -} - -/* - * Prevents `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -/* ========================================================================== - Embedded content - ========================================================================== */ - -/* - * Removes border when inside `a` element in IE 8/9. - */ - -img { - border: 0; -} - -/* - * Corrects overflow displayed oddly in IE 9. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* ========================================================================== - Figures - ========================================================================== */ - -/* - * Addresses margin not present in IE 8/9 and Safari 5. - */ - -figure { - margin: 0; -} - -/* ========================================================================== - Forms - ========================================================================== */ - -/* - * Define consistent border, margin, and padding. - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/* - * 1. Corrects color not being inherited in IE 8/9. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ - -legend { - border: 0; /* 1 */ - padding: 0; /* 2 */ -} - -/* - * 1. Corrects font family not being inherited in all browsers. - * 2. Corrects font size not being inherited in all browsers. - * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome - */ - -button, -input, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 2 */ - margin: 0; /* 3 */ -} - -/* - * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ - -button, -input { - line-height: normal; -} - -/* - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Corrects inability to style clickable `input` types in iOS. - * 3. Improves usability and consistency of cursor style between image-type - * `input` and others. - */ - -button, -html input[type="button"], /* 1 */ -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; /* 2 */ - cursor: pointer; /* 3 */ -} - -/* - * Re-set default cursor for disabled elements. - */ - -button[disabled], -input[disabled] { - cursor: default; -} - -/* - * 1. Addresses box sizing set to `content-box` in IE 8/9. - * 2. Removes excess padding in IE 8/9. - */ - -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/* - * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. - * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome - * (include `-moz` to future-proof). - */ - -input[type="search"] { - -webkit-appearance: textfield; /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; /* 2 */ - box-sizing: content-box; -} - -/* - * Removes inner padding and search cancel button in Safari 5 and Chrome - * on OS X. - */ - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* - * Removes inner padding and border in Firefox 4+. - */ - -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} - -/* - * 1. Removes default vertical scrollbar in IE 8/9. - * 2. Improves readability and alignment in all browsers. - */ - -textarea { - overflow: auto; /* 1 */ - vertical-align: top; /* 2 */ -} - -/* ========================================================================== - Tables - ========================================================================== */ - -/* - * Remove most spacing between table cells. - */ - -table { - border-collapse: collapse; - border-spacing: 0; -}
\ No newline at end of file diff --git a/node_modules/twig/docs/release checklist.md b/node_modules/twig/docs/release checklist.md deleted file mode 100644 index 1718c0d..0000000 --- a/node_modules/twig/docs/release checklist.md +++ /dev/null @@ -1,20 +0,0 @@ -Steps to release twig.js - -## repository - -1. Compile list of changes in CHANGELOG.md -2. Update version in package.json -3. Update version in bower.json -4. Update version in src/twig.header.js -5. `make`, `make docs`, `make test` and commit the changes. -6. `git tag` new version - -## bower - -Bower will pick up the new version in bower.json and use the associated git tag. - -## npm - -To publish the latest version to npmjs.org run this command from the twig.js directory: - - npm publish diff --git a/node_modules/twig/docs/tests.md b/node_modules/twig/docs/tests.md deleted file mode 100644 index 5afa1b7..0000000 --- a/node_modules/twig/docs/tests.md +++ /dev/null @@ -1,3567 +0,0 @@ -# TOC - - [Twig.js Blocks ->](#twigjs-blocks--) - - [block function ->](#twigjs-blocks---block-function--) - - [block shorthand ->](#twigjs-blocks---block-shorthand--) - - [Twig.js Control Structures ->](#twigjs-control-structures--) - - [if tag ->](#twigjs-control-structures---if-tag--) - - [for tag ->](#twigjs-control-structures---for-tag--) - - [set tag ->](#twigjs-control-structures---set-tag--) - - [Twig.js Core ->](#twigjs-core--) - - [Key Notation ->](#twigjs-core---key-notation--) - - [Context ->](#twigjs-core---context--) - - [Twig.js Embed ->](#twigjs-embed--) - - [Twig.js Expressions ->](#twigjs-expressions--) - - [Basic Operators ->](#twigjs-expressions---basic-operators--) - - [Comparison Operators ->](#twigjs-expressions---comparison-operators--) - - [Other Operators ->](#twigjs-expressions---other-operators--) - - [Twig.js Extensions ->](#twigjs-extensions--) - - [Twig.js Filters ->](#twigjs-filters--) - - [url_encode ->](#twigjs-filters---url_encode--) - - [json_encode ->](#twigjs-filters---json_encode--) - - [upper ->](#twigjs-filters---upper--) - - [lower ->](#twigjs-filters---lower--) - - [capitalize ->](#twigjs-filters---capitalize--) - - [title ->](#twigjs-filters---title--) - - [length ->](#twigjs-filters---length--) - - [sort ->](#twigjs-filters---sort--) - - [reverse ->](#twigjs-filters---reverse--) - - [keys ->](#twigjs-filters---keys--) - - [merge ->](#twigjs-filters---merge--) - - [join ->](#twigjs-filters---join--) - - [default ->](#twigjs-filters---default--) - - [date ->](#twigjs-filters---date--) - - [replace ->](#twigjs-filters---replace--) - - [format ->](#twigjs-filters---format--) - - [striptags ->](#twigjs-filters---striptags--) - - [escape ->](#twigjs-filters---escape--) - - [e ->](#twigjs-filters---e--) - - [nl2br ->](#twigjs-filters---nl2br--) - - [truncate ->](#twigjs-filters---truncate--) - - [trim ->](#twigjs-filters---trim--) - - [number_format ->](#twigjs-filters---number_format--) - - [slice ->](#twigjs-filters---slice--) - - [abs ->](#twigjs-filters---abs--) - - [first ->](#twigjs-filters---first--) - - [split ->](#twigjs-filters---split--) - - [batch ->](#twigjs-filters---batch--) - - [last ->](#twigjs-filters---last--) - - [raw ->](#twigjs-filters---raw--) - - [round ->](#twigjs-filters---round--) - - [Twig.js Loader ->](#twigjs-loader--) - - [source ->](#twigjs-loader---source--) - - [Twig.js Include ->](#twigjs-include--) - - [Twig.js Functions ->](#twigjs-functions--) - - [Built-in Functions ->](#twigjs-functions---built-in-functions--) - - [range ->](#twigjs-functions---built-in-functions---range--) - - [cycle ->](#twigjs-functions---built-in-functions---cycle--) - - [date ->](#twigjs-functions---built-in-functions---date--) - - [dump ->](#twigjs-functions---built-in-functions---dump--) - - [block ->](#twigjs-functions---built-in-functions---block--) - - [attribute ->](#twigjs-functions---built-in-functions---attribute--) - - [template_from_string ->](#twigjs-functions---built-in-functions---template_from_string--) - - [random ->](#twigjs-functions---built-in-functions---random--) - - [min, max ->](#twigjs-functions---built-in-functions---min-max--) - - [Twig.js Loaders ->](#twigjs-loaders--) - - [custom loader ->](#twigjs-loaders---custom-loader--) - - [Twig.js Macro ->](#twigjs-macro--) - - [Twig.js Namespaces ->](#twigjs-namespaces--) - - [Twig.js Optional Functionality ->](#twigjs-optional-functionality--) - - [Twig.js Parsers ->](#twigjs-parsers--) - - [custom parser ->](#twigjs-parsers---custom-parser--) - - [Twig.js Path ->](#twigjs-path--) - - [relativePath ->](#twigjs-path---relativepath--) - - [url ->](#twigjs-path---relativepath---url--) - - [path ->](#twigjs-path---relativepath---path--) - - [parsePath ->](#twigjs-path---parsepath--) - - [Twig.js Regression Tests ->](#twigjs-regression-tests--) - - [Twig.js Tags ->](#twigjs-tags--) - - [Twig.js Tests ->](#twigjs-tests--) - - [empty test ->](#twigjs-tests---empty-test--) - - [odd test ->](#twigjs-tests---odd-test--) - - [even test ->](#twigjs-tests---even-test--) - - [divisibleby test ->](#twigjs-tests---divisibleby-test--) - - [defined test ->](#twigjs-tests---defined-test--) - - [none test ->](#twigjs-tests---none-test--) - - [sameas test ->](#twigjs-tests---sameas-test--) - - [iterable test ->](#twigjs-tests---iterable-test--) -<a name=""></a> - -<a name="twigjs-blocks--"></a> -# Twig.js Blocks -> -should load a parent template and render the default values. - -```js -twig({ - id: 'remote-no-extends', - path: 'test/templates/template.twig', - async: false -}); -// Load the template -twig({ref: 'remote-no-extends'}).render({ }).should.equal( "Default Title - body" ); -``` - -should understand {% endblock title %} syntax. - -```js -twig({ - id: 'endblock-extended-syntax', - 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." ); -``` - -should load a child template and replace the parent block's content. - -```js -// Test loading a template from a remote endpoint -twig({ - id: 'child-extends', - path: 'test/templates/child.twig', - load: function(template) { - template.render({ base: "template.twig" }).should.equal( "Other Title - child" ); - done(); - } -}); -``` - -should have access to a parent block content. - -```js -// Test loading a template from a remote endpoint -twig({ - id: 'child-parent', - path: 'test/templates/child-parent.twig', - load: function(template) { - template.render({ - base: "template.twig", - inner: ':value' - }).should.equal( "Other Title - body:value:child" ); - done(); - } -}); -``` - -should include blocks from another template for horizontal reuse. - -```js -// Test 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!" ); - done(); - } -}); -``` - -should allow overriding of included blocks. - -```js -// Test overriding of included blocks -twig({ - id: 'use-override-block', - path: 'test/templates/use-override-block.twig', - load: function(template) { - // Load the template - template.render({ place: "diner" }).should.equal("Sorry, can't come to a diner today." ); - done(); - } -}); -``` - -should allow overriding of included nested blocks. - -```js -// Test overriding of included blocks -twig({ - id: 'use-override-nested-block', - path: 'test/templates/use-override-nested-block.twig', - load: function(template) { - // Load the template - template.render().should.equal("parent:new-child1:new-child2"); - done(); - } -}); -``` - -should make the contents of blocks available after they're rendered. - -```js -// Test rendering and loading one block -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!" ); - done(); - } -}); -``` - -should render nested blocks. - -```js -// Test rendering of blocks within blocks -twig({ - id: 'blocks-nested', - path: 'test/templates/blocks-nested.twig', - load: function(template) { - template.render({ }).should.equal( "parent:child" ) - done(); - } -}) -``` - -should render extended nested blocks. - -```js -// Test rendering of blocks within 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(); - } -}) -``` - -should be able to extend to a absolute template path. - -```js -// Test loading a template from a remote endpoint -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(); - } -}); -``` - -should extends blocks inline. - -```js -twig({ - id: 'inline-parent-template', - data: 'Title: {% block title %}parent{% endblock %}' -}); -twig({ - allowInlineIncludes: true, - data: '{% extends "inline-parent-template" %}{% block title %}child{% endblock %}' -}).render().should.equal("Title: child"); -``` - -<a name="twigjs-blocks---block-function--"></a> -## block function -> -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(); - } -}) -``` - -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(); - } -}) -``` - -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(); - } -}) -``` - -should respect changes of the context made before calling the function. - -```js -twig({ - data: '{% set foo = "original" %}{% block test %}{{ foo }}{% endblock %} {% set foo = "changed" %}{{ block("test") }}' -}).render() -.should.equal("original changed"); -``` - -<a name="twigjs-blocks---block-shorthand--"></a> -## block shorthand -> -should render block content using shorthand syntax. - -```js -twig({ - data: '{% set prefix = "shorthand" %}{% block title (prefix ~ " - " ~ block_value)|title %}' -}) -.render({block_value: 'test succeeded'}) -.should.equal('Shorthand - Test Succeeded'); -``` - -should overload blocks from an extended template using shorthand syntax. - -```js -twig({ - allowInlineIncludes: true, - data: '{% extends "child-extends" %}{% block title "New Title" %}{% block body "new body uses the " ~ base ~ " template" %}' -}) -.render({ base: "template.twig" }) -.should.equal( "New Title - new body uses the template.twig template" ); -``` - -<a name="twigjs-control-structures--"></a> -# Twig.js Control Structures -> -<a name="twigjs-control-structures---if-tag--"></a> -## if tag -> -should parse the contents of the if block if the expression is true. - -```js -var test_template = twig({data: '{% if test %}true{% endif%}'}); -test_template.render({test: true}).should.equal("true" ); -test_template.render({test: false}).should.equal("" ); -``` - -should call the if or else blocks based on the expression result. - -```js -var test_template = twig({data: '{% if test %}true{% endif%}'}); -test_template.render({test: true}).should.equal("true" ); -test_template.render({test: false}).should.equal("" ); -``` - -should support elseif. - -```js -var test_template = twig({data: '{% if test %}1{% elseif other %}2{%else%}3{% endif%}'}); -test_template.render({test: true, other:false}).should.equal("1" ); -test_template.render({test: true, other:true}).should.equal("1" ); -test_template.render({test: false, other:true}).should.equal("2" ); -test_template.render({test: false, other:false}).should.equal("3" ); -``` - -should be able to nest. - -```js -var test_template = twig({data: '{% if test %}{% if test2 %}true{% else %}false{% endif%}{% else %}not{% endif%}'}); -test_template.render({test: true, test2: true}).should.equal("true" ); -test_template.render({test: true, test2: false}).should.equal("false" ); -test_template.render({test: false, test2: true}).should.equal("not" ); -test_template.render({test: false, test2: false}).should.equal("not" ); -``` - -should support newlines in if statement. - -```js -var test_template = twig({data: '{% if test or\r\nother %}true{% endif%}'}); -test_template.render({test: true, other: false}).should.equal("true" ); -test_template.render({test: false, other: false}).should.equal("" ); -``` - -<a name="twigjs-control-structures---for-tag--"></a> -## for tag -> -should provide value only for array input. - -```js -var test_template = twig({data: '{% for value in test %}{{ value }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("1234" ); -test_template.render({test: []}).should.equal("" ); -``` - -should provide both key and value for array input. - -```js -var test_template = twig({data: '{% for key,value in test %}{{key}}:{{ value }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("0:11:22:33:4" ); -test_template.render({test: []}).should.equal("" ); -``` - -should provide value only for object input. - -```js -var test_template = twig({data: '{% for value in test %}{{ value }}{% endfor %}'}); -test_template.render({test: {one: 1, two: 2, three: 3}}).should.equal("123" ); -test_template.render({test: {}}).should.equal("" ); -``` - -should provide both key and value for object input. - -```js -var test_template = twig({data: '{% for key, value in test %}{{key}}:{{ value }}{% endfor %}'}); -test_template.render({test: {one: 1, two: 2, three: 3}}).should.equal("one:1two:2three:3" ); -test_template.render({test: {}}).should.equal("" ); -``` - -should support else if the input is empty. - -```js -var test_template = twig({data: '{% for key,value in test %}{{ value }}{% else %}else{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("1234" ); -test_template.render({test: []}).should.equal("else" ); -``` - -should be able to nest. - -```js -var test_template = twig({data: '{% for key,list in test %}{% for val in list %}{{ val }}{%endfor %}.{% else %}else{% endfor %}'}); -test_template.render({test: [[1,2],[3,4],[5,6]]}).should.equal("12.34.56." ); -test_template.render({test: []}).should.equal("else" ); -``` - -should have a loop context item available for arrays. - -```js -var test_template = twig({data: '{% for key,value in test %}{{ loop.index }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("1234" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.index0 }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("0123" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.revindex }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("4321" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.revindex0 }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("3210" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.length }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("4444" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.first }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("truefalsefalsefalse" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.last }}{% endfor %}'}); -test_template.render({test: [1,2,3,4]}).should.equal("falsefalsefalsetrue" ); -``` - -should have a loop context item available for objects. - -```js -var test_template = twig({data: '{% for key,value in test %}{{ loop.index }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("1234" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.index0 }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("0123" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.revindex }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("4321" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.revindex0 }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("3210" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.length }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("4444" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.first }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("truefalsefalsefalse" ); -test_template = twig({data: '{% for key,value in test %}{{ loop.last }}{% endfor %}'}); -test_template.render({test: {a:1,b:2,c:3,d:4}}).should.equal("falsefalsefalsetrue" ); -``` - -should have a loop context item available in child loops objects. - -```js -var test_template = twig({data: '{% for value in test %}{% for value in inner %}({{ loop.parent.loop.index }},{{ loop.index }}){% endfor %}{% endfor %}'}); -test_template.render({test: {a:1,b:2}, inner:[1,2,3]}).should.equal("(1,1)(1,2)(1,3)(2,1)(2,2)(2,3)"); -``` - -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"}, - b: {show:false, value: "two"}, - c: {show:true, value: "three"}}}).should.equal("a:one,c:three,"); -``` - -<a name="twigjs-control-structures---set-tag--"></a> -## set tag -> -should not set the global context from within a for loop. - -```js -var test_template = twig({data: '{% for value in [1] %}{% set foo="right" %}{% endfor %}{{ foo }}'}); -test_template.render().should.equal(""); -``` - -should set the global context from within a for loop when the variable is initialized outside of the loop. - -```js -var test_template = twig({data: '{% set foo="wrong" %}{% for value in [1] %}{% set foo="right" %}{% endfor %}{{ foo }}'}); -test_template.render().should.equal("right"); -``` - -should set the global context from within a nested for loop when the variable is initialized outside of the loop. - -```js -var test_template = twig({data: '{% set k = 0 %}{% for i in 0..2 %}{% for j in 0..2 %}{{ k }}{% set k = k + 1 %}{% endfor %}{% endfor %}'}); -test_template.render().should.equal("012345678"); -``` - -<a name="twigjs-core--"></a> -# Twig.js Core -> -should save and load a template by reference. - -```js -// Define and save a template - twig({ - id: 'test', - data: '{{ "test" }}' - }); - // Load and render the template - twig({ref: 'test'}).render() - .should.equal("test"); -``` - -should ignore comments. - -```js -twig({data: 'good {# comment #}morning'}).render().should.equal("good morning"); -twig({data: 'good{#comment#}morning'}).render().should.equal("goodmorning"); -``` - -should ignore output tags within comments. - -```js -twig({data: 'good {# {{ "Hello" }} #}morning'}).render().should.equal("good morning"); -twig({data: 'good{#c}}om{{m{{ent#}morning'}).render().should.equal("goodmorning"); -``` - -should ignore logic tags within comments. - -```js -twig({data: 'test {# {% bad syntax if not in comment %} #}test'}).render().should.equal("test test"); -twig({data: '{##}{##}test{# %}}}%}%{%{{% #}pass'}).render().should.equal("testpass"); -``` - -should ignore quotation marks within comments. - -```js -twig({data: "good {# don't stop #}morning"}).render().should.equal("good morning"); -twig({data: 'good{#"dont stop"#}morning'}).render().should.equal("goodmorning"); -twig({data: 'good {# "don\'t stop" #}morning'}).render().should.equal("good morning"); -twig({data: 'good{#"\'#}morning'}).render().should.equal("goodmorning"); -twig({data: 'good {#"\'"\'"\'#} day'}).render().should.equal("good day"); -twig({data: "a {# ' #}b{# ' #} c"}).render().should.equal("a b c"); -``` - -should be able to parse output tags with tag ends in strings. - -```js -// Really all we care about here is not throwing exceptions. -twig({data: '{{ "test" }}'}).render().should.equal("test"); -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(' "}} '); -``` - -should be able to parse whitespace control output tags. - -```js -twig({data: ' {{- "test" -}}'}).render().should.equal("test"); -twig({data: ' {{- "test" -}} '}).render().should.equal("test"); -twig({data: '\n{{- "test" -}}'}).render().should.equal("test"); -twig({data: '{{- "test" -}}\n'}).render().should.equal("test"); -twig({data: '\n{{- "test" -}}\n'}).render().should.equal("test"); -twig({data: '\t{{- "test" -}}\t'}).render().should.equal("test"); -twig({data: '\n\t{{- "test" -}}\n\t'}).render().should.equal("test"); -twig({data: '123\n\t{{- "test" -}}\n\t456'}).render().should.equal("123test456"); -twig({data: '\n{{- orp -}}\n'}).render({ orp: "test"}).should.equal("test"); -twig({data: '\n{{- [1,2 ,1+2 ] -}}\n'}).render().should.equal("1,2,3"); -twig({data: ' {{- "test" -}} {{- "test" -}}'}).render().should.equal("testtest"); -twig({data: '{{ "test" }} {{- "test" -}}'}).render().should.equal("testtest"); -twig({data: '{{- "test" -}} {{ "test" }}'}).render().should.equal("testtest"); -twig({data: '<>{{- "test" -}}<>'}).render().should.equal("<>test<>"); -``` - -should be able to parse mismatched opening whitespace control output tags. - -```js -twig({data: ' {{- "test" }}'}).render().should.equal("test"); -twig({data: '{{- "test" }}\n'}).render().should.equal("test\n"); -twig({data: '\t{{- "test" }}\t'}).render().should.equal("test\t"); -twig({data: '123\n\t{{- "test" }}\n\t456'}).render().should.equal("123test\n\t456"); -twig({data: '\n{{- [1,2 ,1+2 ] }}\n'}).render().should.equal("1,2,3\n"); -twig({data: ' {{- "test" }} {{- "test" }}'}).render().should.equal("testtest"); -twig({data: '{{ "test" }} {{- "test" }}'}).render().should.equal("testtest"); -twig({data: ' {{- "test" }} {{ "test" }}'}).render().should.equal("test test"); -twig({data: ' {{- "test" }} {{- "test" -}}'}).render().should.equal("testtest"); -twig({data: '<>{{- "test" }}'}).render().should.equal("<>test"); -``` - -should be able to parse mismatched closing whitespace control output tags. - -```js -twig({data: ' {{ "test" -}}'}).render().should.equal(" test"); -twig({data: '\n{{ "test" -}}\n'}).render().should.equal("\ntest"); -twig({data: '\t{{ "test" -}}\t'}).render().should.equal("\ttest"); -twig({data: '123\n\t{{ "test" -}}\n\t456'}).render().should.equal("123\n\ttest456"); -twig({data: '\n{{ [1,2 ,1+2 ] -}}\n'}).render().should.equal("\n1,2,3"); -twig({data: ' {{ "test" -}} {{ "test" -}}'}).render().should.equal(" testtest"); -twig({data: '{{ "test" }} {{ "test" -}} '}).render().should.equal("test test"); -twig({data: ' {{ "test" -}} {{ "test" }} '}).render().should.equal(" testtest "); -twig({data: ' {{ "test" -}} {{- "test" -}}'}).render().should.equal(" testtest"); -twig({data: '{{ "test" -}}<>'}).render().should.equal("test<>"); -``` - -should be able to parse whitespace control logic tags. - -```js -// Newlines directly after logic tokens are ignored -// So use double newlines -twig({data: '{%- if true -%}{{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: '{%- if true -%}{{ "test" }}{%- endif -%}'}).render().should.equal("test"); -twig({data: ' {%- if true -%} {{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: '\n{%- if true -%}\n\n{{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: '\n\t{%- if true -%}\n\n\t{{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: '123\n\t{%- if true -%}\n\n\t{{ "test" }}{% endif %}456'}).render().should.equal("123test456"); -twig({data: '\n\t{%- if true -%}\n\n\t{{ [1,2 ,1+2 ] }}{% endif %}'}).render().should.equal("1,2,3"); -twig({data: '<>{%- if true -%}test{% endif %}<>'}).render().should.equal("<>test<>"); -``` - -should be able to parse mismatched opening whitespace control logic tags. - -```js -twig({data: '{%- if true %}{{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: '{%- if true %}{{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: ' {% if true %} {{ "test" }}{% endif %}'}).render().should.equal(" test"); -twig({data: ' {%- if true %} {{ "test" }}{% endif %}'}).render().should.equal(" test"); -twig({data: '\n{% if true %}\n\n{{ "test" }}{% endif %}'}).render().should.equal("\n\ntest"); -twig({data: '\n{%- if true %}\n\n{{ "test" }}{% endif %}'}).render().should.equal("\ntest"); -twig({data: '\n\t{%- if true %}\n\n\t{{ "test" }}{% endif %}'}).render().should.equal("\n\ttest"); -twig({data: '123\n\t{%- if true %}\n\n\t{{ "test" }}{% endif %}456'}).render().should.equal("123\n\ttest456"); -twig({data: '\n\t{%- if true %}\n\n\t{{ [1,2 ,1+2 ] }}{% endif %}'}).render().should.equal("\n\t1,2,3"); -twig({data: '<>{%- if true %}test{% endif %}'}).render().should.equal("<>test"); -``` - -should be able to parse mismatched closing whitespace control logic tags. - -```js -twig({data: '{% if true %}{{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: '{% if true -%} {{ "test" }}{% endif %}'}).render().should.equal("test"); -twig({data: ' {% if true -%} {{ "test" }}{% endif %}'}).render().should.equal(" test"); -twig({data: ' {% if true -%} {{ "test" }}{% endif %}'}).render().should.equal(" test"); -twig({data: '\n{% if true %}\n\n{{ "test" }}{% endif %}'}).render().should.equal("\n\ntest"); -twig({data: '\n{% if true -%}\n\n{{ "test" }}{% endif %}'}).render().should.equal("\ntest"); -twig({data: '\n\t{% if true -%}\n\n\t{{ "test" }}{% endif %}'}).render().should.equal("\n\ttest"); -twig({data: '123\n\t{% if true -%}\n\n\t{{ "test" }}{% endif %}456'}).render().should.equal("123\n\ttest456"); -twig({data: '\n\t{% if true -%}\n\n\t{{ [1,2 ,1+2 ] }}{% endif %}'}).render().should.equal("\n\t1,2,3"); -twig({data: '{% if true -%}<>test{% endif %}'}).render().should.equal("<>test"); -``` - -should be able to output numbers. - -```js -twig({data: '{{ 12 }}'}).render().should.equal( "12" ); -twig({data: '{{ 12.64 }}'}).render().should.equal( "12.64" ); -twig({data: '{{ 0.64 }}'}).render().should.equal("0.64" ); -``` - -should be able to output booleans. - -```js -twig({data: '{{ true }}'}).render().should.equal( "true" ); -twig({data: '{{ false }}'}).render().should.equal( "false" ); -``` - -should be able to output strings. - -```js -twig({data: '{{ "double" }}'}).render().should.equal("double"); -twig({data: "{{ 'single' }}"}).render().should.equal('single'); -twig({data: '{{ "dou\'ble" }}'}).render().should.equal("dou'ble"); -twig({data: "{{ 'sin\"gle' }}"}).render().should.equal('sin"gle'); -twig({data: '{{ "dou\\"ble" }}'}).render().should.equal("dou\"ble"); -twig({data: "{{ 'sin\\'gle' }}"}).render().should.equal("sin'gle"); -``` - -should be able to output strings with newlines. - -```js -twig({data: "{{ 'a\nb\rc\r\nd' }}"}).render().should.equal("a\nb\rc\r\nd"); -``` - -should be able to output arrays. - -```js -twig({data: '{{ [1] }}'}).render().should.equal("1" ); -twig({data: '{{ [1,2 ,3 ] }}'}).render().should.equal("1,2,3" ); -twig({data: '{{ [1,2 ,3 , val ] }}'}).render({val: 4}).should.equal("1,2,3,4" ); -twig({data: '{{ ["[to",\'the\' ,"string]" ] }}'}).render().should.equal('[to,the,string]' ); -twig({data: '{{ ["[to",\'the\' ,"str\\"ing]" ] }}'}).render().should.equal('[to,the,str"ing]' ); -``` - -should be able to output parse expressions in an array. - -```js -twig({data: '{{ [1,2 ,1+2 ] }}'}).render().should.equal("1,2,3" ); -twig({data: '{{ [1,2 ,3 , "-", [4,5, 6] ] }}'}).render({val: 4}).should.equal("1,2,3,-,4,5,6" ); -twig({data: '{{ [a,b ,(1+2) * a ] }}'}).render({a:1,b:2}).should.equal("1,2,3" ); -``` - -should be able to output variables. - -```js -twig({data: '{{ orp }}'}).render({ orp: "test"}).should.equal("test"); -twig({data: '{{ val }}'}).render({ val: function() { - return "test" - }}).should.equal("test"); -``` - -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" ); -``` - -should recognize object literals. - -```js -twig({data: '{% set at = {"foo": "test", bar: "other", 1:"zip"} %}{{ at.foo ~ at.bar ~ at.1 }}'}).render().should.equal( "testotherzip" ); -``` - -should allow newlines in object literals. - -```js -twig({data: '{% set at = {\n"foo": "test",\rbar: "other",\r\n1:"zip"\n} %}{{ at.foo ~ at.bar ~ at.1 }}'}).render().should.equal( "testotherzip" ); -``` - -should recognize null in an object. - -```js -twig({data: '{% set at = {"foo": null} %}{{ at.foo == val }}'}).render({val: null}).should.equal( "true" ); -``` - -should allow int 0 as a key in an object. - -```js -twig({data: '{% set at = {0: "value"} %}{{ at.0 }}'}).render().should.equal( "value" ); -``` - -should support set capture. - -```js -twig({data: '{% set foo %}bar{% endset %}{{foo}}'}).render().should.equal( "bar" ); -``` - -should support raw data. - -```js -twig({ - data: "before {% raw %}{{ test }} {% test2 %} {{{% endraw %} after" -}).render().should.equal( - "before {{ test }} {% test2 %} {{ after" -); -``` - -should support raw data using 'verbatim' tag. - -```js -twig({ - data: "before {% verbatim %}{{ test }} {% test2 %} {{{% endverbatim %} after" -}).render().should.equal( - "before {{ test }} {% test2 %} {{ after" -); -``` - -<a name="twigjs-core---key-notation--"></a> -## Key Notation -> -should support dot key notation. - -```js -twig({data: '{{ key.value }} {{ key.sub.test }}'}).render({ - key: { - value: "test", - sub: { - test: "value" - } - } -}).should.equal("test value"); -``` - -should support square bracket key notation. - -```js -twig({data: '{{ key["value"] }} {{ key[\'sub\']["test"] }}'}).render({ - key: { - value: "test", - sub: { - test: "value" - } - } -}).should.equal("test value"); -``` - -should support mixed dot and bracket key notation. - -```js -twig({data: '{{ key["value"] }} {{ key.sub[key.value] }} {{ s.t["u"].v["w"] }}'}).render({ - key: { - value: "test", - sub: { - test: "value" - } - }, - s: { t: { u: { v: { w: 'x' } } } } -}).should.equal("test value x" ); -``` - -should support dot key notation after a function. - -```js -var test_template = twig({data: '{{ key.fn().value }}'}); -var output = test_template.render({ - key: { - fn: function() { - return { - value: "test" - } - } - } -}); -output.should.equal("test"); -``` - -should support bracket key notation after a function. - -```js -var test_template = twig({data: '{{ key.fn()["value"] }}'}); -var output = test_template.render({ - key: { - fn: function() { - return { - value: "test 2" - } - } - } -}); -output.should.equal("test 2"); -``` - -should check for getKey methods if a key doesn't exist.. - -```js -twig({data: '{{ obj.value }}'}).render({ - obj: { - getValue: function() { - return "val"; - }, - isValue: function() { - return "not val"; - } - } -}).should.equal("val"); -``` - -should check for isKey methods if a key doesn't exist.. - -```js -twig({data: '{{ obj.value }}'}).render({ - obj: { - isValue: function() { - return "val"; - } - } -}).should.equal("val"); -``` - -should check for getKey methods on prototype objects.. - -```js -var object = { - getValue: function() { - return "val"; - } - }; -function Subobj() {}; -Subobj.prototype = object; -var subobj = new Subobj(); - - twig({data: '{{ obj.value }}'}).render({ - obj: subobj - }).should.equal("val"); -``` - -should return null if a period key doesn't exist.. - -```js -twig({data: '{{ obj.value == null }}'}).render({ - obj: {} -}).should.equal("true"); -``` - -should return null if a bracket key doesn't exist.. - -```js -twig({data: '{{ obj["value"] == null }}'}).render({ - obj: {} -}).should.equal("true"); -``` - -<a name="twigjs-core---context--"></a> -## Context -> -should be supported. - -```js -twig({data: '{{ _context.value }}'}).render({ - value: "test" -}).should.equal("test"); -``` - -should be an object even if it's not passed. - -```js -twig({data: '{{ _context|json_encode }}'}).render().should.equal("{}"); -``` - -should support {% set %} tag. - -```js -twig({data: '{% set value = "test" %}{{ _context.value }}'}).render().should.equal("test"); -``` - -should work correctly with properties named dynamically. - -```js -twig({data: '{{ _context[key] }}'}).render({ - key: "value", - value: "test" -}).should.equal("test"); -``` - -should not allow to override context using {% set %}. - -```js -twig({data: '{% set _context = "test" %}{{ _context|json_encode }}'}).render().should.equal('{"_context":"test"}'); -twig({data: '{% set _context = "test" %}{{ _context._context }}'}).render().should.equal("test"); -``` - -should support autoescape option. - -```js -twig({ - autoescape: true, - data: '{{ value }}' -}).render({ - value: "<test>&</test>" -}).should.equal('<test>&</test>'); -``` - -should support autoescape option with alternative strategy. - -```js -twig({ - autoescape: 'js', - data: '{{ value }}' -}).render({ - value: "<test>&</test>" -}).should.equal('\\x3Ctest\\x3E\\x26\\x3C\\x2Ftest\\x3E'); -``` - -should autoescape parent() output correctly. - -```js -twig({id: 'parent1', data: '{% block body %}<p>{{ value }}</p>{% endblock body %}'}); -twig({ - allowInlineIncludes: true, - autoescape: true, - data: '{% extends "parent1" %}{% block body %}{{ parent() }}{% endblock %}' -}).render({ - value: "<test>&</test>" -}).should.equal('<p><test>&</test></p>'); -``` - -should use a correct context in the extended template. - -```js -twig({id: 'parent', data: '{% block body %}{{ value }}{% endblock body %}'}); -twig({ - allowInlineIncludes: true, - data: '{% extends "parent" %}{% set value = "test" %}{% block body %}{{ parent() }}{% endblock %}' -}).render().should.equal("test"); -``` - -should use a correct context in the included template. - -```js -twig({id: 'included', data: '{{ value }}\n{% set value = "inc" %}{{ value }}\n'}); -twig({ - allowInlineIncludes: true, - data: '{% set value = "test" %}{% for i in [0, 1] %}{% include "included" %}{% endfor %}{{ value }}' -}).render().should.equal("test\ninc\ntest\ninc\ntest"); -``` - -<a name="twigjs-embed--"></a> -# Twig.js Embed -> -it should load embed and render. - -```js -twig({ - id: 'embed', - path: 'test/templates/embed-simple.twig', - async: false -}); -// Load the template -twig({ref: 'embed'}).render({ }).trim().should.equal( ['START', - 'A', - 'new header', - 'base footer', - 'B', - '', - 'A', - 'base header', - 'base footer', - 'extended', - 'B', - '', - 'A', - 'base header', - 'extended', - 'base footer', - 'extended', - 'B', - '', - 'A', - 'Super cool new header', - 'Cool footer', - 'B', - 'END'].join('\n') ); -``` - -<a name="twigjs-expressions--"></a> -# Twig.js Expressions -> -<a name="twigjs-expressions---basic-operators--"></a> -## Basic Operators -> -should parse parenthesis. - -```js -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() ); -``` - -should parse nested parenthesis. - -```js -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() ); -``` - -should add numbers. - -```js -var test_template = twig({data: '{{ a + b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal( (pair.a + pair.b).toString() ); -}); -``` - -should subtract numbers. - -```js -var test_template = twig({data: '{{ a - b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal( (pair.a - pair.b).toString() ); -}); -``` - -should multiply numbers. - -```js -var test_template = twig({data: '{{ a * b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a * pair.b).toString() ); -}); -``` - -should divide numbers. - -```js -var test_template = twig({data: '{{ a / b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a / pair.b).toString() ); -}); -``` - -should divide numbers and return an int result. - -```js -var test_template = twig({data: '{{ a // b }}'}); -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() ); -}); -``` - -should raise numbers to a power. - -```js -var test_template = twig({data: '{{ a ** b }}'}); -var pow_test_data = [ - {a: 2, b:3, c: 8} - , {a: 4, b:.5, c: 2} - , {a: 5, b: 1, c: 5} -]; -pow_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal(pair.c.toString() ); -}); -``` - -should concatanate values. - -```js -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) { - var output = test_template.render(pair); - output.should.equal(pair.a.toString() + pair.b.toString()); -}); -// Test strings -test_template = twig({data: '{{ a ~ b }}'}); -string_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal(pair.a.toString() + pair.b.toString()); -}); -``` - -should concatenate null and undefined values and not throw an exception. - -```js -twig({data: '{{ a ~ b }}'}).render().should.equal(""); -twig({data: '{{ a ~ b }}'}).render({ - a: null, - b: null -}).should.equal(""); -``` - -should handle multiple chained operations. - -```js -var data = {a: 4.5, b: 10, c: 12, d: -0.25, e:0, f: 65, g: 21, h: -0.0002}; -var test_template = twig({data: '{{a/b+c*d-e+f/g*h}}'}); -var output = test_template.render(data); -var expected = data.a / data.b + data.c * data.d - data.e + data.f / data.g * data.h; -output.should.equal(expected.toString()); -``` - -should handle parenthesis in chained operations. - -```js -var data = {a: 4.5, b: 10, c: 12, d: -0.25, e:0, f: 65, g: 21, h: -0.0002}; -var test_template = twig({data: '{{a /(b+c )*d-(e+f)/(g*h)}}'}); -var output = test_template.render(data); -var expected = data.a / (data.b + data.c) * data.d - (data.e + data.f) / (data.g * data.h); -output.should.equal(expected.toString()); -``` - -<a name="twigjs-expressions---comparison-operators--"></a> -## Comparison Operators -> -should support less then. - -```js -var test_template = twig({data: '{{ a < b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a < pair.b).toString() ); -}); -``` - -should support less then or equal. - -```js -var test_template = twig({data: '{{ a <= b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a <= pair.b).toString() ); -}); -``` - -should support greater then. - -```js -var test_template = twig({data: '{{ a > b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a > pair.b).toString() ); -}); -``` - -should support greater then or equal. - -```js -var test_template = twig({data: '{{ a >= b }}'}); -numeric_test_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a >= pair.b).toString() ); -}); -``` - -should support equals. - -```js -var test_template = twig({data: '{{ a == b }}'}); -boolean_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a == pair.b).toString() ); -}); -equality_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a == pair.b).toString() ); -}); -``` - -should support not equals. - -```js -var test_template = twig({data: '{{ a != b }}'}); -boolean_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a != pair.b).toString() ); -}); -equality_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a != pair.b).toString() ); -}); -``` - -should support boolean or. - -```js -var test_template = twig({data: '{{ a or b }}'}); -boolean_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a || pair.b).toString() ); -}); -``` - -should support boolean and. - -```js -var test_template = twig({data: '{{ a and b }}'}); -boolean_data.forEach(function(pair) { - var output = test_template.render(pair); - output.should.equal((pair.a && pair.b).toString() ); -}); -``` - -should support boolean not. - -```js -var test_template = twig({data: '{{ not a }}'}); -test_template.render({a:false}).should.equal(true.toString()); -test_template.render({a:true}).should.equal(false.toString()); -``` - -<a name="twigjs-expressions---other-operators--"></a> -## Other Operators -> -should support the ternary operator. - -```js -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" ); -``` - -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" ); -``` - -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" ); -``` - -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()); -``` - -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()); -``` - -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()); -``` - -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()); -``` - -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()); -``` - -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()); -``` - -should support undefined and null for the in operator. - -```js -var test_template = twig({data: '{{ 0 in undefined }} {{ 0 in null }}'}); -test_template.render().should.equal(' '); -``` - -should support expressions as object keys. - -```js -var test_template; -test_template = twig({data: '{% set a = {(foo): "value"} %}{{ a.bar }}'}); -test_template.render({foo: 'bar'}).should.equal('value'); -test_template = twig({data: '{{ {(foo): "value"}.bar }}'}); -test_template.render({foo: 'bar'}).should.equal('value'); -``` - -<a name="twigjs-extensions--"></a> -# Twig.js Extensions -> -should be able to extend a meta-type tag. - -```js -var flags = {}; - Twig.extend(function(Twig) { - Twig.exports.extendTag({ - type: "flag", - regex: /^flag\s+(.+)$/, - next: [ ], - open: true, - compile: function (token) { - var expression = token.match[1]; - - // Compile the expression. - 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 name = Twig.expression.parse.apply(this, [token.stack, context]), - output = ''; - - flags[name] = true; - - return { - chain: false, - output: output - }; - } - }); - }); - 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({ - type: "auth", - regex: /^auth\s+(.+)$/, - next: ["endauth"], // match the type of the end tag - open: true, - compile: function (token) { - var expression = token.match[1]; - - // turn the string expression into tokens. - 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 level = Twig.expression.parse.apply(this, [token.stack, context]), - output = ""; - - if (App.users[App.currentUser].level == level) - { - output = Twig.parse.apply(this, [token.output, context]); - } - - return { - chain: chain, - output: output - }; - } - }); - Twig.exports.extendTag({ - type: "endauth", - regex: /^endauth$/, - next: [ ], - open: false - }); - }); - var template = twig({data:"Welcome{% auth 'admin' %} ADMIN{% endauth %}!"}); - - App.currentUser = "john"; - template.render().should.equal("Welcome ADMIN!"); - - App.currentUser = "tom"; - template.render().should.equal("Welcome!"); -``` - -should be able to extend the same tag twice, replacing it. - -```js -var flags = {}; -Twig.extend(function(Twig) { - Twig.exports.extendTag({ - type: "noop", - regex: /^noop$/, - next: [ ], - open: true, - parse: function (token, context, chain) { - return { - chain: false, - output: "noop1" - }; - } - }); -}); -var result = twig({data:"{% noop %}"}).render(); -result.should.equal("noop1"); -Twig.extend(function(Twig) { - Twig.exports.extendTag({ - type: "noop", - regex: /^noop$/, - next: [ ], - open: true, - parse: function (token, context, chain) { - return { - chain: false, - output: "noop2" - }; - } - }); -}); -var result = twig({data:"{% noop %}"}).render(); -result.should.equal("noop2"); -``` - -<a name="twigjs-filters--"></a> -# Twig.js Filters -> -should chain. - -```js -var test_template = twig({data: '{{ ["a", "b", "c"]|keys|reverse }}' }); -test_template.render().should.equal("2,1,0"); -``` - -<a name="twigjs-filters---url_encode--"></a> -## url_encode -> -should encode URLs. - -```js -var test_template = twig({data: '{{ "http://google.com/?q=twig.js"|url_encode() }}' }); -test_template.render().should.equal("http%3A%2F%2Fgoogle.com%2F%3Fq%3Dtwig.js" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|url_encode() }}' }); -test_template.render().should.equal("" ); -``` - -should handle special characters. - -```js -var data = { "foo": "<foo> \\&\"'.,-_?/Ķä€台北[]{}\t\r\n\b\x80" }; -var test_template = twig({data: '{{ foo|url_encode() }}' }); -test_template.render(data).should.equal("%3Cfoo%3E%20%5C%26%22%27.%2C-_%3F%2F%C4%B6%C3%A4%E2%82%AC%E5%8F%B0%E5%8C%97%5B%5D%7B%7D%09%0D%0A%08%C2%80" ); -``` - -<a name="twigjs-filters---json_encode--"></a> -## json_encode -> -should encode strings to json. - -```js -var test_template = twig({data: '{{ test|json_encode }}' }); -test_template.render({test:'value'}).should.equal('"value"' ); -``` - -should encode numbers to json. - -```js -var test_template = twig({data: '{{ test|json_encode }}' }); -test_template.render({test:21}).should.equal('21' ); -``` - -should encode arrays to json. - -```js -var test_template = twig({data: '{{ [1,"b",3]|json_encode }}' }); -test_template.render().should.equal('[1,"b",3]' ); -``` - -should encode objects to json. - -```js -var test_template = twig({data: '{{ {"a":[1,"b",3]}|json_encode }}' }); -test_template.render().should.equal('{"a":[1,"b",3]}' ); -``` - -should retain key order in an object. - -```js -twig({data: '{{ { "foo": 1, "bar": 2, "baz": 3 }|json_encode }}'}).render().should.equal( '{"foo":1,"bar":2,"baz":3}' ); -``` - -should not add additional information to objects. - -```js -twig({data: '{{ { "foo": 1, "bar": [1, 2, 3], "baz": { "a": "a", "b": "b" } }|json_encode }}'}).render().should.equal( '{"foo":1,"bar":[1,2,3],"baz":{"a":"a","b":"b"}}' ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|json_encode }}' }); -test_template.render().should.equal("null" ); -``` - -<a name="twigjs-filters---upper--"></a> -## upper -> -should convert text to uppercase. - -```js -var test_template = twig({data: '{{ "hello"|upper }}' }); -test_template.render().should.equal("HELLO" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|upper }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---lower--"></a> -## lower -> -should convert text to lowercase. - -```js -var test_template = twig({data: '{{ "HELLO"|lower }}' }); -test_template.render().should.equal("hello" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|lower }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---capitalize--"></a> -## capitalize -> -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" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|capitalize }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---title--"></a> -## title -> -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" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|title }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---length--"></a> -## length -> -should determine the length of a string. - -```js -var test_template = twig({data: '{{ "test"|length }}' }); -test_template.render().should.equal("4"); -``` - -should determine the length of an array. - -```js -var test_template = twig({data: '{{ [1,2,4,76,"tesrt"]|length }}' }); -test_template.render().should.equal("5"); -``` - -should determine the length of an object. - -```js -var test_template = twig({data: '{{ {"a": "b", "c": "1", "test": "test"}|length }}' }); -test_template.render().should.equal("3"); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|length }}' }); -test_template.render().should.equal("0" ); -``` - -<a name="twigjs-filters---sort--"></a> -## sort -> -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" ); -``` - -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 " ); -test_template = twig({data: "{% set obj = {'z':'abc','a':2,'y':7,'m':'test'} %}{% for key,value in obj|sort %}{{key}}:{{value}} {%endfor %}" }); -test_template.render().should.equal("a:2 y:7 z:abc m:test " ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{% set obj = undef|sort %}{% for key, value in obj|sort %}{{key}}:{{value}}{%endfor%}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---reverse--"></a> -## reverse -> -should reverse an array. - -```js -var test_template = twig({data: '{{ ["a", "b", "c"]|reverse }}' }); -test_template.render().should.equal("c,b,a" ); -``` - -should reverse an object. - -```js - -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|reverse }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---keys--"></a> -## keys -> -should return the keys of an array. - -```js -var test_template = twig({data: '{{ ["a", "b", "c"]|keys }}' }); -test_template.render().should.equal("0,1,2" ); -``` - -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" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|keys }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---merge--"></a> -## merge -> -should merge two objects into an object. - -```js -// Object merging -var test_template = twig({data: '{% set obj= {"a":"test", "b":"1"}|merge({"b":2,"c":3}) %}{% for key in obj|keys|sort %}{{key}}:{{obj[key]}} {%endfor %}' }); -test_template.render().should.equal('a:test b:2 c:3 ' ); -``` - -should merge two arrays into and array. - -```js -// Array merging -var test_template = twig({data: '{% set obj= ["a", "b"]|merge(["c", "d"]) %}{% for key in obj|keys|sort %}{{key}}:{{obj[key]}} {%endfor %}' }); -test_template.render().should.equal('0:a 1:b 2:c 3:d ' ); -``` - -should merge an object and an array into an object. - -```js -// 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 ' ); -``` - -<a name="twigjs-filters---join--"></a> -## join -> -should join all values in an object. - -```js -var test_template = twig({data: '{{ {"a":"1", "b": "b", "c":test}|join("-") }}' }); -test_template.render({"test": "t"}).should.equal("1-b-t" ); -``` - -should joing all values in an array. - -```js -var test_template = twig({data: '{{ [1,2,4,76]|join }}' }); -test_template.render().should.equal("12476" ); -test_template = twig({data: '{{ [1+ 5,2,4,76]|join("-" ~ ".") }}' }); -test_template.render().should.equal("6-.2-.4-.76" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|join }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---default--"></a> -## default -> -should not provide the default value if a key is defined and not empty. - -```js -var test_template = twig({data: '{{ var|default("Not Defined") }}' }); -test_template.render({"var":"value"}).should.equal("value" ); -``` - -should provide a default value if a key is not defined. - -```js -var test_template = twig({data: '{{ var|default("Not Defined") }}' }); -test_template.render().should.equal("Not Defined" ); -``` - -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" ); -``` - -should provide a default value of '' if no parameters are passed and a default key is not defined. - -```js -var test_template = twig({data: '{{ var|default }}' }); -test_template.render().should.equal(""); -``` - -should provide a default value of '' if no parameters are passed and a value is empty. - -```js -var test_template = twig({data: '{{ ""|default }}' }); -test_template.render().should.equal(""); -test_template = twig({data: '{{ var.key|default }}' }); -test_template.render({'var':{}}).should.equal(""); -``` - -<a name="twigjs-filters---date--"></a> -## date -> -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) ); -``` - -should recognize timestamps, when they are passed as string. - -```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) ); -``` - -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) ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|date("d/m/Y @ H:i:s") }}' }); -var date = new Date(); -test_template.render().should.equal(stringDate(date)); -``` - -should work with no parameters. - -```js -var test_template = twig({data: '{{ 27571323556|date }}' }); -test_template.render().should.equal(twig({data: '{{ 27571323556|date("F j, Y H:i") }}'}).render()); -``` - -<a name="twigjs-filters---replace--"></a> -## replace -> -should replace strings provided in a map. - -```js -var template = twig({data: '{{ "I like %this% and %that%. Seriously, I like %this% and %that%."|replace({"%this%": foo, "%that%": "bar"}) }}'}); -template.render({foo: "foo"}).should.equal("I like foo and bar. Seriously, I like foo and bar." ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|replace }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---format--"></a> -## format -> -should replace formatting tags with parameters. - -```js -var template = twig({data: '{{ "I like %s and %s."|format(foo, "bar") }}'}); -template.render({foo: "foo"}).should.equal("I like foo and bar." ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|format }}' }); -test_template.render().should.equal("" ); -``` - -should handle positive leading sign without padding. - -```js -var template = twig({data: '{{ "I like positive numbers like %+d."|format(123) }}'}); -template.render({foo: "foo"}).should.equal("I like positive numbers like +123." ); -``` - -should handle negative leading sign without padding. - -```js -var template = twig({data: '{{ "I like negative numbers like %+d."|format(-123) }}'}); -template.render({foo: "foo"}).should.equal("I like negative numbers like -123." ); -``` - -should handle positive leading sign with padding zero. - -```js -var template = twig({data: '{{ "I like positive numbers like %+05d."|format(123) }}'}); -template.render({foo: "foo"}).should.equal("I like positive numbers like +0123." ); -``` - -should handle negative leading sign with padding zero. - -```js -var template = twig({data: '{{ "I like negative numbers like %+05d."|format(-123) }}'}); -template.render({foo: "foo"}).should.equal("I like negative numbers like -0123." ); -``` - -should handle positive leading sign with padding space. - -```js -var template = twig({data: '{{ "I like positive numbers like %+5d."|format(123) }}'}); -template.render({foo: "foo"}).should.equal("I like positive numbers like +123." ); -``` - -should handle negative leading sign with padding space. - -```js -var template = twig({data: '{{ "I like negative numbers like %+5d."|format(-123) }}'}); -template.render({foo: "foo"}).should.equal("I like negative numbers like -123." ); -``` - -<a name="twigjs-filters---striptags--"></a> -## striptags -> -should remove tags from a value. - -```js -var template = twig({data: '{{ "<p>Test paragraph.</p><!-- Comment --> <a href=\\"#fragment\\">Other text</a>"|striptags }}'}); -template.render().should.equal("Test paragraph. Other text" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|striptags }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---escape--"></a> -## escape -> -should convert unsafe characters to HTML entities. - -```js -var template = twig({data: '{{ "<p>Test paragraph.</p><!-- Comment --> <a href=\'#fragment\'>Other text</a>"|escape }}'}); -template.render().should.equal("<p>Test paragraph.</p><!-- Comment --> <a href='#fragment\'>Other text</a>" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|escape }}' }); -test_template.render().should.equal("" ); -``` - -should not escape twice if autoescape is on. - -```js -twig({ - autoescape: true, - data: '{{ value|escape }}' -}).render({ - value: "<test>&</test>" -}).should.equal('<test>&</test>'); -``` - -should handle the strategy parameter. - -```js -var data = { "foo": "<foo> \\&\"'.,-_?/Ķä€台北[]{}\t\r\n\b\x80" }; -var test_template = twig({data: 'Default: {{ foo|escape }}' }); -test_template.render(data).should.equal("Default: <foo> \\&"'.,-_?/Ķä€台北[]{}\t\r\n\b\x80" ); -var test_template = twig({data: 'html: {{ foo|escape("html") }}' }); -test_template.render(data).should.equal("html: <foo> \\&"'.,-_?/Ķä€台北[]{}\t\r\n\b\x80" ); -var test_template = twig({data: 'js: {{ foo|escape("js") }}' }); -test_template.render(data).should.equal("js: \\x3Cfoo\\x3E\\x20\\x5C\\x26\\x22\\x27.,\\x2D_\\x3F\\x2F\\u0136\\u00E4\\u20AC\\u53F0\\u5317\\x5B\\x5D\\x7B\\x7D\\x9\\xD\\xA\\x8\\u0080" ); -var test_template = twig({data: 'css: {{ foo|escape("css") }}' }); -test_template.render(data).should.equal("css: \\3C foo\\3E \\20 \\5C \\26 \\22 \\27 \\2E \\2C \\2D \\5F \\3F \\2F \\136 \\E4 \\20AC \\53F0 \\5317 \\5B \\5D \\7B \\7D \\9 \\D \\A \\8 \\80 " ); -var test_template = twig({data: 'url: {{ foo|escape("url") }}' }); -test_template.render(data).should.equal("url: %3Cfoo%3E%20%5C%26%22%27.%2C-_%3F%2F%C4%B6%C3%A4%E2%82%AC%E5%8F%B0%E5%8C%97%5B%5D%7B%7D%09%0D%0A%08%C2%80" ); -var test_template = twig({data: 'html_attr: {{ foo|escape("html_attr") }}' }); -test_template.render(data).should.equal("html_attr: <foo> \&"'.,-_?/Ķä€台北[]{}	
�€" ); -``` - -should escape strategy != 'html' if autoescape is on. - -```js -twig({ - autoescape: true, - data: '{{ value|escape("js") }}' -}).render({ - value: "<test>&</test>" -}).should.equal('\\x3Ctest\\x3E\\x26\\x3C\\x2Ftest\\x3E'); -``` - -should not escape twice if autoescape is not html. - -```js -twig({ - autoescape: 'js', - data: '{{ value|escape("js") }}' -}).render({ - value: "<test>&</test>" -}).should.equal('\\x3Ctest\\x3E\\x26\\x3C\\x2Ftest\\x3E'); -``` - -should escape twice if escape strategy is different from autoescape option. - -```js -twig({ - autoescape: 'css', - data: '{{ value|escape("js") }}\n{{ value|escape }}' -}).render({ - value: "<test>&</test>" -}).should.equal('\\5C x3Ctest\\5C x3E\\5C x26\\5C x3C\\5C x2Ftest\\5C x3E\n\\26 lt\\3B test\\26 gt\\3B \\26 amp\\3B \\26 lt\\3B \\2F test\\26 gt\\3B '); -``` - -<a name="twigjs-filters---e--"></a> -## e -> -should alias escape function with e. - -```js -var template = twig({data: '{{ "<p>Test paragraph.</p><!-- Comment --> <a href=\'#fragment\'>Other text</a>"|e }}'}); -template.render().should.equal("<p>Test paragraph.</p><!-- Comment --> <a href='#fragment\'>Other text</a>" ); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|e }}' }); -test_template.render().should.equal("" ); -``` - -should not escape twice if autoescape is on. - -```js -var template = twig({ - autoescape: true, - data: '{{ value|e }}' -}); -template.render({ - value: "<test>&</test>" -}).should.equal('<test>&</test>'); -``` - -<a name="twigjs-filters---nl2br--"></a> -## nl2br -> -should convert newlines into html breaks. - -```js -var template = twig({data: '{{ test|nl2br }}'}); -template.render({ test: 'Line 1\r\nLine 2\nLine 3\rLine 4\n\n' }) - .should.equal("Line 1<br />\nLine 2<br />\nLine 3<br />\nLine 4<br />\n<br />\n"); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|nl2br }}' }); -test_template.render().should.equal("" ); -``` - -should not escape br tags if autoescape is on. - -```js -twig({ - autoescape: true, - data: '{{ test|nl2br }}' -}).render({ - test: '<test>Line 1\nLine2</test>' -}).should.equal("<test>Line 1<br />\nLine2</test>"); -``` - -<a name="twigjs-filters---truncate--"></a> -## truncate -> -should truncate string to default size(20) and add default separator. - -```js -var template = twig({data: '{{ test|truncate }}'}); -template.render({test: '01234567890123456789012345678901234567890123456789'}).should.equal("012345678901234567890123456789..."); -``` - -should truncate string to custom size(10) and add default separator. - -```js -var template = twig({data: '{{ test|truncate(10) }}'}); -template.render({test: '01234567890123456789012345678901234567890123456789'}).should.equal("0123456789..."); -``` - -should truncate string to custom size(15) with preserve and add default separator. - -```js -var template = twig({data: '{{ test|truncate(15, true) }}'}); -template.render({test: '0123456789 0123456789 0123456789 0123456789 0123456789'}).should.equal("0123456789 0123456789..."); -``` - -should truncate string to custom size(15) with preserve and add custom(*) separator. - -```js -var template = twig({data: '{{ test|truncate(15, true, "*") }}'}); -template.render({test: '0123456789 0123456789 0123456789 0123456789 0123456789'}).should.equal("0123456789 0123456789*"); -``` - -<a name="twigjs-filters---trim--"></a> -## trim -> -should trim whitespace from strings. - -```js -var template = twig({data: '{{ test|trim }}'}); -template.render({ test: '\r\n Test\n ' }).should.equal("Test"); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|trim }}' }); -test_template.render().should.equal("" ); -``` - -<a name="twigjs-filters---number_format--"></a> -## number_format -> -should round to nearest integer if no parameters. - -```js -var template = twig({data: '{{ 1234.56|number_format }}'}); -template.render().should.equal("1,235"); -``` - -should have customizable precision. - -```js -var template = twig({data: '{{ 1234.567890123|number_format(4) }}'}); -template.render().should.equal("1,234.5679"); -``` - -should have a customizable decimal seperator. - -```js -var template = twig({data: '{{ 1234.567890123|number_format(2,",") }}'}); -template.render().should.equal("1,234,57"); -``` - -should have a customizable thousands seperator. - -```js -var template = twig({data: '{{ 1234.5678|number_format(2,","," ") }}'}); -template.render().should.equal("1 234,57"); -``` - -should handle blank seperators. - -```js -var template = twig({data: '{{ 1234.5678|number_format(2,"","") }}'}); -template.render().should.equal("123457"); -``` - -should handle undefined. - -```js -var test_template = twig({data: '{{ undef|number_format }}' }); -test_template.render().should.equal("0"); -``` - -<a name="twigjs-filters---slice--"></a> -## slice -> -should slice a string. - -```js -var test_template = twig({data: "{{ '12345'|slice(1, 2) }}" }); -test_template.render().should.equal("23"); -``` - -should slice a string to the end. - -```js -var test_template = twig({data: "{{ '12345'|slice(2) }}" }); -test_template.render().should.equal("345"); -``` - -should slice a string from the start. - -```js -var test_template = twig({data: "{{ '12345'|slice(null, 2) }}" }); -test_template.render().should.equal("12"); -``` - -should slice a string from a negative offset. - -```js -var test_template = twig({data: "{{ '12345'|slice(-2, 1) }}" }); -test_template.render().should.equal("4"); -``` - -should slice a string from a negative offset to end of string. - -```js -var test_template = twig({data: "{{ '12345'|slice(-2) }}" }); -test_template.render().should.equal("45"); -``` - -should slice an array. - -```js -var test_template = twig({data: "{{ [1, 2, 3, 4, 5]|slice(1, 2)|join(',') }}" }); -test_template.render().should.equal("2,3"); -``` - -should slice an array to the end. - -```js -var test_template = twig({data: "{{ [1, 2, 3, 4, 5]|slice(2)|join(',') }}" }); -test_template.render().should.equal("3,4,5"); -``` - -should slice an array from the start. - -```js -var test_template = twig({data: "{{ [1, 2, 3, 4, 5]|slice(null, 2)|join(',') }}" }); -test_template.render().should.equal("1,2"); -``` - -should slice an array from a negative offset. - -```js -var test_template = twig({data: "{{ [1, 2, 3, 4, 5]|slice(-2, 1)|join(',') }}" }); -test_template.render().should.equal("4"); -``` - -should slice an array from a negative offset to the end of the array. - -```js -var test_template = twig({data: "{{ [1, 2, 3, 4, 5]|slice(-4)|join(',') }}" }); -test_template.render().should.equal("2,3,4,5"); -``` - -<a name="twigjs-filters---abs--"></a> -## abs -> -should convert negative numbers to its absolute value. - -```js -var test_template = twig({data: "{{ '-7.365'|abs }}"}); -test_template.render().should.equal("7.365"); -``` - -should not alter absolute numbers. - -```js -var test_template = twig({data: "{{ 95|abs }}"}); -test_template.render().should.equal("95"); -``` - -<a name="twigjs-filters---first--"></a> -## first -> -should return first item in array. - -```js -var test_template = twig({data: "{{ ['a', 'b', 'c', 'd']|first }}"}); -test_template.render().should.equal("a"); -``` - -should return first member of object. - -```js -var test_template = twig({data: "{{ { item1: 'a', item2: 'b', item3: 'c', item4: 'd'}|first }}"}); -test_template.render().should.equal("a"); -``` - -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(""); -``` - -should return first character in string. - -```js -var test_template = twig({data: "{{ 'abcde'|first }}"}); -test_template.render().should.equal("a"); -``` - -<a name="twigjs-filters---split--"></a> -## split -> -should split string with a separator. - -```js -var test_template = twig({data: "{{ 'one-two-three'|split('-') }}"}); -test_template.render().should.equal("one,two,three"); -``` - -should split string with a separator and positive limit. - -```js -var test_template = twig({data: "{{ 'one-two-three-four-five'|split('-', 3) }}"}); -test_template.render().should.equal("one,two,three-four-five"); -``` - -should split string with a separator and negative limit. - -```js -var test_template = twig({data: "{{ 'one-two-three-four-five'|split('-', -2) }}"}); -test_template.render().should.equal("one,two,three"); -``` - -should split with empty separator. - -```js -var test_template = twig({data: "{{ '123'|split('') }}"}); -test_template.render().should.equal("1,2,3"); -``` - -should split with empty separator and limit. - -```js -var test_template = twig({data: "{{ 'aabbcc'|split('', 2) }}"}); -test_template.render().should.equal("aa,bb,cc"); -``` - -<a name="twigjs-filters---batch--"></a> -## 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(""); -``` - -<a name="twigjs-filters---last--"></a> -## last -> -should return last character in string. - -```js -var test_template = twig({data: "{{ 'abcd'|last }}"}); -test_template.render().should.equal("d"); -``` - -should return last item in array. - -```js -var test_template = twig({data: "{{ ['a', 'b', 'c', 'd']|last }}"}); -test_template.render().should.equal("d"); -``` - -should return last item in a sorted object. - -```js -var test_template = twig({data: "{{ {'m':1, 'z':5, 'a':3}|sort|last }}" }); -test_template.render().should.equal("5"); -``` - -<a name="twigjs-filters---raw--"></a> -## raw -> -should output the raw value if autoescape is on. - -```js -var template = twig({ - autoescape: true, - data: '{{ value|raw }}' -}); -template.render({ - value: "<test>&</test>" -}).should.equal('<test>&</test>'); -``` - -should output the raw value if autoescape is off. - -```js -var template = twig({ - autoescape: false, - data: '{{ value|raw }}' -}); -template.render({ - value: "<test>&</test>" -}).should.equal('<test>&</test>'); -``` - -<a name="twigjs-filters---round--"></a> -## 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"); -``` - -<a name="twigjs-loader--"></a> -# Twig.js Loader -> -should load a template from the filesystem asynchronously. - -```js -twig({ - id: 'fs-node-async', - path: 'test/templates/test.twig', - load: function(template) { - // Render the template - template.render({ - test: "yes", - flag: true - }).should.equal("Test template = yes\n\nFlag set!"); - done(); - } -}); -``` - -should load a template from the filesystem synchronously. - -```js -var template = twig({ - id: 'fs-node-sync', - path: 'test/templates/test.twig', - async: false -}); -// Render the template -template.render({ - test: "yes", - flag: true -}).should.equal("Test template = yes\n\nFlag set!"); -``` - -<a name="twigjs-loader---source--"></a> -## source -> -should load the non-compiled template source code. - -```js -twig({data: '{{ source("test/templates/source.twig") }}'}) - .render() - .should - .equal('{% if isUserNew == true %}\n Hello {{ name }}\n{% else %}\n Welcome back {{ name }}\n{% endif %}\n') -; -``` - -should indicate if there was a problem loading the template if 'ignore_missing' is false. - -```js -twig({data: '{{ source("test/templates/non-existing-source.twig", false) }}'}) - .render() - .should - .equal('Template "test/templates/non-existing-source.twig" is not defined.') -; -``` - -should NOT indicate if there was a problem loading the template if 'ignore_missing' is true. - -```js -twig({data: '{{ source("test/templates/non-existing-source.twig", true) }}'}) - .render() - .should - .equal('') -; -``` - -<a name="twigjs-include--"></a> -# Twig.js Include -> -should load an included template with no context. - -```js -twig({ - id: 'include', - path: 'test/templates/include.twig', - async: false -}); -// Load the template -twig({ref: 'include'}).render({test: 'tst'}).should.equal( "BeforeTest template = tst\n\nAfter" ); -``` - -should load an included template with additional context. - -```js -twig({ - id: 'include-with', - 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" ); -``` - -should load an included template with only additional context. - -```js -twig({ - id: 'include-only', - 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," ); -``` - -<a name="twigjs-functions--"></a> -# Twig.js Functions -> -should allow you to define a function. - -```js -twig({data: '{{ square(a) }}'}).render({a:4}).should.equal("16"); -``` - -should chain with other expressions. - -```js -twig({data: '{{ square(a) + 4 }}'}).render({a:4}).should.equal("20"); -``` - -should chain with filters. - -```js -twig({data: '{{ echo(a)|default("foo") }}'}).render().should.equal("foo"); -``` - -should work in for loop expressions. - -```js -twig({data: '{% for i in list(1, 2, 3) %}{{ i }},{% endfor %}'}).render().should.equal("1,2,3,"); -``` - -should be able to differentiate between a function and a variable. - -```js -twig({data: '{{ square ( square ) + square }}'}).render({square: 2}).should.equal("6"); -``` - -should work with boolean operations. - -```js -twig({data: '{% if echo(true) or echo(false) %}yes{% endif %}'}).render().should.equal("yes"); -``` - -should execute functions passed as context values. - -```js -twig({ - data: '{{ value }}' -}).render({ - value: function() { - return "test"; - } -}).should.equal("test"); -``` - -should execute functions passed as context values with this mapped to the context. - -```js -twig({ - data: '{{ value }}' -}).render({ - test: "value", - value: function() { - return this.test; - } -}).should.equal("value"); -``` - -should execute functions passed as context values with arguments. - -```js -twig({ - data: '{{ value(1, "test") }}' -}).render({ - value: function(a, b, c) { - return a + "-" + b + "-" + (c===undefined?"true":"false"); - } -}).should.equal("1-test-true"); -``` - -should execute functions passed as context value parameters with this mapped to the context. - -```js -twig({ - data: '{{ value }}' -}).render({ - test: "value", - value: function() { - return this.test; - } -}).should.equal("value"); -``` - -should execute functions passed as context object parameters. - -```js -twig({ - data: '{{ obj.value }}' -}).render({ - obj: { - value: function() { - return "test"; - } - } -}).should.equal("test"); -``` - -should execute functions passed as context object parameters with arguments. - -```js -twig({ - data: '{{ obj.value(1, "test") }}' -}).render({ - obj: { - value: function(a, b, c) { - return a + "-" + b + "-" + (c===undefined?"true":"false"); - } - } -}).should.equal("1-test-true"); -``` - -should execute functions passed as context object parameters. - -```js -twig({ - data: '{{ obj["value"] }}' -}).render({ - obj: { - value: function() { - return "test"; - } - } -}).should.equal("test"); -``` - -should execute functions passed as context object parameters with arguments. - -```js -twig({ - data: '{{ obj["value"](1, "test") }}' -}).render({ - obj: { - value: function(a, b, c) { - return a + "-" + b + "-" + (c===undefined?"true":"false"); - } - } -}).should.equal("1-test-true"); -``` - -<a name="twigjs-functions---built-in-functions--"></a> -## Built-in Functions -> -<a name="twigjs-functions---built-in-functions---range--"></a> -### range -> -should work over a range of numbers. - -```js -twig({data: '{% for i in range(0, 3) %}{{ i }},{% endfor %}'}).render().should.equal("0,1,2,3,"); -``` - -should work over a range of letters. - -```js -twig({data: '{% for i in range("a", "c") %}{{ i }},{% endfor %}'}).render().should.equal("a,b,c,"); -``` - -should work with an interval. - -```js -twig({data: '{% for i in range(1, 15, 3) %}{{ i }},{% endfor %}'}).render().should.equal("1,4,7,10,13,"); -``` - -should work with .. invocation. - -```js -twig({data: '{% for i in 0..3 %}{{ i }},{% endfor %}'}).render().should.equal("0,1,2,3,"); -twig({data: '{% for i in "a" .. "c" %}{{ i }},{% endfor %}'}).render().should.equal("a,b,c,"); -``` - -<a name="twigjs-functions---built-in-functions---cycle--"></a> -### cycle -> -should cycle through an array of values. - -```js -twig({data: '{% for i in range(0, 3) %}{{ cycle(["odd", "even"], i) }};{% endfor %}'}).render().should.equal("odd;even;odd;even;"); -``` - -<a name="twigjs-functions---built-in-functions---date--"></a> -### date -> -should understand timestamps. - -```js -var date = new Date(946706400 * 1000); -twig({data: '{{ date(946706400)|date("d/m/Y @ H:i:s") }}'}).render().should.equal(stringDate(date)); -``` - -should understand relative dates. - -```js -twig({data: '{{ date("+1 day") > date() }}'}).render().should.equal("true"); -twig({data: '{{ date("-1 day") > date() }}'}).render().should.equal("false"); -``` - -should support 'now' as a date parameter. - -```js -twig({data: '{{ date("now") }}' }).render().should.equal(new Date().toString()); -``` - -should understand exact dates. - -```js -var date = new Date("June 20, 2010 UTC"); - -twig({data: '{{ date("June 20, 2010 UTC")|date("d/m/Y @ H:i:s") }}'}).render().should.equal(stringDate(date)); -``` - -<a name="twigjs-functions---built-in-functions---dump--"></a> -### dump -> -should output formatted number. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: 5 }).should.equal('number(5)' + EOL); -``` - -should output formatted string. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: "String" }).should.equal('string(6) "String"' + EOL); -``` - -should output formatted boolean. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: true }).should.equal('bool(true)' + EOL); -``` - -should output formatted null. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: null }).should.equal('NULL' + EOL); -``` - -should output formatted object. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: {} }).should.equal('object(0) {' + EOL + '}' + EOL); -``` - -should output formatted array. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: [] }).should.equal('object(0) {' + EOL + '}' + EOL); -``` - -should output formatted undefined. - -```js -twig({data: '{{ dump(test) }}' }).render({ test: undefined }).should.equal('undefined' + EOL); -``` - -<a name="twigjs-functions---built-in-functions---block--"></a> -### block -> -should render the content of blocks. - -```js -twig({data: '{% block title %}Content - {{ val }}{% endblock %} Title: {{ block("title") }}'}).render({ val: "test" }) - .should.equal("Content - test Title: Content - test"); -``` - -shouldn't escape the content of blocks twice. - -```js -twig({ - autoescape: true, - data: '{% block test %}{{ val }}{% endblock %} {{ block("test") }}' -}).render({ - val: "te&st" -}).should.equal("te&st te&st"); -``` - -<a name="twigjs-functions---built-in-functions---attribute--"></a> -### attribute -> -should access attribute of an object. - -```js -twig({data: '{{ attribute(obj, key) }}' }).render({ - obj: { name: "Twig.js"}, - key: "name" -}) -.should.equal("Twig.js"); -``` - -should call function of attribute of an object. - -```js -twig({data: '{{ attribute(obj, key, params) }}' }).render({ - obj: { - name: function(first, last) { - return first+'.'+last; - } - }, - key: "name", - params: ['Twig', 'js'] - }) - .should.equal("Twig.js"); -``` - -should return undefined for missing attribute of an object. - -```js -twig({data: '{{ attribute(obj, key, params) }}' }).render({ - obj: { - name: function(first, last) { - return first+'.'+last; - } - }, - key: "missing", - params: ['Twig', 'js'] - }) - .should.equal(""); -``` - -should return element of an array. - -```js -twig({data: '{{ attribute(arr, 0) }}' }).render({ - arr: ['Twig', 'js'] - }) - .should.equal("Twig"); -``` - -should return undef for array beyond index size. - -```js -twig({data: '{{ attribute(arr, 100) }}' }).render({ - arr: ['Twig', 'js'] - }) - .should.equal(""); -``` - -<a name="twigjs-functions---built-in-functions---template_from_string--"></a> -### template_from_string -> -should load a template from a string. - -```js -twig({data: '{% include template_from_string("{{ value }}") %}'}).render({ - value: 'test' -}) -.should.equal('test'); -``` - -should load a template from a variable. - -```js -twig({data: '{% include template_from_string(template) %}'}).render({ - template: '{{ value }}', - value: 'test' -}) -.should.equal('test'); -``` - -<a name="twigjs-functions---built-in-functions---random--"></a> -### random -> -should return a random item from a traversable or array. - -```js -var arr = "bcdefghij".split(""); -for (var i = 1; i <= 1000; i++) { - arr.should.containEql(twig({data: '{{ random(arr) }}'}).render({arr: arr})); -} -``` - -should return a random character from a string. - -```js -var str = "abcdefghij"; -for (var i = 1; i <= 1000; i++) { - str.should.containEql(twig({data: '{{ random(str) }}'}).render({str: str})); -} -``` - -should return a random integer between 0 and the integer parameter. - -```js -for (var i = 1; i <= 1000; i++) { - twig({data: '{{ random(10) }}'}).render().should.be.within(0, 10); -} -``` - -should return a random integer between 0 and 2147483647 when no parameters are passed. - -```js -for (var i = 1; i <= 1000; i++) { - twig({data: '{{ random() }}'}).render().should.be.within(0, 2147483647); -} -``` - -<a name="twigjs-functions---built-in-functions---min-max--"></a> -### min, max -> -should support the 'min' function. - -```js -twig({data: '{{ min(2, 1, 3, 5, 4) }}'}).render().should.equal('1'); -twig({data: '{{ min([2, 1, 3, 5, 4]) }}'}).render().should.equal('1'); -twig({data: '{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"four"}) }}'}).render().should.equal('five'); -``` - -should support the 'max' function. - -```js -twig({data: '{{ max([2, 1, 3, 5, 4]) }}'}).render().should.equal('5'); -twig({data: '{{ max(2, 1, 3, 5, 4) }}'}).render().should.equal('5'); -twig({data: '{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"four"}) }}'}).render().should.equal('two'); -``` - -<a name="twigjs-loaders--"></a> -# Twig.js Loaders -> -<a name="twigjs-loaders---custom-loader--"></a> -## custom loader -> -should define a custom loader. - -```js -Twig.extend(function(Twig) { - var obj = { - templates: { - 'custom_loader_block': '{% block main %}This lets you {% block data %}use blocks{% endblock data %}{% endblock main %}', - 'custom_loader_simple': 'the value is: {{ value }}', - 'custom_loader_include': 'include others from the same loader method - {% include "custom_loader_simple" %}', - 'custom_loader_complex': '{% extends "custom_loader_block" %} {% block data %}extend other templates and {% include "custom_loader_include" %}{% endblock data %}' - }, - loader: function(location, params, callback, error_callback) { - params.data = this.templates[location]; - params.allowInlineIncludes = true; - var template = new Twig.Template(params); - if (typeof callback === 'function') { - callback(template); - } - return template; - } - }; - Twig.Templates.registerLoader('custom', obj.loader, obj); - Twig.Templates.loaders.should.have.property('custom'); -}); -``` - -should load a simple template from a custom loader. - -```js -twig({ - method: 'custom', - name: 'custom_loader_simple' -}).render({value: 'test succeeded'}).should.equal('the value is: test succeeded'); -``` - -should load a template that includes another from a custom loader. - -```js -twig({ - method: 'custom', - name: 'custom_loader_include' -}).render({value: 'test succeeded'}).should.equal('include others from the same loader method - the value is: test succeeded'); -``` - -should load a template that extends another from a custom loader. - -```js -twig({ - method: 'custom', - name: 'custom_loader_complex' -}).render({value: 'test succeeded'}).should.equal('This lets you extend other templates and include others from the same loader method - the value is: test succeeded'); -``` - -should remove a registered loader. - -```js -Twig.extend(function(Twig) { - Twig.Templates.unRegisterLoader('custom'); - Twig.Templates.loaders.should.not.have.property('custom'); -}); -``` - -<a name="twigjs-macro--"></a> -# Twig.js Macro -> -it should load macro. - -```js -twig({ - id: 'macro', - path: 'test/templates/macro.twig', - async: false -}); -// Load the template -twig({ref: 'macro'}).render({ }).should.equal( '' ); -``` - -it should import macro. - -```js -twig({ - id: 'import-macro', - path: 'test/templates/import.twig', - async: false -}); -// Load the template -twig({ref: 'import-macro'}).render({ }).trim().should.equal( "Hello World" ); -``` - -it should run macro with self reference. - -```js -twig({ - id: 'import-macro-self', - path: 'test/templates/macro-self.twig', - async: false -}); -// Load the template -twig({ref: 'import-macro-self'}).render({ }).trim().should.equal( '<p><input type="text" name="username" value="" size="20" /></p>' ); -``` - -it should run wrapped macro with self reference. - -```js -twig({ - id: 'import-wrapped-macro-self', - path: 'test/templates/macro-wrapped.twig', - async: false -}); -// Load the template -twig({ref: 'import-wrapped-macro-self'}).render({ }).trim().should.equal( '<p><div class="field"><input type="text" name="username" value="" size="20" /></div></p>' ); -``` - -it should run wrapped macro with context and self reference. - -```js -twig({ - id: 'import-macro-context-self', - path: 'test/templates/macro-context.twig', - async: false -}); -// Load the template -twig({ref: 'import-macro-context-self'}).render({ 'greetings': 'Howdy' }).trim().should.equal( 'Howdy Twigjs' ); -``` - -it should run wrapped macro inside blocks. - -```js -twig({ - id: 'import-macro-inside-block', - path: 'test/templates/macro-blocks.twig', - async: false -}); -// Load the template -twig({ref: 'import-macro-inside-block'}).render({ }).trim().should.equal( 'Welcome <div class="name">Twig Js</div>' ); -``` - -it should import selected macros from template. - -```js -twig({ - id: 'from-macro-import', - path: 'test/templates/from.twig', - async: false -}); -// Load the template -twig({ref: 'from-macro-import'}).render({ }).trim().should.equal( 'Twig.js<div class="field"><input type="text" name="text" value="" size="20" /></div><div class="field red"><input type="text" name="password" value="" size="20" /></div>' ); -``` - -should support inline includes by ID. - -```js -twig({ - id: 'hello', - data: '{% macro echo(name) %}Hello {{ name }}{% endmacro %}' -}); -var template = twig({ - allowInlineIncludes: true, - data: 'template with {% from "hello" import echo %}{{ echo("Twig.js") }}' - }), - output = template.render() -output.should.equal("template with Twig.js"); -``` - -<a name="twigjs-namespaces--"></a> -# Twig.js Namespaces -> -should support namespaces defined with ::. - -```js -twig({ - namespaces: { 'test': 'test/templates/namespaces/' }, - path: 'test/templates/namespaces_::.twig', - load: function(template) { - // Render the template - template.render({ - test: "yes", - flag: true - }).should.equal("namespaces"); - - done(); - } - }); -``` - -should support namespaces defined with @. - -```js -twig({ - namespaces: { 'test': 'test/templates/namespaces/' }, - path: 'test/templates/namespaces_@.twig', - load: function(template) { - // Render the template - template.render({ - test: "yes", - flag: true - }).should.equal("namespaces"); - - done(); - } - }); -``` - -<a name="twigjs-optional-functionality--"></a> -# Twig.js Optional Functionality -> -should support inline includes by ID. - -```js -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"); -``` - -<a name="twigjs-parsers--"></a> -# Twig.js Parsers -> -<a name="twigjs-parsers---custom-parser--"></a> -## custom parser -> -should define a custom parser. - -```js -Twig.extend(function(Twig) { - var parser = function(params) { - return '[CUSTOM PARSER] ' + params.data; - }; - Twig.Templates.registerParser('custom', parser); - Twig.Templates.parsers.should.have.property('custom'); -}); -``` - -should run the data through the custom parser. - -```js -Twig.extend(function(Twig) { - var params = { - data: 'This is a test template.' - }; - var template = Twig.Templates.parsers.custom(params); - template.should.equal('[CUSTOM PARSER] This is a test template.'); -}); -``` - -should remove a registered parser. - -```js -Twig.extend(function(Twig) { - Twig.Templates.unRegisterParser('custom'); - Twig.Templates.parsers.should.not.have.property('custom'); -}); -``` - -<a name="twigjs-path--"></a> -# Twig.js Path -> -<a name="twigjs-path---relativepath--"></a> -## relativePath -> -should throw an error if trying to get a relative path in an inline template. - -```js -(function () { - relativePath({}); -}).should.throw("Cannot extend an inline template."); -``` - -should give the full path to a file when file is passed. - -```js -relativePath({ url: "http://www.test.com/test.twig"}, "templates/myFile.twig").should.equal("http://www.test.com/templates/myFile.twig"); -relativePath({ path: "test/test.twig"}, "templates/myFile.twig").should.equal("test/templates/myFile.twig"); -``` - -should ascend directories. - -```js -relativePath({ url: "http://www.test.com/templates/../test.twig"}, "myFile.twig").should.equal("http://www.test.com/myFile.twig"); -relativePath({ path: "test/templates/../test.twig"}, "myFile.twig").should.equal("test/myFile.twig"); -``` - -should respect relative directories. - -```js -relativePath({ url: "http://www.test.com/templates/./test.twig"}, "myFile.twig").should.equal("http://www.test.com/templates/myFile.twig"); -relativePath({ path: "test/templates/./test.twig"}, "myFile.twig").should.equal("test/templates/myFile.twig"); -``` - -<a name="twigjs-path---relativepath---url--"></a> -### url -> -should use the url if no base is specified. - -```js -relativePath({ url: "http://www.test.com/test.twig"}).should.equal("http://www.test.com/"); -``` - -should use the base if base is specified. - -```js -relativePath({ url: "http://www.test.com/test.twig", base: "myTest" }).should.equal("myTest/"); -``` - -<a name="twigjs-path---relativepath---path--"></a> -### path -> -should use the path if no base is specified. - -```js -relativePath({ path: "test/test.twig"}).should.equal("test/"); -``` - -should use the base if base is specified. - -```js -relativePath({ path: "test/test.twig", base: "myTest" }).should.equal("myTest/"); -``` - -<a name="twigjs-path---parsepath--"></a> -## parsePath -> -should fall back to relativePath if the template has no namespaces defined. - -```js -var relativePathStub = sinon.stub(Twig.path, "relativePath"); -parsePath({ options: {} }); -relativePathStub.should.have.been.called; -``` - -<a name="twigjs-regression-tests--"></a> -# Twig.js Regression Tests -> -#47 should not match variables starting with not. - -```js -// Define and save a template -twig({data: '{% for note in notes %}{{note}}{% endfor %}'}).render({notes:['a', 'b', 'c']}).should.equal("abc"); -``` - -#56 functions work inside parentheses. - -```js -// Define and save a template -Twig.extendFunction('custom', function(value) { - return true; -}); -twig({data: '{% if (custom("val") and custom("val")) %}out{% endif %}'}).render({}).should.equal("out"); -``` - -#83 Support for trailing commas in arrays. - -```js -twig({data: '{{ [1,2,3,4,] }}'}).render().should.equal("1,2,3,4"); -``` - -#83 Support for trailing commas in objects. - -```js -twig({data: '{{ {a:1, b:2, c:3, } }}'}).render(); -``` - -#283 should support quotes between raw tags. - -```js -twig({data: '{% raw %}\n"\n{% endraw %}'}).render().should.equal('"'); -twig({data: "{% raw %}\n'\n{% endraw %}"}).render().should.equal("'"); -``` - -<a name="twigjs-tags--"></a> -# Twig.js Tags -> -should support spaceless. - -```js -twig({ - data: "{% spaceless %}<div>\n <b>b</b> <i>i</i>\n</div>{% endspaceless %}" -}).render().should.equal( - "<div><b>b</b><i>i</i></div>" -); -``` - -<a name="twigjs-tests--"></a> -# Twig.js Tests -> -<a name="twigjs-tests---empty-test--"></a> -## empty test -> -should identify numbers as not empty. - -```js -// number -twig({data: '{{ 1 is empty }}'}).render().should.equal("false" ); -twig({data: '{{ 0 is empty }}'}).render().should.equal("false" ); -``` - -should identify empty strings. - -```js -// String -twig({data: '{{ "" is empty }}'}).render().should.equal("true" ); -twig({data: '{{ "test" is empty }}'}).render().should.equal("false" ); -``` - -should identify empty arrays. - -```js -// Array -twig({data: '{{ [] is empty }}'}).render().should.equal("true" ); -twig({data: '{{ ["1"] is empty }}'}).render().should.equal("false" ); -``` - -should identify empty objects. - -```js -// Object -twig({data: '{{ {} is empty }}'}).render().should.equal("true" ); -twig({data: '{{ {"a":"b"} is empty }}'}).render().should.equal("false" ); -twig({data: '{{ {"a":"b"} is not empty }}'}).render().should.equal("true" ); -``` - -<a name="twigjs-tests---odd-test--"></a> -## odd test -> -should identify a number as odd. - -```js -twig({data: '{{ (1 + 4) is odd }}'}).render().should.equal("true" ); -twig({data: '{{ 6 is odd }}'}).render().should.equal("false" ); -``` - -<a name="twigjs-tests---even-test--"></a> -## even test -> -should identify a number as even. - -```js -twig({data: '{{ (1 + 4) is even }}'}).render().should.equal("false" ); -twig({data: '{{ 6 is even }}'}).render().should.equal("true" ); -``` - -<a name="twigjs-tests---divisibleby-test--"></a> -## divisibleby test -> -should determine if a number is divisible by the given number. - -```js -twig({data: '{{ 5 is divisibleby(3) }}'}).render().should.equal("false" ); -twig({data: '{{ 6 is divisibleby(3) }}'}).render().should.equal("true" ); -``` - -<a name="twigjs-tests---defined-test--"></a> -## defined test -> -should identify a key as defined if it exists in the render context. - -```js -twig({data: '{{ key is defined }}'}).render().should.equal("false" ); -twig({data: '{{ key is defined }}'}).render({key: "test"}).should.equal( "true" ); -var context = { - key: { - foo: "bar", - nothing: null - }, - nothing: null -}; -twig({data: '{{ key.foo is defined }}'}).render(context).should.equal( "true" ); -twig({data: '{{ key.bar is defined }}'}).render(context).should.equal( "false" ); -twig({data: '{{ key.foo.bar is defined }}'}).render(context).should.equal( "false" ); -twig({data: '{{ foo.bar is defined }}'}).render(context).should.equal( "false" ); -twig({data: '{{ nothing is defined }}'}).render(context).should.equal( "true" ); -twig({data: '{{ key.nothing is defined }}'}).render(context).should.equal( "true" ); -``` - -<a name="twigjs-tests---none-test--"></a> -## none test -> -should identify a key as none if it exists in the render context and is null. - -```js -twig({data: '{{ key is none }}'}).render().should.equal("false"); -twig({data: '{{ key is none }}'}).render({key: "test"}).should.equal("false"); -twig({data: '{{ key is none }}'}).render({key: null}).should.equal("true"); -twig({data: '{{ key is null }}'}).render({key: null}).should.equal("true"); -``` - -<a name="twigjs-tests---sameas-test--"></a> -## sameas test -> -should identify the exact same type as true. - -```js -twig({data: '{{ true is sameas(true) }}'}).render().should.equal("true"); -twig({data: '{{ a is sameas(1) }}'}).render({a: 1}).should.equal("true"); -twig({data: '{{ a is sameas("test") }}'}).render({a: "test"}).should.equal("true"); -twig({data: '{{ a is sameas(true) }}'}).render({a: true}).should.equal("true"); -``` - -should identify the different types as false. - -```js -twig({data: '{{ false is sameas(true) }}'}).render().should.equal("false"); -twig({data: '{{ true is sameas(1) }}'}).render().should.equal("false"); -twig({data: '{{ false is sameas("") }}'}).render().should.equal("false"); -twig({data: '{{ a is sameas(1) }}'}).render({a: "1"}).should.equal("false"); -``` - -<a name="twigjs-tests---iterable-test--"></a> -## iterable test -> -should fail on non-iterable data types. - -```js -twig({data: "{{ val is iterable ? 'ok' : 'ko' }}"}).render(data).should.equal("ko"); -twig({data: "{{ val is iterable ? 'ok' : 'ko' }}"}).render({val: null}).should.equal("ko"); -twig({data: "{{ val is iterable ? 'ok' : 'ko' }}"}).render({}).should.equal("ko"); -``` - -should pass on iterable data types. - -```js -twig({data: "{{ foo is iterable ? 'ok' : 'ko' }}"}).render(data).should.equal("ok"); -twig({data: "{{ obj is iterable ? 'ok' : 'ko' }}"}).render(data).should.equal("ok"); -``` - diff --git a/node_modules/twig/docs/twig.html b/node_modules/twig/docs/twig.html deleted file mode 100644 index 687abbd..0000000 --- a/node_modules/twig/docs/twig.html +++ /dev/null @@ -1,11250 +0,0 @@ -<!DOCTYPE html> - -<html> -<head> - <title>twig.js</title> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> - <link rel="stylesheet" media="all" href="docco.css" /> -</head> -<body> - <div id="container"> - <div id="background"></div> - - <ul class="sections"> - - <li id="title"> - <div class="annotation"> - <h1>twig.js</h1> - </div> - </li> - - - - <li id="section-1"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-1">¶</a> - </div> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-comment">/** - * Twig.js 0.8.9 - * - * @copyright 2011-2015 John Roepke and the Twig.js Contributors - * @license Available under the BSD 2-Clause License - * @link https://github.com/justjohn/twig.js - */</span> - -<span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ - - Twig.VERSION = <span class="hljs-string">"0.8.9"</span>; - - <span class="hljs-keyword">return</span> Twig; -})(Twig || {});</pre></div></div> - - </li> - - - <li id="section-2"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-2">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - <div class="content"><div class='highlight'><pre> -<span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>;</pre></div></div> - - </li> - - - <li id="section-3"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-3">¶</a> - </div> - <h2 id="twig-core-js">twig.core.js</h2> -<p>This file handles template level tokenizing, compiling and parsing.</p> - - </div> - - <div class="content"><div class='highlight'><pre> - Twig.trace = <span class="hljs-literal">false</span>; - Twig.debug = <span class="hljs-literal">false</span>;</pre></div></div> - - </li> - - - <li id="section-4"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-4">¶</a> - </div> - <p>Default caching to true for the improved performance it offers</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.cache = <span class="hljs-literal">true</span>; - - Twig.placeholders = { - parent: <span class="hljs-string">"{{|PARENT|}}"</span> - }; - - <span class="hljs-comment">/** - * Fallback for Array.indexOf for IE8 et al - */</span> - Twig.indexOf = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">arr, searchElement <span class="hljs-comment">/*, fromIndex */</span> </span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.prototype.hasOwnProperty(<span class="hljs-string">"indexOf"</span>)) { - <span class="hljs-keyword">return</span> arr.indexOf(searchElement); - } - <span class="hljs-keyword">if</span> (arr === <span class="hljs-keyword">void</span> <span class="hljs-number">0</span> || arr === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(); - } - <span class="hljs-keyword">var</span> t = <span class="hljs-built_in">Object</span>(arr); - <span class="hljs-keyword">var</span> len = t.length >>> <span class="hljs-number">0</span>; - <span class="hljs-keyword">if</span> (len === <span class="hljs-number">0</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - <span class="hljs-keyword">var</span> n = <span class="hljs-number">0</span>; - <span class="hljs-keyword">if</span> (<span class="hljs-built_in">arguments</span>.length > <span class="hljs-number">0</span>) { - n = <span class="hljs-built_in">Number</span>(<span class="hljs-built_in">arguments</span>[<span class="hljs-number">1</span>]); - <span class="hljs-keyword">if</span> (n !== n) { <span class="hljs-comment">// shortcut for verifying if it's NaN</span> - n = <span class="hljs-number">0</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (n !== <span class="hljs-number">0</span> && n !== <span class="hljs-literal">Infinity</span> && n !== -<span class="hljs-literal">Infinity</span>) { - n = (n > <span class="hljs-number">0</span> || <span class="hljs-number">-1</span>) * <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.abs(n)); - } - } - <span class="hljs-keyword">if</span> (n >= len) {</pre></div></div> - - </li> - - - <li id="section-5"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-5">¶</a> - </div> - <p>console.log(“indexOf not found1 “, JSON.stringify(searchElement), JSON.stringify(arr));</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - <span class="hljs-keyword">var</span> k = n >= <span class="hljs-number">0</span> ? n : <span class="hljs-built_in">Math</span>.max(len - <span class="hljs-built_in">Math</span>.abs(n), <span class="hljs-number">0</span>); - <span class="hljs-keyword">for</span> (; k < len; k++) { - <span class="hljs-keyword">if</span> (k <span class="hljs-keyword">in</span> t && t[k] === searchElement) { - <span class="hljs-keyword">return</span> k; - } - } - <span class="hljs-keyword">if</span> (arr == searchElement) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - }</pre></div></div> - - </li> - - - <li id="section-6"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-6">¶</a> - </div> - <p>console.log(“indexOf not found2 “, JSON.stringify(searchElement), JSON.stringify(arr));</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - - Twig.forEach = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">arr, callback, thisArg</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.prototype.forEach ) { - <span class="hljs-keyword">return</span> arr.forEach(callback, thisArg); - } - - <span class="hljs-keyword">var</span> T, k; - - <span class="hljs-keyword">if</span> ( arr == <span class="hljs-literal">null</span> ) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>( <span class="hljs-string">" this is null or not defined"</span> ); - }</pre></div></div> - - </li> - - - <li id="section-7"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-7">¶</a> - </div> - <ol> -<li>Let O be the result of calling ToObject passing the |this| value as the argument.</li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> O = <span class="hljs-built_in">Object</span>(arr);</pre></div></div> - - </li> - - - <li id="section-8"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-8">¶</a> - </div> - <ol> -<li>Let lenValue be the result of calling the Get internal method of O with the argument “length”.</li> -<li>Let len be ToUint32(lenValue).</li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> len = O.length >>> <span class="hljs-number">0</span>; <span class="hljs-comment">// Hack to convert O.length to a UInt32</span></pre></div></div> - - </li> - - - <li id="section-9"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-9">¶</a> - </div> - <ol> -<li>If IsCallable(callback) is false, throw a TypeError exception. -See: <a href="http://es5.github.com/#x9.11">http://es5.github.com/#x9.11</a></li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( {}.toString.call(callback) != <span class="hljs-string">"[object Function]"</span> ) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>( callback + <span class="hljs-string">" is not a function"</span> ); - }</pre></div></div> - - </li> - - - <li id="section-10"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-10">¶</a> - </div> - <ol> -<li>If thisArg was supplied, let T be thisArg; else let T be undefined.</li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( thisArg ) { - T = thisArg; - }</pre></div></div> - - </li> - - - <li id="section-11"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-11">¶</a> - </div> - <ol> -<li>Let k be 0</li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> k = <span class="hljs-number">0</span>;</pre></div></div> - - </li> - - - <li id="section-12"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-12">¶</a> - </div> - <ol> -<li>Repeat, while k < len</li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span>( k < len ) { - - <span class="hljs-keyword">var</span> kValue;</pre></div></div> - - </li> - - - <li id="section-13"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-13">¶</a> - </div> - <p>a. Let Pk be ToString(k). - This is implicit for LHS operands of the in operator -b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. - This step can be combined with c -c. If kPresent is true, then</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( k <span class="hljs-keyword">in</span> O ) {</pre></div></div> - - </li> - - - <li id="section-14"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-14">¶</a> - </div> - <p>i. Let kValue be the result of calling the Get internal method of O with argument Pk.</p> - - </div> - - <div class="content"><div class='highlight'><pre> kValue = O[ k ];</pre></div></div> - - </li> - - - <li id="section-15"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-15">¶</a> - </div> - <p>ii. Call the Call internal method of callback with T as the this value and -argument list containing kValue, k, and O.</p> - - </div> - - <div class="content"><div class='highlight'><pre> callback.call( T, kValue, k, O ); - }</pre></div></div> - - </li> - - - <li id="section-16"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-16">¶</a> - </div> - <p>d. Increase k by 1.</p> - - </div> - - <div class="content"><div class='highlight'><pre> k++; - }</pre></div></div> - - </li> - - - <li id="section-17"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-17">¶</a> - </div> - <ol> -<li>return undefined</li> -</ol> - - </div> - - <div class="content"><div class='highlight'><pre> }; - - Twig.merge = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">target, source, onlyChanged</span>) </span>{ - Twig.forEach(<span class="hljs-built_in">Object</span>.keys(source), <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">key</span>) </span>{ - <span class="hljs-keyword">if</span> (onlyChanged && !(key <span class="hljs-keyword">in</span> target)) { - <span class="hljs-keyword">return</span>; - } - - target[key] = source[key] - }); - - <span class="hljs-keyword">return</span> target; - }; - - <span class="hljs-comment">/** - * Exception thrown by twig.js. - */</span> - Twig.Error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">message</span>) </span>{ - <span class="hljs-keyword">this</span>.message = message; - <span class="hljs-keyword">this</span>.name = <span class="hljs-string">"TwigException"</span>; - <span class="hljs-keyword">this</span>.type = <span class="hljs-string">"TwigException"</span>; - }; - - <span class="hljs-comment">/** - * Get the string representation of a Twig error. - */</span> - Twig.Error.prototype.toString = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> output = <span class="hljs-keyword">this</span>.name + <span class="hljs-string">": "</span> + <span class="hljs-keyword">this</span>.message; - - <span class="hljs-keyword">return</span> output; - }; - - <span class="hljs-comment">/** - * Wrapper for logging to the console. - */</span> - Twig.log = { - trace: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<span class="hljs-keyword">if</span> (Twig.trace && <span class="hljs-built_in">console</span>) {<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>));}}, - debug: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<span class="hljs-keyword">if</span> (Twig.debug && <span class="hljs-built_in">console</span>) {<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>));}} - }; - - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">console</span> !== <span class="hljs-string">"undefined"</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">console</span>.error !== <span class="hljs-string">"undefined"</span>) { - Twig.log.error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-built_in">console</span>.error.apply(<span class="hljs-built_in">console</span>, <span class="hljs-built_in">arguments</span>); - } - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">console</span>.log !== <span class="hljs-string">"undefined"</span>) { - Twig.log.error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-built_in">console</span>.log.apply(<span class="hljs-built_in">console</span>, <span class="hljs-built_in">arguments</span>); - } - } - } <span class="hljs-keyword">else</span> { - Twig.log.error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{}; - } - - <span class="hljs-comment">/** - * Wrapper for child context objects in Twig. - * - * @param {Object} context Values to initialize the context with. - */</span> - Twig.ChildContext = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{ - <span class="hljs-keyword">var</span> ChildContext = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ChildContext</span>(<span class="hljs-params"></span>) </span>{}; - ChildContext.prototype = context; - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ChildContext(); - }; - - <span class="hljs-comment">/** - * Container for methods related to handling high level template tokens - * (for example: {{ expression }}, {% logic %}, {# comment #}, raw data) - */</span> - Twig.token = {}; - - <span class="hljs-comment">/** - * Token types. - */</span> - Twig.token.type = { - output: <span class="hljs-string">'output'</span>, - logic: <span class="hljs-string">'logic'</span>, - comment: <span class="hljs-string">'comment'</span>, - raw: <span class="hljs-string">'raw'</span>, - output_whitespace_pre: <span class="hljs-string">'output_whitespace_pre'</span>, - output_whitespace_post: <span class="hljs-string">'output_whitespace_post'</span>, - output_whitespace_both: <span class="hljs-string">'output_whitespace_both'</span>, - logic_whitespace_pre: <span class="hljs-string">'logic_whitespace_pre'</span>, - logic_whitespace_post: <span class="hljs-string">'logic_whitespace_post'</span>, - logic_whitespace_both: <span class="hljs-string">'logic_whitespace_both'</span> - }; - - <span class="hljs-comment">/** - * Token syntax definitions. - */</span> - Twig.token.definitions = [ - { - type: Twig.token.type.raw, - open: <span class="hljs-string">'{% raw %}'</span>, - close: <span class="hljs-string">'{% endraw %}'</span> - }, - { - type: Twig.token.type.raw, - open: <span class="hljs-string">'{% verbatim %}'</span>, - close: <span class="hljs-string">'{% endverbatim %}'</span> - },</pre></div></div> - - </li> - - - <li id="section-18"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-18">¶</a> - </div> - <p><em>Whitespace type tokens</em></p> -<p>These typically take the form <code>{{- expression -}}</code> or <code>{{- expression }}</code> or <code>{{ expression -}}</code>.</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.token.type.output_whitespace_pre, - open: <span class="hljs-string">'{{-'</span>, - close: <span class="hljs-string">'}}'</span> - }, - { - type: Twig.token.type.output_whitespace_post, - open: <span class="hljs-string">'{{'</span>, - close: <span class="hljs-string">'-}}'</span> - }, - { - type: Twig.token.type.output_whitespace_both, - open: <span class="hljs-string">'{{-'</span>, - close: <span class="hljs-string">'-}}'</span> - }, - { - type: Twig.token.type.logic_whitespace_pre, - open: <span class="hljs-string">'{%-'</span>, - close: <span class="hljs-string">'%}'</span> - }, - { - type: Twig.token.type.logic_whitespace_post, - open: <span class="hljs-string">'{%'</span>, - close: <span class="hljs-string">'-%}'</span> - }, - { - type: Twig.token.type.logic_whitespace_both, - open: <span class="hljs-string">'{%-'</span>, - close: <span class="hljs-string">'-%}'</span> - },</pre></div></div> - - </li> - - - <li id="section-19"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-19">¶</a> - </div> - <p><em>Output type tokens</em></p> -<p>These typically take the form <code>{{ expression }}</code>.</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.token.type.output, - open: <span class="hljs-string">'{{'</span>, - close: <span class="hljs-string">'}}'</span> - },</pre></div></div> - - </li> - - - <li id="section-20"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-20">¶</a> - </div> - <p><em>Logic type tokens</em></p> -<p>These typically take a form like <code>{% if expression %}</code> or <code>{% endif %}</code></p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.token.type.logic, - open: <span class="hljs-string">'{%'</span>, - close: <span class="hljs-string">'%}'</span> - },</pre></div></div> - - </li> - - - <li id="section-21"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-21">¶</a> - </div> - <p><em>Comment type tokens</em></p> -<p>These take the form <code>{# anything #}</code></p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.token.type.comment, - open: <span class="hljs-string">'{#'</span>, - close: <span class="hljs-string">'#}'</span> - } - ]; - - - <span class="hljs-comment">/** - * What characters start "strings" in token definitions. We need this to ignore token close - * strings inside an expression. - */</span> - Twig.token.strings = [<span class="hljs-string">'"'</span>, <span class="hljs-string">"'"</span>]; - - Twig.token.findStart = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">template</span>) </span>{ - <span class="hljs-keyword">var</span> output = { - position: <span class="hljs-literal">null</span>, - close_position: <span class="hljs-literal">null</span>, - def: <span class="hljs-literal">null</span> - }, - i, - token_template, - first_key_position, - close_key_position; - - <span class="hljs-keyword">for</span> (i=<span class="hljs-number">0</span>;i<Twig.token.definitions.length;i++) { - token_template = Twig.token.definitions[i]; - first_key_position = template.indexOf(token_template.open); - close_key_position = template.indexOf(token_template.close); - - Twig.log.trace(<span class="hljs-string">"Twig.token.findStart: "</span>, <span class="hljs-string">"Searching for "</span>, token_template.open, <span class="hljs-string">" found at "</span>, first_key_position);</pre></div></div> - - </li> - - - <li id="section-22"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-22">¶</a> - </div> - <p>Special handling for mismatched tokens</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (first_key_position >= <span class="hljs-number">0</span>) {</pre></div></div> - - </li> - - - <li id="section-23"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-23">¶</a> - </div> - <p>This token matches the template</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_template.open.length !== token_template.close.length) {</pre></div></div> - - </li> - - - <li id="section-24"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-24">¶</a> - </div> - <p>This token has mismatched closing and opening tags</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (close_key_position < <span class="hljs-number">0</span>) {</pre></div></div> - - </li> - - - <li id="section-25"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-25">¶</a> - </div> - <p>This token’s closing tag does not match the template</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">continue</span>; - } - } - }</pre></div></div> - - </li> - - - <li id="section-26"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-26">¶</a> - </div> - <p>Does this token occur before any other types?</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (first_key_position >= <span class="hljs-number">0</span> && (output.position === <span class="hljs-literal">null</span> || first_key_position < output.position)) { - output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (first_key_position >= <span class="hljs-number">0</span> && output.position !== <span class="hljs-literal">null</span> && first_key_position === output.position) { - <span class="hljs-comment">/*This token exactly matches another token, - greedily match to check if this token has a greater specificity*/</span> - <span class="hljs-keyword">if</span> (token_template.open.length > output.def.open.length) {</pre></div></div> - - </li> - - - <li id="section-27"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-27">¶</a> - </div> - <p>This token’s opening tag is more specific than the previous match</p> - - </div> - - <div class="content"><div class='highlight'><pre> output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (token_template.open.length === output.def.open.length) { - <span class="hljs-keyword">if</span> (token_template.close.length > output.def.close.length) {</pre></div></div> - - </li> - - - <li id="section-28"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-28">¶</a> - </div> - <p>This token’s opening tag is as specific as the previous match, -but the closing tag has greater specificity</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (close_key_position >= <span class="hljs-number">0</span> && close_key_position < output.close_position) {</pre></div></div> - - </li> - - - <li id="section-29"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-29">¶</a> - </div> - <p>This token’s closing tag exists in the template, -and it occurs sooner than the previous match</p> - - </div> - - <div class="content"><div class='highlight'><pre> output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (close_key_position >= <span class="hljs-number">0</span> && close_key_position < output.close_position) {</pre></div></div> - - </li> - - - <li id="section-30"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-30">¶</a> - </div> - <p>This token’s closing tag is not more specific than the previous match, -but it occurs sooner than the previous match</p> - - </div> - - <div class="content"><div class='highlight'><pre> output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } - } - } - } - - <span class="hljs-keyword">delete</span> output[<span class="hljs-string">'close_position'</span>]; - - <span class="hljs-keyword">return</span> output; - }; - - Twig.token.findEnd = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">template, token_def, start</span>) </span>{ - <span class="hljs-keyword">var</span> end = <span class="hljs-literal">null</span>, - found = <span class="hljs-literal">false</span>, - offset = <span class="hljs-number">0</span>,</pre></div></div> - - </li> - - - <li id="section-31"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-31">¶</a> - </div> - <p>String position variables</p> - - </div> - - <div class="content"><div class='highlight'><pre> str_pos = <span class="hljs-literal">null</span>, - str_found = <span class="hljs-literal">null</span>, - pos = <span class="hljs-literal">null</span>, - end_offset = <span class="hljs-literal">null</span>, - this_str_pos = <span class="hljs-literal">null</span>, - end_str_pos = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-32"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-32">¶</a> - </div> - <p>For loop variables</p> - - </div> - - <div class="content"><div class='highlight'><pre> i, - l; - - <span class="hljs-keyword">while</span> (!found) { - str_pos = <span class="hljs-literal">null</span>; - str_found = <span class="hljs-literal">null</span>; - pos = template.indexOf(token_def.close, offset); - - <span class="hljs-keyword">if</span> (pos >= <span class="hljs-number">0</span>) { - end = pos; - found = <span class="hljs-literal">true</span>; - } <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-33"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-33">¶</a> - </div> - <p>throw an exception</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to find closing bracket '"</span> + token_def.close + - <span class="hljs-string">"'"</span> + <span class="hljs-string">" opened near template position "</span> + start); - }</pre></div></div> - - </li> - - - <li id="section-34"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-34">¶</a> - </div> - <p>Ignore quotes within comments; just look for the next comment close sequence, -regardless of what comes before it. <a href="https://github.com/justjohn/twig.js/issues/95">https://github.com/justjohn/twig.js/issues/95</a></p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_def.type === Twig.token.type.comment) { - <span class="hljs-keyword">break</span>; - }</pre></div></div> - - </li> - - - <li id="section-35"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-35">¶</a> - </div> - <p>Ignore quotes within raw tag -Fixes #283</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_def.type === Twig.token.type.raw) { - <span class="hljs-keyword">break</span>; - } - - l = Twig.token.strings.length; - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < l; i += <span class="hljs-number">1</span>) { - this_str_pos = template.indexOf(Twig.token.strings[i], offset); - - <span class="hljs-keyword">if</span> (this_str_pos > <span class="hljs-number">0</span> && this_str_pos < pos && - (str_pos === <span class="hljs-literal">null</span> || this_str_pos < str_pos)) { - str_pos = this_str_pos; - str_found = Twig.token.strings[i]; - } - }</pre></div></div> - - </li> - - - <li id="section-36"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-36">¶</a> - </div> - <p>We found a string before the end of the token, now find the string’s end and set the search offset to it</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (str_pos !== <span class="hljs-literal">null</span>) { - end_offset = str_pos + <span class="hljs-number">1</span>; - end = <span class="hljs-literal">null</span>; - found = <span class="hljs-literal">false</span>; - <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) { - end_str_pos = template.indexOf(str_found, end_offset); - <span class="hljs-keyword">if</span> (end_str_pos < <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-string">"Unclosed string in template"</span>; - }</pre></div></div> - - </li> - - - <li id="section-37"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-37">¶</a> - </div> - <p>Ignore escaped quotes</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (template.substr(end_str_pos - <span class="hljs-number">1</span>, <span class="hljs-number">1</span>) !== <span class="hljs-string">"\\"</span>) { - offset = end_str_pos + <span class="hljs-number">1</span>; - <span class="hljs-keyword">break</span>; - } <span class="hljs-keyword">else</span> { - end_offset = end_str_pos + <span class="hljs-number">1</span>; - } - } - } - } - <span class="hljs-keyword">return</span> end; - }; - - <span class="hljs-comment">/** - * Convert a template into high-level tokens. - */</span> - Twig.tokenize = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">template</span>) </span>{ - <span class="hljs-keyword">var</span> tokens = [],</pre></div></div> - - </li> - - - <li id="section-38"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-38">¶</a> - </div> - <p>An offset for reporting errors locations in the template.</p> - - </div> - - <div class="content"><div class='highlight'><pre> error_offset = <span class="hljs-number">0</span>,</pre></div></div> - - </li> - - - <li id="section-39"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-39">¶</a> - </div> - <p>The start and type of the first token found in the template.</p> - - </div> - - <div class="content"><div class='highlight'><pre> found_token = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-40"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-40">¶</a> - </div> - <p>The end position of the matched token.</p> - - </div> - - <div class="content"><div class='highlight'><pre> end = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">while</span> (template.length > <span class="hljs-number">0</span>) {</pre></div></div> - - </li> - - - <li id="section-41"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-41">¶</a> - </div> - <p>Find the first occurance of any token type in the template</p> - - </div> - - <div class="content"><div class='highlight'><pre> found_token = Twig.token.findStart(template); - - Twig.log.trace(<span class="hljs-string">"Twig.tokenize: "</span>, <span class="hljs-string">"Found token: "</span>, found_token); - - <span class="hljs-keyword">if</span> (found_token.position !== <span class="hljs-literal">null</span>) {</pre></div></div> - - </li> - - - <li id="section-42"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-42">¶</a> - </div> - <p>Add a raw type token for anything before the start of the token</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (found_token.position > <span class="hljs-number">0</span>) { - tokens.push({ - type: Twig.token.type.raw, - value: template.substring(<span class="hljs-number">0</span>, found_token.position) - }); - } - template = template.substr(found_token.position + found_token.def.open.length); - error_offset += found_token.position + found_token.def.open.length;</pre></div></div> - - </li> - - - <li id="section-43"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-43">¶</a> - </div> - <p>Find the end of the token</p> - - </div> - - <div class="content"><div class='highlight'><pre> end = Twig.token.findEnd(template, found_token.def, error_offset); - - Twig.log.trace(<span class="hljs-string">"Twig.tokenize: "</span>, <span class="hljs-string">"Token ends at "</span>, end); - - tokens.push({ - type: found_token.def.type, - value: template.substring(<span class="hljs-number">0</span>, end).trim() - }); - - <span class="hljs-keyword">if</span> (template.substr( end + found_token.def.close.length, <span class="hljs-number">1</span> ) === <span class="hljs-string">"\n"</span>) { - <span class="hljs-keyword">switch</span> (found_token.def.type) { - <span class="hljs-keyword">case</span> <span class="hljs-string">"logic_whitespace_pre"</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">"logic_whitespace_post"</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">"logic_whitespace_both"</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">"logic"</span>:</pre></div></div> - - </li> - - - <li id="section-44"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-44">¶</a> - </div> - <p>Newlines directly after logic tokens are ignored</p> - - </div> - - <div class="content"><div class='highlight'><pre> end += <span class="hljs-number">1</span>; - <span class="hljs-keyword">break</span>; - } - } - - template = template.substr(end + found_token.def.close.length);</pre></div></div> - - </li> - - - <li id="section-45"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-45">¶</a> - </div> - <p>Increment the position in the template</p> - - </div> - - <div class="content"><div class='highlight'><pre> error_offset += end + found_token.def.close.length; - - } <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-46"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-46">¶</a> - </div> - <p>No more tokens -> add the rest of the template as a raw-type token</p> - - </div> - - <div class="content"><div class='highlight'><pre> tokens.push({ - type: Twig.token.type.raw, - value: template - }); - template = <span class="hljs-string">''</span>; - } - } - - <span class="hljs-keyword">return</span> tokens; - }; - - - Twig.compile = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">tokens</span>) </span>{ - <span class="hljs-keyword">try</span> {</pre></div></div> - - </li> - - - <li id="section-47"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-47">¶</a> - </div> - <p>Output and intermediate stacks</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> output = [], - stack = [],</pre></div></div> - - </li> - - - <li id="section-48"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-48">¶</a> - </div> - <p>The tokens between open and close tags</p> - - </div> - - <div class="content"><div class='highlight'><pre> intermediate_output = [], - - token = <span class="hljs-literal">null</span>, - logic_token = <span class="hljs-literal">null</span>, - unclosed_token = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-49"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-49">¶</a> - </div> - <p>Temporary previous token.</p> - - </div> - - <div class="content"><div class='highlight'><pre> prev_token = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-50"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-50">¶</a> - </div> - <p>Temporary previous output.</p> - - </div> - - <div class="content"><div class='highlight'><pre> prev_output = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-51"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-51">¶</a> - </div> - <p>Temporary previous intermediate output.</p> - - </div> - - <div class="content"><div class='highlight'><pre> prev_intermediate_output = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-52"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-52">¶</a> - </div> - <p>The previous token’s template</p> - - </div> - - <div class="content"><div class='highlight'><pre> prev_template = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-53"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-53">¶</a> - </div> - <p>Token lookahead</p> - - </div> - - <div class="content"><div class='highlight'><pre> next_token = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-54"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-54">¶</a> - </div> - <p>The output token</p> - - </div> - - <div class="content"><div class='highlight'><pre> tok_output = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-55"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-55">¶</a> - </div> - <p>Logic Token values</p> - - </div> - - <div class="content"><div class='highlight'><pre> type = <span class="hljs-literal">null</span>, - open = <span class="hljs-literal">null</span>, - next = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">var</span> compile_output = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token</span>) </span>{ - Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [token]); - <span class="hljs-keyword">if</span> (stack.length > <span class="hljs-number">0</span>) { - intermediate_output.push(token); - } <span class="hljs-keyword">else</span> { - output.push(token); - } - }; - - <span class="hljs-keyword">var</span> compile_logic = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-56"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-56">¶</a> - </div> - <p>Compile the logic token</p> - - </div> - - <div class="content"><div class='highlight'><pre> logic_token = Twig.logic.compile.apply(<span class="hljs-keyword">this</span>, [token]); - - type = logic_token.type; - open = Twig.logic.handler[type].open; - next = Twig.logic.handler[type].next; - - Twig.log.trace(<span class="hljs-string">"Twig.compile: "</span>, <span class="hljs-string">"Compiled logic token to "</span>, logic_token, - <span class="hljs-string">" next is: "</span>, next, <span class="hljs-string">" open is : "</span>, open);</pre></div></div> - - </li> - - - <li id="section-57"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-57">¶</a> - </div> - <p>Not a standalone token, check logic stack to see if this is expected</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (open !== <span class="hljs-literal">undefined</span> && !open) { - prev_token = stack.pop(); - prev_template = Twig.logic.handler[prev_token.type]; - - <span class="hljs-keyword">if</span> (Twig.indexOf(prev_template.next, type) < <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(type + <span class="hljs-string">" not expected after a "</span> + 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 - }; - <span class="hljs-keyword">if</span> (stack.length > <span class="hljs-number">0</span>) { - intermediate_output.push(tok_output); - } <span class="hljs-keyword">else</span> { - output.push(tok_output); - } - }</pre></div></div> - - </li> - - - <li id="section-58"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-58">¶</a> - </div> - <p>This token requires additional tokens to complete the logic structure.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (next !== <span class="hljs-literal">undefined</span> && next.length > <span class="hljs-number">0</span>) { - Twig.log.trace(<span class="hljs-string">"Twig.compile: "</span>, <span class="hljs-string">"Pushing "</span>, logic_token, <span class="hljs-string">" to logic stack."</span>); - - <span class="hljs-keyword">if</span> (stack.length > <span class="hljs-number">0</span>) {</pre></div></div> - - </li> - - - <li id="section-59"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-59">¶</a> - </div> - <p>Put any currently held output into the output list of the logic operator -currently at the head of the stack before we push a new one on.</p> - - </div> - - <div class="content"><div class='highlight'><pre> 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 = []; - }</pre></div></div> - - </li> - - - <li id="section-60"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-60">¶</a> - </div> - <p>Push the new logic token onto the logic stack</p> - - </div> - - <div class="content"><div class='highlight'><pre> stack.push(logic_token); - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (open !== <span class="hljs-literal">undefined</span> && open) { - tok_output = { - type: Twig.token.type.logic, - token: logic_token - };</pre></div></div> - - </li> - - - <li id="section-61"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-61">¶</a> - </div> - <p>Standalone token (like {% set … %}</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (stack.length > <span class="hljs-number">0</span>) { - intermediate_output.push(tok_output); - } <span class="hljs-keyword">else</span> { - output.push(tok_output); - } - } - }; - - <span class="hljs-keyword">while</span> (tokens.length > <span class="hljs-number">0</span>) { - token = tokens.shift(); - prev_output = output[output.length - <span class="hljs-number">1</span>]; - prev_intermediate_output = intermediate_output[intermediate_output.length - <span class="hljs-number">1</span>]; - next_token = tokens[<span class="hljs-number">0</span>]; - Twig.log.trace(<span class="hljs-string">"Compiling token "</span>, token); - <span class="hljs-keyword">switch</span> (token.type) { - <span class="hljs-keyword">case</span> Twig.token.type.raw: - <span class="hljs-keyword">if</span> (stack.length > <span class="hljs-number">0</span>) { - intermediate_output.push(token); - } <span class="hljs-keyword">else</span> { - output.push(token); - } - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> Twig.token.type.logic: - compile_logic.call(<span class="hljs-keyword">this</span>, token); - <span class="hljs-keyword">break</span>;</pre></div></div> - - </li> - - - <li id="section-62"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-62">¶</a> - </div> - <p>Do nothing, comments should be ignored</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> Twig.token.type.comment: - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> Twig.token.type.output: - compile_output.call(<span class="hljs-keyword">this</span>, token); - <span class="hljs-keyword">break</span>;</pre></div></div> - - </li> - - - <li id="section-63"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-63">¶</a> - </div> - <p>Kill whitespace ahead and behind this token</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> Twig.token.type.logic_whitespace_pre: - <span class="hljs-keyword">case</span> Twig.token.type.logic_whitespace_post: - <span class="hljs-keyword">case</span> Twig.token.type.logic_whitespace_both: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_pre: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_post: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_both: - <span class="hljs-keyword">if</span> (token.type !== Twig.token.type.output_whitespace_post && token.type !== Twig.token.type.logic_whitespace_post) { - <span class="hljs-keyword">if</span> (prev_output) {</pre></div></div> - - </li> - - - <li id="section-64"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-64">¶</a> - </div> - <p>If the previous output is raw, pop it off</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (prev_output.type === Twig.token.type.raw) { - output.pop();</pre></div></div> - - </li> - - - <li id="section-65"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-65">¶</a> - </div> - <p>If the previous output is not just whitespace, trim it</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (prev_output.value.match(<span class="hljs-regexp">/^\s*$/</span>) === <span class="hljs-literal">null</span>) { - prev_output.value = prev_output.value.trim();</pre></div></div> - - </li> - - - <li id="section-66"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-66">¶</a> - </div> - <p>Repush the previous output</p> - - </div> - - <div class="content"><div class='highlight'><pre> output.push(prev_output); - } - } - } - - <span class="hljs-keyword">if</span> (prev_intermediate_output) {</pre></div></div> - - </li> - - - <li id="section-67"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-67">¶</a> - </div> - <p>If the previous intermediate output is raw, pop it off</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (prev_intermediate_output.type === Twig.token.type.raw) { - intermediate_output.pop();</pre></div></div> - - </li> - - - <li id="section-68"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-68">¶</a> - </div> - <p>If the previous output is not just whitespace, trim it</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (prev_intermediate_output.value.match(<span class="hljs-regexp">/^\s*$/</span>) === <span class="hljs-literal">null</span>) { - prev_intermediate_output.value = prev_intermediate_output.value.trim();</pre></div></div> - - </li> - - - <li id="section-69"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-69">¶</a> - </div> - <p>Repush the previous intermediate output</p> - - </div> - - <div class="content"><div class='highlight'><pre> intermediate_output.push(prev_intermediate_output); - } - } - } - }</pre></div></div> - - </li> - - - <li id="section-70"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-70">¶</a> - </div> - <p>Compile this token</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">switch</span> (token.type) { - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_pre: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_post: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_both: - compile_output.call(<span class="hljs-keyword">this</span>, token); - <span class="hljs-keyword">break</span>; - <span class="hljs-keyword">case</span> Twig.token.type.logic_whitespace_pre: - <span class="hljs-keyword">case</span> Twig.token.type.logic_whitespace_post: - <span class="hljs-keyword">case</span> Twig.token.type.logic_whitespace_both: - compile_logic.call(<span class="hljs-keyword">this</span>, token); - <span class="hljs-keyword">break</span>; - } - - <span class="hljs-keyword">if</span> (token.type !== Twig.token.type.output_whitespace_pre && token.type !== Twig.token.type.logic_whitespace_pre) { - <span class="hljs-keyword">if</span> (next_token) {</pre></div></div> - - </li> - - - <li id="section-71"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-71">¶</a> - </div> - <p>If the next token is raw, shift it out</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (next_token.type === Twig.token.type.raw) { - tokens.shift();</pre></div></div> - - </li> - - - <li id="section-72"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-72">¶</a> - </div> - <p>If the next token is not just whitespace, trim it</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (next_token.value.match(<span class="hljs-regexp">/^\s*$/</span>) === <span class="hljs-literal">null</span>) { - next_token.value = next_token.value.trim();</pre></div></div> - - </li> - - - <li id="section-73"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-73">¶</a> - </div> - <p>Unshift the next token</p> - - </div> - - <div class="content"><div class='highlight'><pre> tokens.unshift(next_token); - } - } - } - } - - <span class="hljs-keyword">break</span>; - } - - Twig.log.trace(<span class="hljs-string">"Twig.compile: "</span>, <span class="hljs-string">" Output: "</span>, output, - <span class="hljs-string">" Logic Stack: "</span>, stack, - <span class="hljs-string">" Pending Output: "</span>, intermediate_output ); - }</pre></div></div> - - </li> - - - <li id="section-74"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-74">¶</a> - </div> - <p>Verify that there are no logic tokens left in the stack.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (stack.length > <span class="hljs-number">0</span>) { - unclosed_token = stack.pop(); - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Unable to find an end tag for "</span> + unclosed_token.type + - <span class="hljs-string">", expecting one of "</span> + unclosed_token.next); - } - <span class="hljs-keyword">return</span> output; - } <span class="hljs-keyword">catch</span> (ex) { - Twig.log.error(<span class="hljs-string">"Error compiling twig template "</span> + <span class="hljs-keyword">this</span>.id + <span class="hljs-string">": "</span>); - <span class="hljs-keyword">if</span> (ex.stack) { - Twig.log.error(ex.stack); - } <span class="hljs-keyword">else</span> { - Twig.log.error(ex.toString()); - } - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.options.rethrow) <span class="hljs-keyword">throw</span> ex; - } - }; - - <span class="hljs-comment">/** - * Parse a compiled template. - * - * @param {Array} tokens The compiled tokens. - * @param {Object} context The render context. - * - * @return {string} The parsed template. - */</span> - Twig.parse = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">tokens, context</span>) </span>{ - <span class="hljs-keyword">try</span> { - <span class="hljs-keyword">var</span> output = [],</pre></div></div> - - </li> - - - <li id="section-75"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-75">¶</a> - </div> - <p>Track logic chains</p> - - </div> - - <div class="content"><div class='highlight'><pre> chain = <span class="hljs-literal">true</span>, - that = <span class="hljs-keyword">this</span>; - - Twig.forEach(tokens, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseToken</span>(<span class="hljs-params">token</span>) </span>{ - Twig.log.debug(<span class="hljs-string">"Twig.parse: "</span>, <span class="hljs-string">"Parsing token: "</span>, token); - - <span class="hljs-keyword">switch</span> (token.type) { - <span class="hljs-keyword">case</span> Twig.token.type.raw: - output.push(Twig.filters.raw(token.value)); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> Twig.token.type.logic: - <span class="hljs-keyword">var</span> logic_token = token.token, - logic = Twig.logic.parse.apply(that, [logic_token, context, chain]); - - <span class="hljs-keyword">if</span> (logic.chain !== <span class="hljs-literal">undefined</span>) { - chain = logic.chain; - } - <span class="hljs-keyword">if</span> (logic.context !== <span class="hljs-literal">undefined</span>) { - context = logic.context; - } - <span class="hljs-keyword">if</span> (logic.output !== <span class="hljs-literal">undefined</span>) { - output.push(logic.output); - } - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> Twig.token.type.comment:</pre></div></div> - - </li> - - - <li id="section-76"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-76">¶</a> - </div> - <p>Do nothing, comments should be ignored</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">break</span>;</pre></div></div> - - </li> - - - <li id="section-77"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-77">¶</a> - </div> - <p>Fall through whitespace to output</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_pre: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_post: - <span class="hljs-keyword">case</span> Twig.token.type.output_whitespace_both: - <span class="hljs-keyword">case</span> Twig.token.type.output: - Twig.log.debug(<span class="hljs-string">"Twig.parse: "</span>, <span class="hljs-string">"Output token: "</span>, token.stack);</pre></div></div> - - </li> - - - <li id="section-78"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-78">¶</a> - </div> - <p>Parse the given expression in the given context</p> - - </div> - - <div class="content"><div class='highlight'><pre> output.push(Twig.expression.parse.apply(that, [token.stack, context])); - <span class="hljs-keyword">break</span>; - } - }); - <span class="hljs-keyword">return</span> Twig.output.apply(<span class="hljs-keyword">this</span>, [output]); - } <span class="hljs-keyword">catch</span> (ex) { - Twig.log.error(<span class="hljs-string">"Error parsing twig template "</span> + <span class="hljs-keyword">this</span>.id + <span class="hljs-string">": "</span>); - <span class="hljs-keyword">if</span> (ex.stack) { - Twig.log.error(ex.stack); - } <span class="hljs-keyword">else</span> { - Twig.log.error(ex.toString()); - } - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.options.rethrow) <span class="hljs-keyword">throw</span> ex; - - <span class="hljs-keyword">if</span> (Twig.debug) { - <span class="hljs-keyword">return</span> ex.toString(); - } - } - }; - - <span class="hljs-comment">/** - * Tokenize and compile a string template. - * - * @param {string} data The template. - * - * @return {Array} The compiled tokens. - */</span> - Twig.prepare = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>) </span>{ - <span class="hljs-keyword">var</span> tokens, raw_tokens;</pre></div></div> - - </li> - - - <li id="section-79"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-79">¶</a> - </div> - <p>Tokenize</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.log.debug(<span class="hljs-string">"Twig.prepare: "</span>, <span class="hljs-string">"Tokenizing "</span>, data); - raw_tokens = Twig.tokenize.apply(<span class="hljs-keyword">this</span>, [data]);</pre></div></div> - - </li> - - - <li id="section-80"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-80">¶</a> - </div> - <p>Compile</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.log.debug(<span class="hljs-string">"Twig.prepare: "</span>, <span class="hljs-string">"Compiling "</span>, raw_tokens); - tokens = Twig.compile.apply(<span class="hljs-keyword">this</span>, [raw_tokens]); - - Twig.log.debug(<span class="hljs-string">"Twig.prepare: "</span>, <span class="hljs-string">"Compiled "</span>, tokens); - - <span class="hljs-keyword">return</span> tokens; - }; - - <span class="hljs-comment">/** - * Join the output token's stack and escape it if needed - * - * @param {Array} Output token's stack - * - * @return {string|String} Autoescaped output - */</span> - Twig.output = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">output</span>) </span>{ - <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.options.autoescape) { - <span class="hljs-keyword">return</span> output.join(<span class="hljs-string">""</span>); - } - - <span class="hljs-keyword">var</span> strategy = <span class="hljs-string">'html'</span>; - <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> <span class="hljs-keyword">this</span>.options.autoescape == <span class="hljs-string">'string'</span>) - strategy = <span class="hljs-keyword">this</span>.options.autoescape;</pre></div></div> - - </li> - - - <li id="section-81"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-81">¶</a> - </div> - <p>[].map would be better but it’s not supported by IE8-</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> escaped_output = []; - Twig.forEach(output, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">str</span>) </span>{ - <span class="hljs-keyword">if</span> (str && (str.twig_markup !== <span class="hljs-literal">true</span> && str.twig_markup != strategy)) { - str = Twig.filters.escape(str, [ strategy ]); - } - escaped_output.push(str); - }); - <span class="hljs-keyword">return</span> Twig.Markup(escaped_output.join(<span class="hljs-string">""</span>)); - }</pre></div></div> - - </li> - - - <li id="section-82"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-82">¶</a> - </div> - <p>Namespace for template storage and retrieval</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.Templates = { - <span class="hljs-comment">/** - * Registered template loaders - use Twig.Templates.registerLoader to add supported loaders - * @type {Object} - */</span> - loaders: {}, - - <span class="hljs-comment">/** - * Registered template parsers - use Twig.Templates.registerParser to add supported parsers - * @type {Object} - */</span> - parsers: {}, - - <span class="hljs-comment">/** - * Cached / loaded templates - * @type {Object} - */</span> - registry: {} - }; - - <span class="hljs-comment">/** - * Is this id valid for a twig template? - * - * @param {string} id The ID to check. - * - * @throws {Twig.Error} If the ID is invalid or used. - * @return {boolean} True if the ID is valid. - */</span> - Twig.validateId = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">id</span>) </span>{ - <span class="hljs-keyword">if</span> (id === <span class="hljs-string">"prototype"</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(id + <span class="hljs-string">" is not a valid twig identifier"</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.cache && Twig.Templates.registry.hasOwnProperty(id)) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"There is already a template with the ID "</span> + id); - } - <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; - } - - <span class="hljs-comment">/** - * Register a template loader - * - * @example - * Twig.extend(function(Twig) { - * Twig.Templates.registerLoader('custom_loader', function(location, params, callback, error_callback) { - * // ... load the template ... - * params.data = loadedTemplateData; - * // create and return the template - * var template = new Twig.Template(params); - * if (typeof callback === 'function') { - * callback(template); - * } - * return template; - * }); - * }); - * - * @param {String} method_name The method this loader is intended for (ajax, fs) - * @param {Function} func The function to execute when loading the template - * @param {Object|undefined} scope Optional scope parameter to bind func to - * - * @throws Twig.Error - * - * @return {void} - */</span> - Twig.Templates.registerLoader = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">method_name, func, scope</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> func !== <span class="hljs-string">'function'</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Unable to add loader for '</span> + method_name + <span class="hljs-string">': Invalid function reference given.'</span>); - } - <span class="hljs-keyword">if</span> (scope) { - func = func.bind(scope); - } - <span class="hljs-keyword">this</span>.loaders[method_name] = func; - }; - - <span class="hljs-comment">/** - * Remove a registered loader - * - * @param {String} method_name The method name for the loader you wish to remove - * - * @return {void} - */</span> - Twig.Templates.unRegisterLoader = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">method_name</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.isRegisteredLoader(method_name)) { - <span class="hljs-keyword">delete</span> <span class="hljs-keyword">this</span>.loaders[method_name]; - } - }; - - <span class="hljs-comment">/** - * See if a loader is registered by its method name - * - * @param {String} method_name The name of the loader you are looking for - * - * @return {boolean} - */</span> - Twig.Templates.isRegisteredLoader = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">method_name</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.loaders.hasOwnProperty(method_name); - }; - - <span class="hljs-comment">/** - * Register a template parser - * - * @example - * Twig.extend(function(Twig) { - * Twig.Templates.registerParser('custom_parser', function(params) { - * // this template source can be accessed in params.data - * var template = params.data - * - * // ... custom process that modifies the template - * - * // return the parsed template - * return template; - * }); - * }); - * - * @param {String} method_name The method this parser is intended for (twig, source) - * @param {Function} func The function to execute when parsing the template - * @param {Object|undefined} scope Optional scope parameter to bind func to - * - * @throws Twig.Error - * - * @return {void} - */</span> - Twig.Templates.registerParser = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">method_name, func, scope</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> func !== <span class="hljs-string">'function'</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Unable to add parser for '</span> + method_name + <span class="hljs-string">': Invalid function regerence given.'</span>); - } - - <span class="hljs-keyword">if</span> (scope) { - func = func.bind(scope); - } - - <span class="hljs-keyword">this</span>.parsers[method_name] = func; - }; - - <span class="hljs-comment">/** - * Remove a registered parser - * - * @param {String} method_name The method name for the parser you wish to remove - * - * @return {void} - */</span> - Twig.Templates.unRegisterParser = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">method_name</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.isRegisteredParser(method_name)) { - <span class="hljs-keyword">delete</span> <span class="hljs-keyword">this</span>.parsers[method_name]; - } - }; - - <span class="hljs-comment">/** - * See if a parser is registered by its method name - * - * @param {String} method_name The name of the parser you are looking for - * - * @return {boolean} - */</span> - Twig.Templates.isRegisteredParser = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">method_name</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.parsers.hasOwnProperty(method_name); - }; - - <span class="hljs-comment">/** - * Save a template object to the store. - * - * @param {Twig.Template} template The twig.js template to store. - */</span> - Twig.Templates.save = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">template</span>) </span>{ - <span class="hljs-keyword">if</span> (template.id === <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to save template with no id"</span>); - } - Twig.Templates.registry[template.id] = template; - }; - - <span class="hljs-comment">/** - * 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. - */</span> - Twig.Templates.load = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">id</span>) </span>{ - <span class="hljs-keyword">if</span> (!Twig.Templates.registry.hasOwnProperty(id)) { - <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; - } - <span class="hljs-keyword">return</span> Twig.Templates.registry[id]; - }; - - <span class="hljs-comment">/** - * Load a template from a remote location using AJAX and saves in with the given ID. - * - * Available parameters: - * - * async: Should the HTTP request be performed asynchronously. - * Defaults to true. - * method: What method should be used to load the template - * (fs or ajax) - * parser: What method should be used to parse the template - * (twig or source) - * precompiled: Has the template already been compiled. - * - * @param {string} location The remote URL to load as a template. - * @param {Object} params The template parameters. - * @param {function} callback A callback triggered when the template finishes loading. - * @param {function} error_callback A callback triggered if an error occurs loading the template. - * - * - */</span> - Twig.Templates.loadRemote = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">location, params, callback, error_callback</span>) </span>{ - <span class="hljs-keyword">var</span> loader;</pre></div></div> - - </li> - - - <li id="section-83"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-83">¶</a> - </div> - <p>Default to async</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (params.async === <span class="hljs-literal">undefined</span>) { - params.async = <span class="hljs-literal">true</span>; - }</pre></div></div> - - </li> - - - <li id="section-84"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-84">¶</a> - </div> - <p>Default to the URL so the template is cached.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (params.id === <span class="hljs-literal">undefined</span>) { - params.id = location; - }</pre></div></div> - - </li> - - - <li id="section-85"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-85">¶</a> - </div> - <p>Check for existing template</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (Twig.cache && Twig.Templates.registry.hasOwnProperty(params.id)) {</pre></div></div> - - </li> - - - <li id="section-86"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-86">¶</a> - </div> - <p>A template is already saved with the given id.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> callback === <span class="hljs-string">'function'</span>) { - callback(Twig.Templates.registry[params.id]); - }</pre></div></div> - - </li> - - - <li id="section-87"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-87">¶</a> - </div> - <p>TODO: if async, return deferred promise</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> Twig.Templates.registry[params.id]; - }</pre></div></div> - - </li> - - - <li id="section-88"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-88">¶</a> - </div> - <p>if the parser name hasn’t been set, default it to twig</p> - - </div> - - <div class="content"><div class='highlight'><pre> params.parser = params.parser || <span class="hljs-string">'twig'</span>;</pre></div></div> - - </li> - - - <li id="section-89"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-89">¶</a> - </div> - <p>Assume ‘fs’ if the loader is not defined</p> - - </div> - - <div class="content"><div class='highlight'><pre> loader = <span class="hljs-keyword">this</span>.loaders[params.method] || <span class="hljs-keyword">this</span>.loaders.fs; - <span class="hljs-keyword">return</span> loader.apply(<span class="hljs-keyword">this</span>, <span class="hljs-built_in">arguments</span>); - };</pre></div></div> - - </li> - - - <li id="section-90"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-90">¶</a> - </div> - <p>Determine object type</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">is</span>(<span class="hljs-params">type, obj</span>) </span>{ - <span class="hljs-keyword">var</span> clas = <span class="hljs-built_in">Object</span>.prototype.toString.call(obj).slice(<span class="hljs-number">8</span>, <span class="hljs-number">-1</span>); - <span class="hljs-keyword">return</span> obj !== <span class="hljs-literal">undefined</span> && obj !== <span class="hljs-literal">null</span> && clas === type; - } - - <span class="hljs-comment">/** - * Create a new twig.js template. - * - * Parameters: { - * data: The template, either pre-compiled tokens or a string template - * id: The name of this template - * blocks: Any pre-existing block from a child template - * } - * - * @param {Object} params The template parameters. - */</span> - Twig.Template = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"> params </span>) </span>{ - <span class="hljs-keyword">var</span> data = params.data, - id = params.id, - blocks = params.blocks, - macros = params.macros || {}, - base = params.base, - path = params.path, - url = params.url, - name = params.name, - method = params.method,</pre></div></div> - - </li> - - - <li id="section-91"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-91">¶</a> - </div> - <p>parser options</p> - - </div> - - <div class="content"><div class='highlight'><pre> options = params.options;</pre></div></div> - - </li> - - - <li id="section-92"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-92">¶</a> - </div> - <h1 id="what-is-stored-in-a-twig-template">What is stored in a Twig.Template</h1> -<p>The Twig Template hold several chucks of data.</p> -<pre><code>{ - id: The token ID (<span class="hljs-keyword">if</span> any) - tokens: The list <span class="hljs-keyword">of</span> tokens that makes up <span class="hljs-keyword">this</span> template. - blocks: The list <span class="hljs-keyword">of</span> block <span class="hljs-keyword">this</span> template contains. - base: The base template (<span class="hljs-keyword">if</span> any) - options: { - Compiler/parser options - - strict_variables: <span class="hljs-literal">true</span>/<span class="hljs-literal">false</span> - Should missing variable/keys emit an error message. If <span class="hljs-literal">false</span>, they <span class="hljs-keyword">default</span> to <span class="hljs-literal">null</span>. - } -} -</code></pre> - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">this</span>.id = id; - <span class="hljs-keyword">this</span>.method = method; - <span class="hljs-keyword">this</span>.base = base; - <span class="hljs-keyword">this</span>.path = path; - <span class="hljs-keyword">this</span>.url = url; - <span class="hljs-keyword">this</span>.name = name; - <span class="hljs-keyword">this</span>.macros = macros; - <span class="hljs-keyword">this</span>.options = options; - - <span class="hljs-keyword">this</span>.reset(blocks); - - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">'String'</span>, data)) { - <span class="hljs-keyword">this</span>.tokens = Twig.prepare.apply(<span class="hljs-keyword">this</span>, [data]); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">this</span>.tokens = data; - } - - <span class="hljs-keyword">if</span> (id !== <span class="hljs-literal">undefined</span>) { - Twig.Templates.save(<span class="hljs-keyword">this</span>); - } - }; - - Twig.Template.prototype.reset = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">blocks</span>) </span>{ - Twig.log.debug(<span class="hljs-string">"Twig.Template.reset"</span>, <span class="hljs-string">"Reseting template "</span> + <span class="hljs-keyword">this</span>.id); - <span class="hljs-keyword">this</span>.blocks = {}; - <span class="hljs-keyword">this</span>.importedBlocks = []; - <span class="hljs-keyword">this</span>.originalBlockTokens = {}; - <span class="hljs-keyword">this</span>.child = { - blocks: blocks || {} - }; - <span class="hljs-keyword">this</span>.extend = <span class="hljs-literal">null</span>; - }; - - Twig.Template.prototype.render = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context, params</span>) </span>{ - params = params || {}; - - <span class="hljs-keyword">var</span> output, - url; - - <span class="hljs-keyword">this</span>.context = context || {};</pre></div></div> - - </li> - - - <li id="section-93"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-93">¶</a> - </div> - <p>Clear any previous state</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">this</span>.reset(); - <span class="hljs-keyword">if</span> (params.blocks) { - <span class="hljs-keyword">this</span>.blocks = params.blocks; - } - <span class="hljs-keyword">if</span> (params.macros) { - <span class="hljs-keyword">this</span>.macros = params.macros; - } - - output = Twig.parse.apply(<span class="hljs-keyword">this</span>, [<span class="hljs-keyword">this</span>.tokens, <span class="hljs-keyword">this</span>.context]);</pre></div></div> - - </li> - - - <li id="section-94"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-94">¶</a> - </div> - <p>Does this template extend another</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.extend) { - <span class="hljs-keyword">var</span> ext_template;</pre></div></div> - - </li> - - - <li id="section-95"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-95">¶</a> - </div> - <p>check if the template is provided inline</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">this</span>.options.allowInlineIncludes ) { - ext_template = Twig.Templates.load(<span class="hljs-keyword">this</span>.extend); - <span class="hljs-keyword">if</span> ( ext_template ) { - ext_template.options = <span class="hljs-keyword">this</span>.options; - } - }</pre></div></div> - - </li> - - - <li id="section-96"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-96">¶</a> - </div> - <p>check for the template file via include</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!ext_template) { - url = Twig.path.parsePath(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">this</span>.extend); - - ext_template = Twig.Templates.loadRemote(url, { - method: <span class="hljs-keyword">this</span>.getLoaderMethod(), - base: <span class="hljs-keyword">this</span>.base, - <span class="hljs-keyword">async</span>: <span class="hljs-literal">false</span>, - id: url, - options: <span class="hljs-keyword">this</span>.options - }); - } - - <span class="hljs-keyword">this</span>.parent = ext_template; - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.parent.render(<span class="hljs-keyword">this</span>.context, { - blocks: <span class="hljs-keyword">this</span>.blocks - }); - } - - <span class="hljs-keyword">if</span> (params.output == <span class="hljs-string">'blocks'</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.blocks; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (params.output == <span class="hljs-string">'macros'</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.macros; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> output; - } - }; - - Twig.Template.prototype.importFile = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">file</span>) </span>{ - <span class="hljs-keyword">var</span> url, sub_template; - <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.url && <span class="hljs-keyword">this</span>.options.allowInlineIncludes) { - file = <span class="hljs-keyword">this</span>.path ? <span class="hljs-keyword">this</span>.path + <span class="hljs-string">'/'</span> + file : file; - sub_template = Twig.Templates.load(file); - - <span class="hljs-keyword">if</span> (!sub_template) { - sub_template = Twig.Templates.loadRemote(url, { - id: file, - method: <span class="hljs-keyword">this</span>.getLoaderMethod(), - <span class="hljs-keyword">async</span>: <span class="hljs-literal">false</span>, - options: <span class="hljs-keyword">this</span>.options - }); - - <span class="hljs-keyword">if</span> (!sub_template) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to find the template "</span> + file); - } - } - - sub_template.options = <span class="hljs-keyword">this</span>.options; - - <span class="hljs-keyword">return</span> sub_template; - } - - url = Twig.path.parsePath(<span class="hljs-keyword">this</span>, file);</pre></div></div> - - </li> - - - <li id="section-97"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-97">¶</a> - </div> - <p>Load blocks from an external file</p> - - </div> - - <div class="content"><div class='highlight'><pre> sub_template = Twig.Templates.loadRemote(url, { - method: <span class="hljs-keyword">this</span>.getLoaderMethod(), - base: <span class="hljs-keyword">this</span>.base, - <span class="hljs-keyword">async</span>: <span class="hljs-literal">false</span>, - options: <span class="hljs-keyword">this</span>.options, - id: url - }); - - <span class="hljs-keyword">return</span> sub_template; - }; - - Twig.Template.prototype.importBlocks = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">file, override</span>) </span>{ - <span class="hljs-keyword">var</span> sub_template = <span class="hljs-keyword">this</span>.importFile(file), - context = <span class="hljs-keyword">this</span>.context, - that = <span class="hljs-keyword">this</span>, - key; - - override = override || <span class="hljs-literal">false</span>; - - sub_template.render(context);</pre></div></div> - - </li> - - - <li id="section-98"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-98">¶</a> - </div> - <p>Mixin blocks</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.forEach(<span class="hljs-built_in">Object</span>.keys(sub_template.blocks), <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{ - <span class="hljs-keyword">if</span> (override || that.blocks[key] === <span class="hljs-literal">undefined</span>) { - that.blocks[key] = sub_template.blocks[key]; - that.importedBlocks.push(key); - } - }); - }; - - Twig.Template.prototype.importMacros = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">file</span>) </span>{ - <span class="hljs-keyword">var</span> url = Twig.path.parsePath(<span class="hljs-keyword">this</span>, file);</pre></div></div> - - </li> - - - <li id="section-99"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-99">¶</a> - </div> - <p>load remote template</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> remoteTemplate = Twig.Templates.loadRemote(url, { - method: <span class="hljs-keyword">this</span>.getLoaderMethod(), - <span class="hljs-keyword">async</span>: <span class="hljs-literal">false</span>, - id: url - }); - - <span class="hljs-keyword">return</span> remoteTemplate; - }; - - Twig.Template.prototype.getLoaderMethod = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.path) { - <span class="hljs-keyword">return</span> <span class="hljs-string">'fs'</span>; - } - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.url) { - <span class="hljs-keyword">return</span> <span class="hljs-string">'ajax'</span>; - } - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.method || <span class="hljs-string">'fs'</span>; - }; - - Twig.Template.prototype.compile = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">options</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-100"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-100">¶</a> - </div> - <p>compile the template into raw JS</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> Twig.compiler.compile(<span class="hljs-keyword">this</span>, options); - }; - - <span class="hljs-comment">/** - * Create safe output - * - * @param {string} Content safe to output - * - * @return {String} Content wrapped into a String - */</span> - - Twig.Markup = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">content, strategy</span>) </span>{ - <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> strategy == <span class="hljs-string">'undefined'</span>) { - strategy = <span class="hljs-literal">true</span>; - } - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> content === <span class="hljs-string">'string'</span> && content.length > <span class="hljs-number">0</span>) { - content = <span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>(content); - content.twig_markup = strategy; - } - <span class="hljs-keyword">return</span> content; - }; - - <span class="hljs-keyword">return</span> Twig; - -}) (Twig || { }); - -(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> - 'use strict'</span>; - - Twig.Templates.registerLoader(<span class="hljs-string">'ajax'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">location, params, callback, error_callback</span>) </span>{ - <span class="hljs-keyword">var</span> template, - xmlhttp, - precompiled = params.precompiled, - parser = <span class="hljs-keyword">this</span>.parsers[params.parser] || <span class="hljs-keyword">this</span>.parser.twig; - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> XMLHttpRequest === <span class="hljs-string">"undefined"</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Unsupported platform: Unable to do ajax requests '</span> + - <span class="hljs-string">'because there is no "XMLHTTPRequest" implementation'</span>); - } - - xmlhttp = <span class="hljs-keyword">new</span> XMLHttpRequest(); - xmlhttp.onreadystatechange = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> data = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">if</span>(xmlhttp.readyState === <span class="hljs-number">4</span>) { - <span class="hljs-keyword">if</span> (xmlhttp.status === <span class="hljs-number">200</span> || (<span class="hljs-built_in">window</span>.cordova && xmlhttp.status == <span class="hljs-number">0</span>)) { - Twig.log.debug(<span class="hljs-string">"Got template "</span>, xmlhttp.responseText); - - <span class="hljs-keyword">if</span> (precompiled === <span class="hljs-literal">true</span>) { - data = <span class="hljs-built_in">JSON</span>.parse(xmlhttp.responseText); - } <span class="hljs-keyword">else</span> { - data = xmlhttp.responseText; - } - - params.url = location; - params.data = data; - - template = parser.call(<span class="hljs-keyword">this</span>, params); - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> callback === <span class="hljs-string">'function'</span>) { - callback(template); - } - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> error_callback === <span class="hljs-string">'function'</span>) { - error_callback(xmlhttp); - } - } - } - }; - xmlhttp.open(<span class="hljs-string">"GET"</span>, location, !!params.async); - xmlhttp.send(); - - <span class="hljs-keyword">if</span> (params.async) {</pre></div></div> - - </li> - - - <li id="section-101"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-101">¶</a> - </div> - <p>TODO: return deferred promise</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> template; - } - }); - -}(Twig));(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> 'use strict'</span>; - - <span class="hljs-keyword">var</span> fs, path; - - <span class="hljs-keyword">try</span> {</pre></div></div> - - </li> - - - <li id="section-102"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-102">¶</a> - </div> - <p>require lib dependencies at runtime</p> - - </div> - - <div class="content"><div class='highlight'><pre> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>); - path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>); - } <span class="hljs-keyword">catch</span> (e) {</pre></div></div> - - </li> - - - <li id="section-103"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-103">¶</a> - </div> - <p>NOTE: this is in a try/catch to avoid errors cross platform</p> - - </div> - - <div class="content"><div class='highlight'><pre> } - - Twig.Templates.registerLoader(<span class="hljs-string">'fs'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">location, params, callback, error_callback</span>) </span>{ - <span class="hljs-keyword">var</span> template, - data = <span class="hljs-literal">null</span>, - precompiled = params.precompiled, - parser = <span class="hljs-keyword">this</span>.parsers[params.parser] || <span class="hljs-keyword">this</span>.parser.twig; - - <span class="hljs-keyword">if</span> (!fs || !path) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Unsupported platform: Unable to load from file '</span> + - <span class="hljs-string">'because there is no "fs" or "path" implementation'</span>); - } - - <span class="hljs-keyword">var</span> loadTemplateFn = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, data</span>) </span>{ - <span class="hljs-keyword">if</span> (err) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> error_callback === <span class="hljs-string">'function'</span>) { - error_callback(err); - } - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">if</span> (precompiled === <span class="hljs-literal">true</span>) { - data = <span class="hljs-built_in">JSON</span>.parse(data); - } - - params.data = data; - params.path = params.path || location;</pre></div></div> - - </li> - - - <li id="section-104"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-104">¶</a> - </div> - <p>template is in data</p> - - </div> - - <div class="content"><div class='highlight'><pre> template = parser.call(<span class="hljs-keyword">this</span>, params); - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> callback === <span class="hljs-string">'function'</span>) { - callback(template); - } - }; - params.path = params.path || location; - - <span class="hljs-keyword">if</span> (params.async) { - fs.stat(params.path, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, stats</span>) </span>{ - <span class="hljs-keyword">if</span> (err || !stats.isFile()) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Unable to find template file '</span> + location); - } - fs.readFile(params.path, <span class="hljs-string">'utf8'</span>, loadTemplateFn); - });</pre></div></div> - - </li> - - - <li id="section-105"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-105">¶</a> - </div> - <p>TODO: return deferred promise</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">if</span> (!fs.statSync(params.path).isFile()) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Unable to find template file '</span> + location); - } - data = fs.readFileSync(params.path, <span class="hljs-string">'utf8'</span>); - loadTemplateFn(<span class="hljs-literal">undefined</span>, data); - <span class="hljs-keyword">return</span> template - } - }); - -}(Twig));(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Twig</span>)</span>{ -<span class="hljs-meta"> 'use strict'</span>; - - Twig.Templates.registerParser(<span class="hljs-string">'source'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">params</span>) </span>{ - <span class="hljs-keyword">return</span> params.data || <span class="hljs-string">''</span>; - }); -})(Twig); -(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Twig</span>)</span>{ -<span class="hljs-meta"> 'use strict'</span>; - - Twig.Templates.registerParser(<span class="hljs-string">'twig'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">params</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Twig.Template(params); - }); -})(Twig);</pre></div></div> - - </li> - - - <li id="section-106"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-106">¶</a> - </div> - <p>The following methods are from MDN and are available under a -<a href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> or are -<a href="https://developer.mozilla.org/Project:Copyrights">Public Domain</a>.</p> -<p>See:</p> -<ul> -<li><a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys">Object.keys - MDN</a></li> -</ul> - - </div> - - </li> - - - <li id="section-107"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-107">¶</a> - </div> - <h2 id="twig-fills-js">twig.fills.js</h2> -<p>This file contains fills for backwards compatability.</p> - - </div> - - <div class="content"><div class='highlight'><pre>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ -<span class="hljs-meta"> "use strict"</span>;</pre></div></div> - - </li> - - - <li id="section-108"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-108">¶</a> - </div> - <p>Handle methods that don’t yet exist in every browser</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">String</span>.prototype.trim) { - <span class="hljs-built_in">String</span>.prototype.trim = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.replace(<span class="hljs-regexp">/^\s+|\s+$/g</span>,<span class="hljs-string">''</span>); - } - }; - - <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">Object</span>.keys) <span class="hljs-built_in">Object</span>.keys = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">o</span>)</span>{ - <span class="hljs-keyword">if</span> (o !== <span class="hljs-built_in">Object</span>(o)) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Object.keys called on non-object'</span>); - } - <span class="hljs-keyword">var</span> ret = [], p; - <span class="hljs-keyword">for</span> (p <span class="hljs-keyword">in</span> o) <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Object</span>.prototype.hasOwnProperty.call(o, p)) ret.push(p); - <span class="hljs-keyword">return</span> ret; - } - -})();</pre></div></div> - - </li> - - - <li id="section-109"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-109">¶</a> - </div> - <h2 id="twig-lib-js">twig.lib.js</h2> -<p>This file contains 3rd party libraries used within twig.</p> -<p>Copies of the licenses for the code included here can be found in the -LICENSES.md file.</p> - - </div> - - <div class="content"><div class='highlight'><pre> -<span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Twig</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-110"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-110">¶</a> - </div> - <p>Namespace for libraries</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.lib = { }; - - <span class="hljs-comment">/** - sprintf() for JavaScript 1.0.3 - https://github.com/alexei/sprintf.js - **/</span> - <span class="hljs-keyword">var</span> sprintfLib = (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> re = { - not_string: <span class="hljs-regexp">/[^s]/</span>, - number: <span class="hljs-regexp">/[diefg]/</span>, - json: <span class="hljs-regexp">/[j]/</span>, - not_json: <span class="hljs-regexp">/[^j]/</span>, - text: <span class="hljs-regexp">/^[^\x25]+/</span>, - modulo: <span class="hljs-regexp">/^\x25{2}/</span>, - placeholder: <span class="hljs-regexp">/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/</span>, - key: <span class="hljs-regexp">/^([a-z_][a-z_\d]*)/i</span>, - key_access: <span class="hljs-regexp">/^\.([a-z_][a-z_\d]*)/i</span>, - index_access: <span class="hljs-regexp">/^\[(\d+)\]/</span>, - sign: <span class="hljs-regexp">/^[\+\-]/</span> - } - - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sprintf</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> key = <span class="hljs-built_in">arguments</span>[<span class="hljs-number">0</span>], cache = sprintf.cache - <span class="hljs-keyword">if</span> (!(cache[key] && cache.hasOwnProperty(key))) { - cache[key] = sprintf.parse(key) - } - <span class="hljs-keyword">return</span> sprintf.format.call(<span class="hljs-literal">null</span>, cache[key], <span class="hljs-built_in">arguments</span>) - } - - sprintf.format = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parse_tree, argv</span>) </span>{ - <span class="hljs-keyword">var</span> cursor = <span class="hljs-number">1</span>, tree_length = parse_tree.length, node_type = <span class="hljs-string">""</span>, arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = <span class="hljs-literal">true</span>, sign = <span class="hljs-string">""</span> - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < tree_length; i++) { - node_type = get_type(parse_tree[i]) - <span class="hljs-keyword">if</span> (node_type === <span class="hljs-string">"string"</span>) { - output[output.length] = parse_tree[i] - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (node_type === <span class="hljs-string">"array"</span>) { - match = parse_tree[i] <span class="hljs-comment">// convenience purposes only</span> - <span class="hljs-keyword">if</span> (match[<span class="hljs-number">2</span>]) { <span class="hljs-comment">// keyword argument</span> - arg = argv[cursor] - <span class="hljs-keyword">for</span> (k = <span class="hljs-number">0</span>; k < match[<span class="hljs-number">2</span>].length; k++) { - <span class="hljs-keyword">if</span> (!arg.hasOwnProperty(match[<span class="hljs-number">2</span>][k])) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(sprintf(<span class="hljs-string">"[sprintf] property '%s' does not exist"</span>, match[<span class="hljs-number">2</span>][k])) - } - arg = arg[match[<span class="hljs-number">2</span>][k]] - } - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">1</span>]) { <span class="hljs-comment">// positional argument (explicit)</span> - arg = argv[match[<span class="hljs-number">1</span>]] - } - <span class="hljs-keyword">else</span> { <span class="hljs-comment">// positional argument (implicit)</span> - arg = argv[cursor++] - } - - <span class="hljs-keyword">if</span> (get_type(arg) == <span class="hljs-string">"function"</span>) { - arg = arg() - } - - <span class="hljs-keyword">if</span> (re.not_string.test(match[<span class="hljs-number">8</span>]) && re.not_json.test(match[<span class="hljs-number">8</span>]) && (get_type(arg) != <span class="hljs-string">"number"</span> && <span class="hljs-built_in">isNaN</span>(arg))) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(sprintf(<span class="hljs-string">"[sprintf] expecting number but found %s"</span>, get_type(arg))) - } - - <span class="hljs-keyword">if</span> (re.number.test(match[<span class="hljs-number">8</span>])) { - is_positive = arg >= <span class="hljs-number">0</span> - } - - <span class="hljs-keyword">switch</span> (match[<span class="hljs-number">8</span>]) { - <span class="hljs-keyword">case</span> <span class="hljs-string">"b"</span>: - arg = arg.toString(<span class="hljs-number">2</span>) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"c"</span>: - arg = <span class="hljs-built_in">String</span>.fromCharCode(arg) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"d"</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">"i"</span>: - arg = <span class="hljs-built_in">parseInt</span>(arg, <span class="hljs-number">10</span>) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"j"</span>: - arg = <span class="hljs-built_in">JSON</span>.stringify(arg, <span class="hljs-literal">null</span>, match[<span class="hljs-number">6</span>] ? <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">6</span>]) : <span class="hljs-number">0</span>) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"e"</span>: - arg = match[<span class="hljs-number">7</span>] ? arg.toExponential(match[<span class="hljs-number">7</span>]) : arg.toExponential() - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"f"</span>: - arg = match[<span class="hljs-number">7</span>] ? <span class="hljs-built_in">parseFloat</span>(arg).toFixed(match[<span class="hljs-number">7</span>]) : <span class="hljs-built_in">parseFloat</span>(arg) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"g"</span>: - arg = match[<span class="hljs-number">7</span>] ? <span class="hljs-built_in">parseFloat</span>(arg).toPrecision(match[<span class="hljs-number">7</span>]) : <span class="hljs-built_in">parseFloat</span>(arg) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"o"</span>: - arg = arg.toString(<span class="hljs-number">8</span>) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"s"</span>: - arg = ((arg = <span class="hljs-built_in">String</span>(arg)) && match[<span class="hljs-number">7</span>] ? arg.substring(<span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>]) : arg) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"u"</span>: - arg = arg >>> <span class="hljs-number">0</span> - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"x"</span>: - arg = arg.toString(<span class="hljs-number">16</span>) - <span class="hljs-keyword">break</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">"X"</span>: - arg = arg.toString(<span class="hljs-number">16</span>).toUpperCase() - <span class="hljs-keyword">break</span> - } - <span class="hljs-keyword">if</span> (re.json.test(match[<span class="hljs-number">8</span>])) { - output[output.length] = arg - } - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">if</span> (re.number.test(match[<span class="hljs-number">8</span>]) && (!is_positive || match[<span class="hljs-number">3</span>])) { - sign = is_positive ? <span class="hljs-string">"+"</span> : <span class="hljs-string">"-"</span> - arg = arg.toString().replace(re.sign, <span class="hljs-string">""</span>) - } - <span class="hljs-keyword">else</span> { - sign = <span class="hljs-string">""</span> - } - pad_character = match[<span class="hljs-number">4</span>] ? match[<span class="hljs-number">4</span>] === <span class="hljs-string">"0"</span> ? <span class="hljs-string">"0"</span> : match[<span class="hljs-number">4</span>].charAt(<span class="hljs-number">1</span>) : <span class="hljs-string">" "</span> - pad_length = match[<span class="hljs-number">6</span>] - (sign + arg).length - pad = match[<span class="hljs-number">6</span>] ? (pad_length > <span class="hljs-number">0</span> ? str_repeat(pad_character, pad_length) : <span class="hljs-string">""</span>) : <span class="hljs-string">""</span> - output[output.length] = match[<span class="hljs-number">5</span>] ? sign + arg + pad : (pad_character === <span class="hljs-string">"0"</span> ? sign + pad + arg : pad + sign + arg) - } - } - } - <span class="hljs-keyword">return</span> output.join(<span class="hljs-string">""</span>) - } - - sprintf.cache = {} - - sprintf.parse = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fmt</span>) </span>{ - <span class="hljs-keyword">var</span> _fmt = fmt, match = [], parse_tree = [], arg_names = <span class="hljs-number">0</span> - <span class="hljs-keyword">while</span> (_fmt) { - <span class="hljs-keyword">if</span> ((match = re.text.exec(_fmt)) !== <span class="hljs-literal">null</span>) { - parse_tree[parse_tree.length] = match[<span class="hljs-number">0</span>] - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((match = re.modulo.exec(_fmt)) !== <span class="hljs-literal">null</span>) { - parse_tree[parse_tree.length] = <span class="hljs-string">"%"</span> - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((match = re.placeholder.exec(_fmt)) !== <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">if</span> (match[<span class="hljs-number">2</span>]) { - arg_names |= <span class="hljs-number">1</span> - <span class="hljs-keyword">var</span> field_list = [], replacement_field = match[<span class="hljs-number">2</span>], field_match = [] - <span class="hljs-keyword">if</span> ((field_match = re.key.exec(replacement_field)) !== <span class="hljs-literal">null</span>) { - field_list[field_list.length] = field_match[<span class="hljs-number">1</span>] - <span class="hljs-keyword">while</span> ((replacement_field = replacement_field.substring(field_match[<span class="hljs-number">0</span>].length)) !== <span class="hljs-string">""</span>) { - <span class="hljs-keyword">if</span> ((field_match = re.key_access.exec(replacement_field)) !== <span class="hljs-literal">null</span>) { - field_list[field_list.length] = field_match[<span class="hljs-number">1</span>] - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((field_match = re.index_access.exec(replacement_field)) !== <span class="hljs-literal">null</span>) { - field_list[field_list.length] = field_match[<span class="hljs-number">1</span>] - } - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">SyntaxError</span>(<span class="hljs-string">"[sprintf] failed to parse named argument key"</span>) - } - } - } - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">SyntaxError</span>(<span class="hljs-string">"[sprintf] failed to parse named argument key"</span>) - } - match[<span class="hljs-number">2</span>] = field_list - } - <span class="hljs-keyword">else</span> { - arg_names |= <span class="hljs-number">2</span> - } - <span class="hljs-keyword">if</span> (arg_names === <span class="hljs-number">3</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"[sprintf] mixing positional and named placeholders is not (yet) supported"</span>) - } - parse_tree[parse_tree.length] = match - } - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">SyntaxError</span>(<span class="hljs-string">"[sprintf] unexpected placeholder"</span>) - } - _fmt = _fmt.substring(match[<span class="hljs-number">0</span>].length) - } - <span class="hljs-keyword">return</span> parse_tree - } - - <span class="hljs-keyword">var</span> vsprintf = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fmt, argv, _argv</span>) </span>{ - _argv = (argv || []).slice(<span class="hljs-number">0</span>) - _argv.splice(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, fmt) - <span class="hljs-keyword">return</span> sprintf.apply(<span class="hljs-literal">null</span>, _argv) - } - - <span class="hljs-comment">/** - * helpers - */</span> - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get_type</span>(<span class="hljs-params">variable</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.prototype.toString.call(variable).slice(<span class="hljs-number">8</span>, <span class="hljs-number">-1</span>).toLowerCase() - } - - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">str_repeat</span>(<span class="hljs-params">input, multiplier</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-built_in">Array</span>(multiplier + <span class="hljs-number">1</span>).join(input) - } - - <span class="hljs-comment">/** - * export - */</span> - <span class="hljs-keyword">return</span> { - sprintf: sprintf, - vsprintf: vsprintf - } - })(); - - <span class="hljs-keyword">var</span> sprintf = sprintfLib.sprintf; - <span class="hljs-keyword">var</span> vsprintf = sprintfLib.vsprintf;</pre></div></div> - - </li> - - - <li id="section-111"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-111">¶</a> - </div> - <p>Expose to Twig</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.lib.sprintf = sprintf; - Twig.lib.vsprintf = vsprintf; - - - <span class="hljs-comment">/** - * jPaq - A fully customizable JavaScript/JScript library - * http://jpaq.org/ - * - * Copyright (c) 2011 Christopher West - * Licensed under the MIT license. - * http://jpaq.org/license/ - * - * Version: 1.0.6.0000W - * Revised: April 6, 2011 - */</span> - ; (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> shortDays = <span class="hljs-string">"Sun,Mon,Tue,Wed,Thu,Fri,Sat"</span>.split(<span class="hljs-string">","</span>); - <span class="hljs-keyword">var</span> fullDays = <span class="hljs-string">"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday"</span>.split(<span class="hljs-string">","</span>); - <span class="hljs-keyword">var</span> shortMonths = <span class="hljs-string">"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec"</span>.split(<span class="hljs-string">","</span>); - <span class="hljs-keyword">var</span> fullMonths = <span class="hljs-string">"January,February,March,April,May,June,July,August,September,October,November,December"</span>.split(<span class="hljs-string">","</span>); - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getOrdinalFor</span>(<span class="hljs-params">intNum</span>) </span>{ - <span class="hljs-keyword">return</span> (((intNum = <span class="hljs-built_in">Math</span>.abs(intNum) % <span class="hljs-number">100</span>) % <span class="hljs-number">10</span> == <span class="hljs-number">1</span> && intNum != <span class="hljs-number">11</span>) ? <span class="hljs-string">"st"</span> - : (intNum % <span class="hljs-number">10</span> == <span class="hljs-number">2</span> && intNum != <span class="hljs-number">12</span>) ? <span class="hljs-string">"nd"</span> : (intNum % <span class="hljs-number">10</span> == <span class="hljs-number">3</span> - && intNum != <span class="hljs-number">13</span>) ? <span class="hljs-string">"rd"</span> : <span class="hljs-string">"th"</span>); - } - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getISO8601Year</span>(<span class="hljs-params">aDate</span>) </span>{ - <span class="hljs-keyword">var</span> d = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(aDate.getFullYear() + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">4</span>); - <span class="hljs-keyword">if</span>((d - aDate) / <span class="hljs-number">86400000</span> < <span class="hljs-number">7</span> && (aDate.getDay() + <span class="hljs-number">6</span>) % <span class="hljs-number">7</span> < (d.getDay() + <span class="hljs-number">6</span>) % <span class="hljs-number">7</span>) - <span class="hljs-keyword">return</span> d.getFullYear(); - <span class="hljs-keyword">if</span>(aDate.getMonth() > <span class="hljs-number">0</span> || aDate.getDate() >= <span class="hljs-number">4</span>) - <span class="hljs-keyword">return</span> aDate.getFullYear(); - <span class="hljs-keyword">return</span> aDate.getFullYear() - (((aDate.getDay() + <span class="hljs-number">6</span>) % <span class="hljs-number">7</span> - aDate.getDate() > <span class="hljs-number">2</span>) ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>); - } - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getISO8601Week</span>(<span class="hljs-params">aDate</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-112"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-112">¶</a> - </div> - <p>Get a day during the first week of the year.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> d = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(getISO8601Year(aDate), <span class="hljs-number">0</span>, <span class="hljs-number">4</span>);</pre></div></div> - - </li> - - - <li id="section-113"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-113">¶</a> - </div> - <p>Get the first monday of the year.</p> - - </div> - - <div class="content"><div class='highlight'><pre> d.setDate(d.getDate() - (d.getDay() + <span class="hljs-number">6</span>) % <span class="hljs-number">7</span>); - <span class="hljs-keyword">return</span> <span class="hljs-built_in">parseInt</span>((aDate - d) / <span class="hljs-number">604800000</span>) + <span class="hljs-number">1</span>; - } - Twig.lib.formatDate = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">date, format</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-114"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-114">¶</a> - </div> - <p>/ <summary> -/ Gets a string for this date, formatted according to the given format -/ string. -/ </summary> -/ <param name="format" type="String"> -/ 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: -/ <a href="http://php.net/manual/en/function.date.php">http://php.net/manual/en/function.date.php</a>. -/ 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 -/ 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. -/ w - Numeric representation of the day of the week (starting from 0) -/ z - The day of the year (starting from 0) -/ W - ISO-8601 week number of year, weeks starting on Monday -/ F - A full textual representation of a month, such as January or March -/ m - Numeric representation of a month, with leading zeros -/ 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 -/ 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. -/ Y - A full numeric representation of a year, 4 digits -/ y - A two digit representation of a year -/ a - Lowercase Ante meridiem and Post meridiem -/ A - Uppercase Ante meridiem and Post meridiem -/ B - Swatch Internet time -/ g - 12-hour format of an hour without leading zeros -/ G - 24-hour format of an hour without leading zeros -/ h - 12-hour format of an hour with leading zeros -/ H - 24-hour format of an hour with leading zeros -/ i - Minutes with leading zeros -/ s - Seconds, with leading zeros -/ u - Milliseconds -/ U - Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) -/ </param> -/ <returns type="String"> -/ Returns the string for this date, formatted according to the given -/ format string. -/ </returns> -If the format was not passed, use the default toString method.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> format !== <span class="hljs-string">"string"</span> || <span class="hljs-regexp">/^\s*$/</span>.test(format)) - <span class="hljs-keyword">return</span> date + <span class="hljs-string">""</span>; - <span class="hljs-keyword">var</span> jan1st = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(date.getFullYear(), <span class="hljs-number">0</span>, <span class="hljs-number">1</span>); - <span class="hljs-keyword">var</span> me = date; - <span class="hljs-keyword">return</span> format.replace(<span class="hljs-regexp">/[dDjlNSwzWFmMntLoYyaABgGhHisuU]/g</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">option</span>) </span>{ - <span class="hljs-keyword">switch</span>(option) {</pre></div></div> - - </li> - - - <li id="section-115"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-115">¶</a> - </div> - <p>Day of the month, 2 digits with leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"d"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + me.getDate()).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-116"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-116">¶</a> - </div> - <p>A textual representation of a day, three letters</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"D"</span>: <span class="hljs-keyword">return</span> shortDays[me.getDay()];</pre></div></div> - - </li> - - - <li id="section-117"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-117">¶</a> - </div> - <p>Day of the month without leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"j"</span>: <span class="hljs-keyword">return</span> me.getDate();</pre></div></div> - - </li> - - - <li id="section-118"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-118">¶</a> - </div> - <p>A full textual representation of the day of the week</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"l"</span>: <span class="hljs-keyword">return</span> fullDays[me.getDay()];</pre></div></div> - - </li> - - - <li id="section-119"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-119">¶</a> - </div> - <p>ISO-8601 numeric representation of the day of the week</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"N"</span>: <span class="hljs-keyword">return</span> (me.getDay() + <span class="hljs-number">6</span>) % <span class="hljs-number">7</span> + <span class="hljs-number">1</span>;</pre></div></div> - - </li> - - - <li id="section-120"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-120">¶</a> - </div> - <p>English ordinal suffix for the day of the month, 2 characters</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"S"</span>: <span class="hljs-keyword">return</span> getOrdinalFor(me.getDate());</pre></div></div> - - </li> - - - <li id="section-121"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-121">¶</a> - </div> - <p>Numeric representation of the day of the week</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"w"</span>: <span class="hljs-keyword">return</span> me.getDay();</pre></div></div> - - </li> - - - <li id="section-122"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-122">¶</a> - </div> - <p>The day of the year (starting from 0)</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"z"</span>: <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.ceil((jan1st - me) / <span class="hljs-number">86400000</span>);</pre></div></div> - - </li> - - - <li id="section-123"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-123">¶</a> - </div> - <p>ISO-8601 week number of year, weeks starting on Monday</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"W"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + getISO8601Week(me)).replace(<span class="hljs-regexp">/^.(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-124"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-124">¶</a> - </div> - <p>A full textual representation of a month, such as January or March</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"F"</span>: <span class="hljs-keyword">return</span> fullMonths[me.getMonth()];</pre></div></div> - - </li> - - - <li id="section-125"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-125">¶</a> - </div> - <p>Numeric representation of a month, with leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"m"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + (me.getMonth() + <span class="hljs-number">1</span>)).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-126"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-126">¶</a> - </div> - <p>A short textual representation of a month, three letters</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"M"</span>: <span class="hljs-keyword">return</span> shortMonths[me.getMonth()];</pre></div></div> - - </li> - - - <li id="section-127"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-127">¶</a> - </div> - <p>Numeric representation of a month, without leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"n"</span>: <span class="hljs-keyword">return</span> me.getMonth() + <span class="hljs-number">1</span>;</pre></div></div> - - </li> - - - <li id="section-128"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-128">¶</a> - </div> - <p>Number of days in the given month</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"t"</span>: <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(me.getFullYear(), me.getMonth() + <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>).getDate();</pre></div></div> - - </li> - - - <li id="section-129"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-129">¶</a> - </div> - <p>Whether it’s a leap year</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"L"</span>: <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(me.getFullYear(), <span class="hljs-number">1</span>, <span class="hljs-number">29</span>).getDate() == <span class="hljs-number">29</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>;</pre></div></div> - - </li> - - - <li id="section-130"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-130">¶</a> - </div> - <p>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.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"o"</span>: <span class="hljs-keyword">return</span> getISO8601Year(me);</pre></div></div> - - </li> - - - <li id="section-131"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-131">¶</a> - </div> - <p>A full numeric representation of a year, 4 digits</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"Y"</span>: <span class="hljs-keyword">return</span> me.getFullYear();</pre></div></div> - - </li> - - - <li id="section-132"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-132">¶</a> - </div> - <p>A two digit representation of a year</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"y"</span>: <span class="hljs-keyword">return</span> (me.getFullYear() + <span class="hljs-string">""</span>).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-133"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-133">¶</a> - </div> - <p>Lowercase Ante meridiem and Post meridiem</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"a"</span>: <span class="hljs-keyword">return</span> me.getHours() < <span class="hljs-number">12</span> ? <span class="hljs-string">"am"</span> : <span class="hljs-string">"pm"</span>;</pre></div></div> - - </li> - - - <li id="section-134"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-134">¶</a> - </div> - <p>Uppercase Ante meridiem and Post meridiem</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"A"</span>: <span class="hljs-keyword">return</span> me.getHours() < <span class="hljs-number">12</span> ? <span class="hljs-string">"AM"</span> : <span class="hljs-string">"PM"</span>;</pre></div></div> - - </li> - - - <li id="section-135"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-135">¶</a> - </div> - <p>Swatch Internet time</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"B"</span>: <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.floor((((me.getUTCHours() + <span class="hljs-number">1</span>) % <span class="hljs-number">24</span>) + me.getUTCMinutes() / <span class="hljs-number">60</span> + me.getUTCSeconds() / <span class="hljs-number">3600</span>) * <span class="hljs-number">1000</span> / <span class="hljs-number">24</span>);</pre></div></div> - - </li> - - - <li id="section-136"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-136">¶</a> - </div> - <p>12-hour format of an hour without leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"g"</span>: <span class="hljs-keyword">return</span> me.getHours() % <span class="hljs-number">12</span> != <span class="hljs-number">0</span> ? me.getHours() % <span class="hljs-number">12</span> : <span class="hljs-number">12</span>;</pre></div></div> - - </li> - - - <li id="section-137"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-137">¶</a> - </div> - <p>24-hour format of an hour without leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"G"</span>: <span class="hljs-keyword">return</span> me.getHours();</pre></div></div> - - </li> - - - <li id="section-138"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-138">¶</a> - </div> - <p>12-hour format of an hour with leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"h"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + (me.getHours() % <span class="hljs-number">12</span> != <span class="hljs-number">0</span> ? me.getHours() % <span class="hljs-number">12</span> : <span class="hljs-number">12</span>)).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-139"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-139">¶</a> - </div> - <p>24-hour format of an hour with leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"H"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + me.getHours()).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-140"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-140">¶</a> - </div> - <p>Minutes with leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"i"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + me.getMinutes()).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-141"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-141">¶</a> - </div> - <p>Seconds, with leading zeros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"s"</span>: <span class="hljs-keyword">return</span> (<span class="hljs-string">"0"</span> + me.getSeconds()).replace(<span class="hljs-regexp">/^.+(..)$/</span>, <span class="hljs-string">"$1"</span>);</pre></div></div> - - </li> - - - <li id="section-142"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-142">¶</a> - </div> - <p>Milliseconds</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"u"</span>: <span class="hljs-keyword">return</span> me.getMilliseconds();</pre></div></div> - - </li> - - - <li id="section-143"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-143">¶</a> - </div> - <p>Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">"U"</span>: <span class="hljs-keyword">return</span> me.getTime() / <span class="hljs-number">1000</span>; - } - }); - }; - })(); - - Twig.lib.strip_tags = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">input, allowed</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-144"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-144">¶</a> - </div> - <p>Strips HTML and PHP tags from a string</p> -<p>version: 1109.2015 -discuss at: <a href="http://phpjs.org/functions/strip_tags">http://phpjs.org/functions/strip_tags</a></p> -<ul> -<li>original by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>)</li> -<li>improved by: Luke Godfrey</li> -<li>input by: Pul</li> -<li>bugfixed by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>)</li> -<li>bugfixed by: Onno Marsman</li> -<li>input by: Alex</li> -<li>bugfixed by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>)</li> -<li>input by: Marc Palau</li> -<li>improved by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>)</li> -<li>input by: Brett Zamir (<a href="http://brett-zamir.me">http://brett-zamir.me</a>)</li> -<li>bugfixed by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>)</li> -<li>bugfixed by: Eric Nagel</li> -<li>input by: Bobby Drake</li> -<li>bugfixed by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>)</li> -<li>bugfixed by: Tomasz Wesolowski</li> -<li>input by: Evertjan Garretsen</li> -<li>revised by: Rafał Kukawski (<a href="http://blog.kukawski.pl/">http://blog.kukawski.pl/</a>)</li> -<li>example 1: strip_tags(‘<p>Kevin</p> <b>van</b> <i>Zonneveld</i>‘, ‘<i><b>‘);</li> -<li>returns 1: ‘Kevin <b>van</b> <i>Zonneveld</i>‘</li> -<li>example 2: strip_tags(‘<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>‘, ‘<p>‘);</li> -<li>returns 2: ‘<p>Kevin van Zonneveld</p>‘</li> -<li>example 3: strip_tags(“<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>“, “<a>“);</li> -<li>returns 3: ‘<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>‘</li> -<li>example 4: strip_tags(‘1 < 5 5 > 1’);</li> -<li>returns 4: ‘1 < 5 5 > 1’</li> -<li>example 5: strip_tags(‘1 <br/> 1’);</li> -<li>returns 5: ‘1 1’</li> -<li>example 6: strip_tags(‘1 <br/> 1’, ‘<br>‘);</li> -<li>returns 6: ‘1 1’</li> -<li>example 7: strip_tags(‘1 <br/> 1’, ‘<br><br/>‘);</li> -<li>returns 7: ‘1 <br/> 1’</li> -</ul> - - </div> - - <div class="content"><div class='highlight'><pre> allowed = (((allowed || <span class="hljs-string">""</span>) + <span class="hljs-string">""</span>).toLowerCase().match(<span class="hljs-regexp">/<[a-z][a-z0-9]*>/g</span>) || []).join(<span class="hljs-string">''</span>); <span class="hljs-comment">// making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)</span> - <span class="hljs-keyword">var</span> tags = <span class="hljs-regexp">/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi</span>, - commentsAndPhpTags = <span class="hljs-regexp">/<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi</span>; - <span class="hljs-keyword">return</span> input.replace(commentsAndPhpTags, <span class="hljs-string">''</span>).replace(tags, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">$0, $1</span>) </span>{ - <span class="hljs-keyword">return</span> allowed.indexOf(<span class="hljs-string">'<'</span> + $<span class="hljs-number">1.</span>toLowerCase() + <span class="hljs-string">'>'</span>) > <span class="hljs-number">-1</span> ? $<span class="hljs-number">0</span> : <span class="hljs-string">''</span>; - }); - } - - Twig.lib.parseISO8601Date = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">s</span>)</span>{</pre></div></div> - - </li> - - - <li id="section-145"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-145">¶</a> - </div> - <p>Taken from <a href="http://n8v.enteuxis.org/2010/12/parsing-iso-8601-dates-in-javascript/">http://n8v.enteuxis.org/2010/12/parsing-iso-8601-dates-in-javascript/</a> -parenthese matches: -year month day hours minutes seconds<br>dotmilliseconds -tzstring plusminus hours minutes</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> re = <span class="hljs-regexp">/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+-])(\d\d):(\d\d))/</span>; - - <span class="hljs-keyword">var</span> d = []; - d = s.match(re);</pre></div></div> - - </li> - - - <li id="section-146"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-146">¶</a> - </div> - <p>“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]</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">if</span> (! d) { - <span class="hljs-keyword">throw</span> <span class="hljs-string">"Couldn't parse ISO 8601 date string '"</span> + s + <span class="hljs-string">"'"</span>; - }</pre></div></div> - - </li> - - - <li id="section-147"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-147">¶</a> - </div> - <p>parse strings, leading zeros into proper ints</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> a = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">10</span>,<span class="hljs-number">11</span>]; - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i <span class="hljs-keyword">in</span> a) { - d[a[i]] = <span class="hljs-built_in">parseInt</span>(d[a[i]], <span class="hljs-number">10</span>); - } - d[<span class="hljs-number">7</span>] = <span class="hljs-built_in">parseFloat</span>(d[<span class="hljs-number">7</span>]);</pre></div></div> - - </li> - - - <li id="section-148"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-148">¶</a> - </div> - <p>Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]]) -note that month is 0-11, not 1-12 -see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/UTC">https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/UTC</a></p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> ms = <span class="hljs-built_in">Date</span>.UTC(d[<span class="hljs-number">1</span>], d[<span class="hljs-number">2</span>] - <span class="hljs-number">1</span>, d[<span class="hljs-number">3</span>], d[<span class="hljs-number">4</span>], d[<span class="hljs-number">5</span>], d[<span class="hljs-number">6</span>]);</pre></div></div> - - </li> - - - <li id="section-149"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-149">¶</a> - </div> - <p>if there are milliseconds, add them</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (d[<span class="hljs-number">7</span>] > <span class="hljs-number">0</span>) { - ms += <span class="hljs-built_in">Math</span>.round(d[<span class="hljs-number">7</span>] * <span class="hljs-number">1000</span>); - }</pre></div></div> - - </li> - - - <li id="section-150"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-150">¶</a> - </div> - <p>if there’s a timezone, calculate it</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (d[<span class="hljs-number">8</span>] != <span class="hljs-string">"Z"</span> && d[<span class="hljs-number">10</span>]) { - <span class="hljs-keyword">var</span> offset = d[<span class="hljs-number">10</span>] * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>; - <span class="hljs-keyword">if</span> (d[<span class="hljs-number">11</span>]) { - offset += d[<span class="hljs-number">11</span>] * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">if</span> (d[<span class="hljs-number">9</span>] == <span class="hljs-string">"-"</span>) { - ms -= offset; - } - <span class="hljs-keyword">else</span> { - ms += offset; - } - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(ms); - }; - - Twig.lib.strtotime = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">text, now</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-151"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-151">¶</a> - </div> - <p> discuss at: <a href="http://phpjs.org/functions/strtotime/">http://phpjs.org/functions/strtotime/</a> - version: 1109.2016 -original by: Caio Ariede (<a href="http://caioariede.com">http://caioariede.com</a>) -improved by: Kevin van Zonneveld (<a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a>) -improved by: Caio Ariede (<a href="http://caioariede.com">http://caioariede.com</a>) -improved by: A. Matías Quezada (<a href="http://amatiasq.com">http://amatiasq.com</a>) -improved by: preuter -improved by: Brett Zamir (<a href="http://brett-zamir.me">http://brett-zamir.me</a>) -improved by: Mirko Faber - input by: David -bugfixed by: Wagner B. Soares -bugfixed by: Artur Tchernychev -bugfixed by: Stephan Bösch-Plepelits (<a href="http://github.com/plepe">http://github.com/plepe</a>) - note: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones) - example 1: strtotime(‘+1 day’, 1129633200); - returns 1: 1129719600 - example 2: strtotime(‘+1 week 2 days 4 hours 2 seconds’, 1129633200); - returns 2: 1130425202 - example 3: strtotime(‘last month’, 1129633200); - returns 3: 1127041200 - example 4: strtotime(‘2009-05-04 08:30:00 GMT’); - returns 4: 1241425800 - example 5: strtotime(‘2009-05-04 08:30:00+00’); - returns 5: 1241425800 - example 6: strtotime(‘2009-05-04 08:30:00+02:00’); - returns 6: 1241418600 - example 7: strtotime(‘2009-05-04T08:30:00Z’); - returns 7: 1241425800</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">var</span> parsed, match, today, year, date, days, ranges, len, times, regex, i, fail = <span class="hljs-literal">false</span>; - - <span class="hljs-keyword">if</span> (!text) { - <span class="hljs-keyword">return</span> fail; - }</pre></div></div> - - </li> - - - <li id="section-152"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-152">¶</a> - </div> - <p>Unecessary spaces</p> - - </div> - - <div class="content"><div class='highlight'><pre> text = text.replace(<span class="hljs-regexp">/^\s+|\s+$/g</span>, <span class="hljs-string">''</span>) - .replace(<span class="hljs-regexp">/\s{2,}/g</span>, <span class="hljs-string">' '</span>) - .replace(<span class="hljs-regexp">/[\t\r\n]/g</span>, <span class="hljs-string">''</span>) - .toLowerCase();</pre></div></div> - - </li> - - - <li id="section-153"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-153">¶</a> - </div> - <p>in contrast to php, js Date.parse function interprets: -dates given as yyyy-mm-dd as in timezone: UTC, -dates with “.” or “-“ as MDY instead of DMY -dates with two-digit years differently -etc…etc… -…therefore we manually parse lots of common date formats</p> - - </div> - - <div class="content"><div class='highlight'><pre> match = text.match( - <span class="hljs-regexp">/^(\d{1,4})([\-\.\/\:])(\d{1,2})([\-\.\/\:])(\d{1,4})(?:\s(\d{1,2}):(\d{2})?:?(\d{2})?)?(?:\s([A-Z]+)?)?$/</span>); - - <span class="hljs-keyword">if</span> (match && match[<span class="hljs-number">2</span>] === match[<span class="hljs-number">4</span>]) { - <span class="hljs-keyword">if</span> (match[<span class="hljs-number">1</span>] > <span class="hljs-number">1901</span>) { - <span class="hljs-keyword">switch</span> (match[<span class="hljs-number">2</span>]) { - <span class="hljs-keyword">case</span> <span class="hljs-string">'-'</span>: - {</pre></div></div> - - </li> - - - <li id="section-154"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-154">¶</a> - </div> - <p>YYYY-M-D</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">3</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">5</span>] > <span class="hljs-number">31</span>) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(match[<span class="hljs-number">1</span>], <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">3</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">5</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">'.'</span>: - {</pre></div></div> - - </li> - - - <li id="section-155"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-155">¶</a> - </div> - <p>YYYY.M.D is not parsed by strtotime()</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> fail; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">'/'</span>: - {</pre></div></div> - - </li> - - - <li id="section-156"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-156">¶</a> - </div> - <p>YYYY/M/D</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">3</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">5</span>] > <span class="hljs-number">31</span>) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(match[<span class="hljs-number">1</span>], <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">3</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">5</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - } - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">5</span>] > <span class="hljs-number">1901</span>) { - <span class="hljs-keyword">switch</span> (match[<span class="hljs-number">2</span>]) { - <span class="hljs-keyword">case</span> <span class="hljs-string">'-'</span>: - {</pre></div></div> - - </li> - - - <li id="section-157"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-157">¶</a> - </div> - <p>D-M-YYYY</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">3</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">1</span>] > <span class="hljs-number">31</span>) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(match[<span class="hljs-number">5</span>], <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">3</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">1</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">'.'</span>: - {</pre></div></div> - - </li> - - - <li id="section-158"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-158">¶</a> - </div> - <p>D.M.YYYY</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">3</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">1</span>] > <span class="hljs-number">31</span>) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(match[<span class="hljs-number">5</span>], <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">3</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">1</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">'/'</span>: - {</pre></div></div> - - </li> - - - <li id="section-159"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-159">¶</a> - </div> - <p>M/D/YYYY</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">1</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">3</span>] > <span class="hljs-number">31</span>) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(match[<span class="hljs-number">5</span>], <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">1</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">3</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - } - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">switch</span> (match[<span class="hljs-number">2</span>]) { - <span class="hljs-keyword">case</span> <span class="hljs-string">'-'</span>: - {</pre></div></div> - - </li> - - - <li id="section-160"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-160">¶</a> - </div> - <p>YY-M-D</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">3</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">5</span>] > <span class="hljs-number">31</span> || (match[<span class="hljs-number">1</span>] < <span class="hljs-number">70</span> && match[<span class="hljs-number">1</span>] > <span class="hljs-number">38</span>)) { - <span class="hljs-keyword">return</span> fail; - } - - year = match[<span class="hljs-number">1</span>] >= <span class="hljs-number">0</span> && match[<span class="hljs-number">1</span>] <= <span class="hljs-number">38</span> ? +match[<span class="hljs-number">1</span>] + <span class="hljs-number">2000</span> : match[<span class="hljs-number">1</span>]; - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(year, <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">3</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">5</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">'.'</span>: - {</pre></div></div> - - </li> - - - <li id="section-161"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-161">¶</a> - </div> - <p>D.M.YY or H.MM.SS</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">5</span>] >= <span class="hljs-number">70</span>) {</pre></div></div> - - </li> - - - <li id="section-162"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-162">¶</a> - </div> - <p>D.M.YY</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">3</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">1</span>] > <span class="hljs-number">31</span>) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(match[<span class="hljs-number">5</span>], <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">3</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">1</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">if</span> (match[<span class="hljs-number">5</span>] < <span class="hljs-number">60</span> && !match[<span class="hljs-number">6</span>]) {</pre></div></div> - - </li> - - - <li id="section-163"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-163">¶</a> - </div> - <p>H.MM.SS</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">1</span>] > <span class="hljs-number">23</span> || match[<span class="hljs-number">3</span>] > <span class="hljs-number">59</span>) { - <span class="hljs-keyword">return</span> fail; - } - - today = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(); - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(today.getFullYear(), today.getMonth(), today.getDate(), - match[<span class="hljs-number">1</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">3</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">5</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - }</pre></div></div> - - </li> - - - <li id="section-164"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-164">¶</a> - </div> - <p>invalid format, cannot be parsed</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> fail; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">'/'</span>: - {</pre></div></div> - - </li> - - - <li id="section-165"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-165">¶</a> - </div> - <p>M/D/YY</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">1</span>] > <span class="hljs-number">12</span> || match[<span class="hljs-number">3</span>] > <span class="hljs-number">31</span> || (match[<span class="hljs-number">5</span>] < <span class="hljs-number">70</span> && match[<span class="hljs-number">5</span>] > <span class="hljs-number">38</span>)) { - <span class="hljs-keyword">return</span> fail; - } - - year = match[<span class="hljs-number">5</span>] >= <span class="hljs-number">0</span> && match[<span class="hljs-number">5</span>] <= <span class="hljs-number">38</span> ? +match[<span class="hljs-number">5</span>] + <span class="hljs-number">2000</span> : match[<span class="hljs-number">5</span>]; - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(year, <span class="hljs-built_in">parseInt</span>(match[<span class="hljs-number">1</span>], <span class="hljs-number">10</span>) - <span class="hljs-number">1</span>, match[<span class="hljs-number">3</span>], - match[<span class="hljs-number">6</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">7</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">8</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">9</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - <span class="hljs-keyword">case</span> <span class="hljs-string">':'</span>: - {</pre></div></div> - - </li> - - - <li id="section-166"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-166">¶</a> - </div> - <p>HH:MM:SS</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">1</span>] > <span class="hljs-number">23</span> || match[<span class="hljs-number">3</span>] > <span class="hljs-number">59</span> || match[<span class="hljs-number">5</span>] > <span class="hljs-number">59</span>) { - <span class="hljs-keyword">return</span> fail; - } - - today = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(); - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(today.getFullYear(), today.getMonth(), today.getDate(), - match[<span class="hljs-number">1</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">3</span>] || <span class="hljs-number">0</span>, match[<span class="hljs-number">5</span>] || <span class="hljs-number">0</span>) / <span class="hljs-number">1000</span>; - } - } - } - }</pre></div></div> - - </li> - - - <li id="section-167"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-167">¶</a> - </div> - <p>other formats and “now” should be parsed by Date.parse()</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (text === <span class="hljs-string">'now'</span>) { - <span class="hljs-keyword">return</span> now === <span class="hljs-literal">null</span> || <span class="hljs-built_in">isNaN</span>(now) ? <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() - .getTime() / <span class="hljs-number">1000</span> | <span class="hljs-number">0</span> : now | <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(parsed = <span class="hljs-built_in">Date</span>.parse(text))) { - <span class="hljs-keyword">return</span> parsed / <span class="hljs-number">1000</span> | <span class="hljs-number">0</span>; - }</pre></div></div> - - </li> - - - <li id="section-168"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-168">¶</a> - </div> - <p>Browsers != Chrome have problems parsing ISO 8601 date strings, as they do -not accept lower case characters, space, or shortened time zones. -Therefore, fix these problems and try again. -Examples: - 2015-04-15 20:33:59+02 - 2015-04-15 20:33:59z - 2015-04-15t20:33:59+02:00</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match = text.match(<span class="hljs-regexp">/^([0-9]{4}-[0-9]{2}-[0-9]{2})[ t]([0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)([\+-][0-9]{2}(:[0-9]{2})?|z)/</span>)) {</pre></div></div> - - </li> - - - <li id="section-169"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-169">¶</a> - </div> - <p>fix time zone information</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">4</span>] == <span class="hljs-string">'z'</span>) { - match[<span class="hljs-number">4</span>] = <span class="hljs-string">'Z'</span>; - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (match[<span class="hljs-number">4</span>].match(<span class="hljs-regexp">/^([\+-][0-9]{2})$/</span>)) { - match[<span class="hljs-number">4</span>] = match[<span class="hljs-number">4</span>] + <span class="hljs-string">':00'</span>; - } - - <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(parsed = <span class="hljs-built_in">Date</span>.parse(match[<span class="hljs-number">1</span>] + <span class="hljs-string">'T'</span> + match[<span class="hljs-number">2</span>] + match[<span class="hljs-number">4</span>]))) { - <span class="hljs-keyword">return</span> parsed / <span class="hljs-number">1000</span> | <span class="hljs-number">0</span>; - } - } - - date = now ? <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(now * <span class="hljs-number">1000</span>) : <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(); - days = { - <span class="hljs-string">'sun'</span>: <span class="hljs-number">0</span>, - <span class="hljs-string">'mon'</span>: <span class="hljs-number">1</span>, - <span class="hljs-string">'tue'</span>: <span class="hljs-number">2</span>, - <span class="hljs-string">'wed'</span>: <span class="hljs-number">3</span>, - <span class="hljs-string">'thu'</span>: <span class="hljs-number">4</span>, - <span class="hljs-string">'fri'</span>: <span class="hljs-number">5</span>, - <span class="hljs-string">'sat'</span>: <span class="hljs-number">6</span> - }; - ranges = { - <span class="hljs-string">'yea'</span>: <span class="hljs-string">'FullYear'</span>, - <span class="hljs-string">'mon'</span>: <span class="hljs-string">'Month'</span>, - <span class="hljs-string">'day'</span>: <span class="hljs-string">'Date'</span>, - <span class="hljs-string">'hou'</span>: <span class="hljs-string">'Hours'</span>, - <span class="hljs-string">'min'</span>: <span class="hljs-string">'Minutes'</span>, - <span class="hljs-string">'sec'</span>: <span class="hljs-string">'Seconds'</span> - }; - - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">lastNext</span>(<span class="hljs-params">type, range, modifier</span>) </span>{ - <span class="hljs-keyword">var</span> diff, day = days[range]; - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> day !== <span class="hljs-string">'undefined'</span>) { - diff = day - date.getDay(); - - <span class="hljs-keyword">if</span> (diff === <span class="hljs-number">0</span>) { - diff = <span class="hljs-number">7</span> * modifier; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (diff > <span class="hljs-number">0</span> && type === <span class="hljs-string">'last'</span>) { - diff -= <span class="hljs-number">7</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (diff < <span class="hljs-number">0</span> && type === <span class="hljs-string">'next'</span>) { - diff += <span class="hljs-number">7</span>; - } - - date.setDate(date.getDate() + diff); - } - } - - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">process</span>(<span class="hljs-params">val</span>) </span>{ - <span class="hljs-keyword">var</span> splt = val.split(<span class="hljs-string">' '</span>), <span class="hljs-comment">// Todo: Reconcile this with regex using \s, taking into account browser issues with split and regexes</span> - type = splt[<span class="hljs-number">0</span>], - range = splt[<span class="hljs-number">1</span>].substring(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>), - typeIsNumber = <span class="hljs-regexp">/\d+/</span>.test(type), - ago = splt[<span class="hljs-number">2</span>] === <span class="hljs-string">'ago'</span>, - num = (type === <span class="hljs-string">'last'</span> ? <span class="hljs-number">-1</span> : <span class="hljs-number">1</span>) * (ago ? <span class="hljs-number">-1</span> : <span class="hljs-number">1</span>); - - <span class="hljs-keyword">if</span> (typeIsNumber) { - num *= <span class="hljs-built_in">parseInt</span>(type, <span class="hljs-number">10</span>); - } - - <span class="hljs-keyword">if</span> (ranges.hasOwnProperty(range) && !splt[<span class="hljs-number">1</span>].match(<span class="hljs-regexp">/^mon(day|\.)?$/i</span>)) { - <span class="hljs-keyword">return</span> date[<span class="hljs-string">'set'</span> + ranges[range]](date[<span class="hljs-string">'get'</span> + ranges[range]]() + num); - } - - <span class="hljs-keyword">if</span> (range === <span class="hljs-string">'wee'</span>) { - <span class="hljs-keyword">return</span> date.setDate(date.getDate() + (num * <span class="hljs-number">7</span>)); - } - - <span class="hljs-keyword">if</span> (type === <span class="hljs-string">'next'</span> || type === <span class="hljs-string">'last'</span>) { - lastNext(type, range, num); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!typeIsNumber) { - <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; - } - - <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; - } - - times = <span class="hljs-string">'(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec'</span> + - <span class="hljs-string">'|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?'</span> + - <span class="hljs-string">'|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'</span>; - regex = <span class="hljs-string">'([+-]?\\d+\\s'</span> + times + <span class="hljs-string">'|'</span> + <span class="hljs-string">'(last|next)\\s'</span> + times + <span class="hljs-string">')(\\sago)?'</span>; - - match = text.match(<span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(regex, <span class="hljs-string">'gi'</span>)); - <span class="hljs-keyword">if</span> (!match) { - <span class="hljs-keyword">return</span> fail; - } - - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>, len = match.length; i < len; i++) { - <span class="hljs-keyword">if</span> (!process(match[i])) { - <span class="hljs-keyword">return</span> fail; - } - }</pre></div></div> - - </li> - - - <li id="section-170"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-170">¶</a> - </div> - <p>ECMAScript 5 only -if (!match.every(process)) - return false;</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">return</span> (date.getTime() / <span class="hljs-number">1000</span>); - }; - - Twig.lib.is = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">type, obj</span>) </span>{ - <span class="hljs-keyword">var</span> clas = <span class="hljs-built_in">Object</span>.prototype.toString.call(obj).slice(<span class="hljs-number">8</span>, <span class="hljs-number">-1</span>); - <span class="hljs-keyword">return</span> obj !== <span class="hljs-literal">undefined</span> && obj !== <span class="hljs-literal">null</span> && clas === type; - };</pre></div></div> - - </li> - - - <li id="section-171"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-171">¶</a> - </div> - <p>shallow-copy an object</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.lib.copy = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">src</span>) </span>{ - <span class="hljs-keyword">var</span> target = {}, - key; - <span class="hljs-keyword">for</span> (key <span class="hljs-keyword">in</span> src) - target[key] = src[key]; - - <span class="hljs-keyword">return</span> target; - }; - - Twig.lib.replaceAll = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">string, search, replace</span>) </span>{ - <span class="hljs-keyword">return</span> string.split(search).join(replace); - };</pre></div></div> - - </li> - - - <li id="section-172"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-172">¶</a> - </div> - <p>chunk an array (arr) into arrays of (size) items, returns an array of arrays, or an empty array on invalid input</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.lib.chunkArray = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">arr, size</span>) </span>{ - <span class="hljs-keyword">var</span> returnVal = [], - x = <span class="hljs-number">0</span>, - len = arr.length; - - <span class="hljs-keyword">if</span> (size < <span class="hljs-number">1</span> || !Twig.lib.is(<span class="hljs-string">"Array"</span>, arr)) { - <span class="hljs-keyword">return</span> []; - } - - <span class="hljs-keyword">while</span> (x < len) { - returnVal.push(arr.slice(x, x += size)); - } - - <span class="hljs-keyword">return</span> returnVal; - }; - - Twig.lib.round = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">round</span>(<span class="hljs-params">value, precision, mode</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-173"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-173">¶</a> - </div> - <p> discuss at: <a href="http://phpjs.org/functions/round/">http://phpjs.org/functions/round/</a> -original by: Philip Peterson - revised by: Onno Marsman - revised by: T.Wild - revised by: Rafał Kukawski (<a href="http://blog.kukawski.pl/">http://blog.kukawski.pl/</a>) - input by: Greenseed - input by: meo - input by: William - input by: Josep Sanz (<a href="http://www.ws3.es/">http://www.ws3.es/</a>) -bugfixed by: Brett Zamir (<a href="http://brett-zamir.me">http://brett-zamir.me</a>) - 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</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">var</span> m, f, isHalf, sgn; <span class="hljs-comment">// helper variables</span> - precision |= <span class="hljs-number">0</span>; <span class="hljs-comment">// making sure precision is integer</span> - m = <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">10</span>, precision); - value *= m; - sgn = (value > <span class="hljs-number">0</span>) | -(value < <span class="hljs-number">0</span>); <span class="hljs-comment">// sign of the number</span> - isHalf = value % <span class="hljs-number">1</span> === <span class="hljs-number">0.5</span> * sgn; - f = <span class="hljs-built_in">Math</span>.floor(value); - - <span class="hljs-keyword">if</span> (isHalf) { - <span class="hljs-keyword">switch</span> (mode) { - <span class="hljs-keyword">case</span> <span class="hljs-string">'PHP_ROUND_HALF_DOWN'</span>: - value = f + (sgn < <span class="hljs-number">0</span>); <span class="hljs-comment">// rounds .5 toward zero</span> - <span class="hljs-keyword">break</span>; - <span class="hljs-keyword">case</span> <span class="hljs-string">'PHP_ROUND_HALF_EVEN'</span>: - value = f + (f % <span class="hljs-number">2</span> * sgn); <span class="hljs-comment">// rouds .5 towards the next even integer</span> - <span class="hljs-keyword">break</span>; - <span class="hljs-keyword">case</span> <span class="hljs-string">'PHP_ROUND_HALF_ODD'</span>: - value = f + !(f % <span class="hljs-number">2</span>); <span class="hljs-comment">// rounds .5 towards the next odd integer</span> - <span class="hljs-keyword">break</span>; - <span class="hljs-keyword">default</span>: - value = f + (sgn > <span class="hljs-number">0</span>); <span class="hljs-comment">// rounds .5 away from zero</span> - } - } - - <span class="hljs-keyword">return</span> (isHalf ? value : <span class="hljs-built_in">Math</span>.round(value)) / m; - }; - - Twig.lib.max = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">max</span>(<span class="hljs-params"></span>) </span>{</pre></div></div> - - </li> - - - <li id="section-174"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-174">¶</a> - </div> - <p> discuss at: <a href="http://phpjs.org/functions/max/">http://phpjs.org/functions/max/</a> -original by: Onno Marsman - revised by: Onno Marsman -improved by: Jack - note: Long code cause we’re aiming for maximum PHP compatibility - example 1: max(1, 3, 5, 6, 7); - returns 1: 7 - example 2: max([2, 4, 5]); - returns 2: 5 - example 3: max(0, ‘hello’); - returns 3: 0 - example 4: max(‘hello’, 0); - returns 4: ‘hello’ - example 5: max(-1, ‘hello’); - returns 5: ‘hello’ - example 6: max([2, 4, 8], [2, 5, 7]); - returns 6: [2, 5, 7]</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">var</span> ar, retVal, i = <span class="hljs-number">0</span>, - n = <span class="hljs-number">0</span>, - argv = <span class="hljs-built_in">arguments</span>, - argc = argv.length, - _obj2Array = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">obj</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Object</span>.prototype.toString.call(obj) === <span class="hljs-string">'[object Array]'</span>) { - <span class="hljs-keyword">return</span> obj; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">var</span> ar = []; - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i <span class="hljs-keyword">in</span> obj) { - <span class="hljs-keyword">if</span> (obj.hasOwnProperty(i)) { - ar.push(obj[i]); - } - } - <span class="hljs-keyword">return</span> ar; - } - }, <span class="hljs-comment">//function _obj2Array</span> - _compare = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">current, next</span>) </span>{ - <span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, - n = <span class="hljs-number">0</span>, - tmp = <span class="hljs-number">0</span>, - nl = <span class="hljs-number">0</span>, - cl = <span class="hljs-number">0</span>; - - <span class="hljs-keyword">if</span> (current === next) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> current === <span class="hljs-string">'object'</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> next === <span class="hljs-string">'object'</span>) { - current = _obj2Array(current); - next = _obj2Array(next); - cl = current.length; - nl = next.length; - <span class="hljs-keyword">if</span> (nl > cl) { - <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (nl < cl) { - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>, n = cl; i < n; ++i) { - tmp = _compare(current[i], next[i]); - <span class="hljs-keyword">if</span> (tmp == <span class="hljs-number">1</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (tmp == <span class="hljs-number">-1</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - } - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> next === <span class="hljs-string">'object'</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(next) && !<span class="hljs-built_in">isNaN</span>(current)) { - <span class="hljs-keyword">if</span> (current == <span class="hljs-number">0</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> (current < <span class="hljs-number">0</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">-1</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(current) && !<span class="hljs-built_in">isNaN</span>(next)) { - <span class="hljs-keyword">if</span> (next == <span class="hljs-number">0</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> (next > <span class="hljs-number">0</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">-1</span>); - } - - <span class="hljs-keyword">if</span> (next == current) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> (next > current ? <span class="hljs-number">1</span> : <span class="hljs-number">-1</span>); - }; <span class="hljs-comment">//function _compare</span> - <span class="hljs-keyword">if</span> (argc === <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'At least one value should be passed to max()'</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (argc === <span class="hljs-number">1</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> argv[<span class="hljs-number">0</span>] === <span class="hljs-string">'object'</span>) { - ar = _obj2Array(argv[<span class="hljs-number">0</span>]); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Wrong parameter count for max()'</span>); - } - <span class="hljs-keyword">if</span> (ar.length === <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Array must contain at least one element for max()'</span>); - } - } <span class="hljs-keyword">else</span> { - ar = argv; - } - - retVal = ar[<span class="hljs-number">0</span>]; - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>, n = ar.length; i < n; ++i) { - <span class="hljs-keyword">if</span> (_compare(retVal, ar[i]) == <span class="hljs-number">1</span>) { - retVal = ar[i]; - } - } - - <span class="hljs-keyword">return</span> retVal; - }; - - Twig.lib.min = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">min</span>(<span class="hljs-params"></span>) </span>{</pre></div></div> - - </li> - - - <li id="section-175"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-175">¶</a> - </div> - <p> discuss at: <a href="http://phpjs.org/functions/min/">http://phpjs.org/functions/min/</a> -original by: Onno Marsman - revised by: Onno Marsman -improved by: Jack - note: Long code cause we’re aiming for maximum PHP compatibility - example 1: min(1, 3, 5, 6, 7); - returns 1: 1 - example 2: min([2, 4, 5]); - returns 2: 2 - example 3: min(0, ‘hello’); - returns 3: 0 - example 4: min(‘hello’, 0); - returns 4: ‘hello’ - example 5: min(-1, ‘hello’); - returns 5: -1 - example 6: min([2, 4, 8], [2, 5, 7]); - returns 6: [2, 4, 8]</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">var</span> ar, retVal, i = <span class="hljs-number">0</span>, - n = <span class="hljs-number">0</span>, - argv = <span class="hljs-built_in">arguments</span>, - argc = argv.length, - _obj2Array = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">obj</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Object</span>.prototype.toString.call(obj) === <span class="hljs-string">'[object Array]'</span>) { - <span class="hljs-keyword">return</span> obj; - } - <span class="hljs-keyword">var</span> ar = []; - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i <span class="hljs-keyword">in</span> obj) { - <span class="hljs-keyword">if</span> (obj.hasOwnProperty(i)) { - ar.push(obj[i]); - } - } - <span class="hljs-keyword">return</span> ar; - }, <span class="hljs-comment">//function _obj2Array</span> - _compare = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">current, next</span>) </span>{ - <span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, - n = <span class="hljs-number">0</span>, - tmp = <span class="hljs-number">0</span>, - nl = <span class="hljs-number">0</span>, - cl = <span class="hljs-number">0</span>; - - <span class="hljs-keyword">if</span> (current === next) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> current === <span class="hljs-string">'object'</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> next === <span class="hljs-string">'object'</span>) { - current = _obj2Array(current); - next = _obj2Array(next); - cl = current.length; - nl = next.length; - <span class="hljs-keyword">if</span> (nl > cl) { - <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (nl < cl) { - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>, n = cl; i < n; ++i) { - tmp = _compare(current[i], next[i]); - <span class="hljs-keyword">if</span> (tmp == <span class="hljs-number">1</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (tmp == <span class="hljs-number">-1</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } - } - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> next === <span class="hljs-string">'object'</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(next) && !<span class="hljs-built_in">isNaN</span>(current)) { - <span class="hljs-keyword">if</span> (current == <span class="hljs-number">0</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> (current < <span class="hljs-number">0</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">-1</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(current) && !<span class="hljs-built_in">isNaN</span>(next)) { - <span class="hljs-keyword">if</span> (next == <span class="hljs-number">0</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> (next > <span class="hljs-number">0</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">-1</span>); - } - - <span class="hljs-keyword">if</span> (next == current) { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">return</span> (next > current ? <span class="hljs-number">1</span> : <span class="hljs-number">-1</span>); - }; <span class="hljs-comment">//function _compare</span> - - <span class="hljs-keyword">if</span> (argc === <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'At least one value should be passed to min()'</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (argc === <span class="hljs-number">1</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> argv[<span class="hljs-number">0</span>] === <span class="hljs-string">'object'</span>) { - ar = _obj2Array(argv[<span class="hljs-number">0</span>]); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Wrong parameter count for min()'</span>); - } - - <span class="hljs-keyword">if</span> (ar.length === <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Array must contain at least one element for min()'</span>); - } - } <span class="hljs-keyword">else</span> { - ar = argv; - } - - retVal = ar[<span class="hljs-number">0</span>]; - - <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>, n = ar.length; i < n; ++i) { - <span class="hljs-keyword">if</span> (_compare(retVal, ar[i]) == <span class="hljs-number">-1</span>) { - retVal = ar[i]; - } - } - - <span class="hljs-keyword">return</span> retVal; - }; - - <span class="hljs-keyword">return</span> Twig; - -})(Twig || { });</pre></div></div> - - </li> - - - <li id="section-176"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-176">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-177"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-177">¶</a> - </div> - <h2 id="twig-logic-js">twig.logic.js</h2> -<p>This file handles tokenizing, compiling and parsing logic tokens. {% … %}</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>; - - <span class="hljs-comment">/** - * Namespace for logic handling. - */</span> - Twig.logic = {}; - - <span class="hljs-comment">/** - * Logic token types. - */</span> - Twig.logic.type = { - if_: <span class="hljs-string">'Twig.logic.type.if'</span>, - endif: <span class="hljs-string">'Twig.logic.type.endif'</span>, - for_: <span class="hljs-string">'Twig.logic.type.for'</span>, - endfor: <span class="hljs-string">'Twig.logic.type.endfor'</span>, - else_: <span class="hljs-string">'Twig.logic.type.else'</span>, - elseif: <span class="hljs-string">'Twig.logic.type.elseif'</span>, - set: <span class="hljs-string">'Twig.logic.type.set'</span>, - setcapture:<span class="hljs-string">'Twig.logic.type.setcapture'</span>, - endset: <span class="hljs-string">'Twig.logic.type.endset'</span>, - filter: <span class="hljs-string">'Twig.logic.type.filter'</span>, - endfilter: <span class="hljs-string">'Twig.logic.type.endfilter'</span>, - shortblock: <span class="hljs-string">'Twig.logic.type.shortblock'</span>, - block: <span class="hljs-string">'Twig.logic.type.block'</span>, - endblock: <span class="hljs-string">'Twig.logic.type.endblock'</span>, - extends_: <span class="hljs-string">'Twig.logic.type.extends'</span>, - use: <span class="hljs-string">'Twig.logic.type.use'</span>, - include: <span class="hljs-string">'Twig.logic.type.include'</span>, - spaceless: <span class="hljs-string">'Twig.logic.type.spaceless'</span>, - endspaceless: <span class="hljs-string">'Twig.logic.type.endspaceless'</span>, - macro: <span class="hljs-string">'Twig.logic.type.macro'</span>, - endmacro: <span class="hljs-string">'Twig.logic.type.endmacro'</span>, - import_: <span class="hljs-string">'Twig.logic.type.import'</span>, - <span class="hljs-keyword">from</span>: <span class="hljs-string">'Twig.logic.type.from'</span>, - embed: <span class="hljs-string">'Twig.logic.type.embed'</span>, - endembed: <span class="hljs-string">'Twig.logic.type.endembed'</span> - };</pre></div></div> - - </li> - - - <li id="section-178"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-178">¶</a> - </div> - <p>Regular expressions for handling logic tokens.</p> -<p>Properties:</p> -<pre><code> type: The type of expression this matches - - regex: A regular expression that matches the format of the token - - next: What logic tokens (if any) pop this token off the logic stack. If empty, the - 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. -</code></pre><p> Functions:</p> -<pre><code> compile: A <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">that</span> <span class="hljs-title">handles</span> <span class="hljs-title">compiling</span> <span class="hljs-title">the</span> <span class="hljs-title">token</span> <span class="hljs-title">into</span> <span class="hljs-title">an</span> <span class="hljs-title">output</span> <span class="hljs-title">token</span> <span class="hljs-title">ready</span> <span class="hljs-title">for</span> - <span class="hljs-title">parsing</span> <span class="hljs-title">with</span> <span class="hljs-title">the</span> <span class="hljs-title">parse</span> <span class="hljs-title">function</span>. - - <span class="hljs-title">parse</span>: <span class="hljs-title">A</span> <span class="hljs-title">function</span> <span class="hljs-title">that</span> <span class="hljs-title">parses</span> <span class="hljs-title">the</span> <span class="hljs-title">compiled</span> <span class="hljs-title">token</span> <span class="hljs-title">into</span> <span class="hljs-title">output</span> (<span class="hljs-params">HTML / whatever the - template represents</span>).</span> -</code></pre> - </div> - - <div class="content"><div class='highlight'><pre> Twig.logic.definitions = [ - { - <span class="hljs-comment">/** - * If type logic tokens. - * - * Format: {% if expression %} - */</span> - type: Twig.logic.type.if_, - regex: <span class="hljs-regexp">/^if\s+([\s\S]+)$/</span>, - next: [ - Twig.logic.type.else_, - Twig.logic.type.elseif, - Twig.logic.type.endif - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = token.match[<span class="hljs-number">1</span>];</pre></div></div> - - </li> - - - <li id="section-179"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-179">¶</a> - </div> - <p>Compile the expression.</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> output = <span class="hljs-string">''</span>,</pre></div></div> - - </li> - - - <li id="section-180"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-180">¶</a> - </div> - <p>Parse the expression</p> - - </div> - - <div class="content"><div class='highlight'><pre> result = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]);</pre></div></div> - - </li> - - - <li id="section-181"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-181">¶</a> - </div> - <p>Start a new logic chain</p> - - </div> - - <div class="content"><div class='highlight'><pre> chain = <span class="hljs-literal">true</span>; - - <span class="hljs-keyword">if</span> (result) { - chain = <span class="hljs-literal">false</span>;</pre></div></div> - - </li> - - - <li id="section-182"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-182">¶</a> - </div> - <p>parse if output</p> - - </div> - - <div class="content"><div class='highlight'><pre> output = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]); - } - <span class="hljs-keyword">return</span> { - chain: chain, - output: output - }; - } - }, - { - <span class="hljs-comment">/** - * Else if type logic tokens. - * - * Format: {% elseif expression %} - */</span> - type: Twig.logic.type.elseif, - regex: <span class="hljs-regexp">/^elseif\s+([^\s].*)$/</span>, - next: [ - Twig.logic.type.else_, - Twig.logic.type.elseif, - Twig.logic.type.endif - ], - open: <span class="hljs-literal">false</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = token.match[<span class="hljs-number">1</span>];</pre></div></div> - - </li> - - - <li id="section-183"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-183">¶</a> - </div> - <p>Compile the expression.</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> output = <span class="hljs-string">''</span>; - - <span class="hljs-keyword">if</span> (chain && Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]) === <span class="hljs-literal">true</span>) { - chain = <span class="hljs-literal">false</span>;</pre></div></div> - - </li> - - - <li id="section-184"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-184">¶</a> - </div> - <p>parse if output</p> - - </div> - - <div class="content"><div class='highlight'><pre> output = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]); - } - - <span class="hljs-keyword">return</span> { - chain: chain, - output: output - }; - } - }, - { - <span class="hljs-comment">/** - * Else if type logic tokens. - * - * Format: {% elseif expression %} - */</span> - type: Twig.logic.type.else_, - regex: <span class="hljs-regexp">/^else$/</span>, - next: [ - Twig.logic.type.endif, - Twig.logic.type.endfor - ], - open: <span class="hljs-literal">false</span>, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> output = <span class="hljs-string">''</span>; - <span class="hljs-keyword">if</span> (chain) { - output = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]); - } - <span class="hljs-keyword">return</span> { - chain: chain, - output: output - }; - } - }, - { - <span class="hljs-comment">/** - * End if type logic tokens. - * - * Format: {% endif %} - */</span> - type: Twig.logic.type.endif, - regex: <span class="hljs-regexp">/^endif$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/** - * For type logic tokens. - * - * Format: {% for expression %} - */</span> - type: Twig.logic.type.for_, - regex: <span class="hljs-regexp">/^for\s+([a-zA-Z0-9_,\s]+)\s+in\s+([^\s].*?)(?:\s+if\s+([^\s].*))?$/</span>, - next: [ - Twig.logic.type.else_, - Twig.logic.type.endfor - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> key_value = token.match[<span class="hljs-number">1</span>], - expression = token.match[<span class="hljs-number">2</span>], - conditional = token.match[<span class="hljs-number">3</span>], - kv_split = <span class="hljs-literal">null</span>; - - token.key_var = <span class="hljs-literal">null</span>; - token.value_var = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">if</span> (key_value.indexOf(<span class="hljs-string">","</span>) >= <span class="hljs-number">0</span>) { - kv_split = key_value.split(<span class="hljs-string">','</span>); - <span class="hljs-keyword">if</span> (kv_split.length === <span class="hljs-number">2</span>) { - token.key_var = kv_split[<span class="hljs-number">0</span>].trim(); - token.value_var = kv_split[<span class="hljs-number">1</span>].trim(); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Invalid expression in for loop: "</span> + key_value); - } - } <span class="hljs-keyword">else</span> { - token.value_var = key_value; - }</pre></div></div> - - </li> - - - <li id="section-185"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-185">¶</a> - </div> - <p>Valid expressions for a for loop - for item in expression - for key,item in expression</p> - - </div> - - </li> - - - <li id="section-186"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-186">¶</a> - </div> - <p>Compile the expression.</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.expression = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack;</pre></div></div> - - </li> - - - <li id="section-187"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-187">¶</a> - </div> - <p>Compile the conditional (if available)</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (conditional) { - token.conditional = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: conditional - }]).stack; - } - - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, continue_chain</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-188"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-188">¶</a> - </div> - <p>Parse expression</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> result = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.expression, context]), - output = [], - len, - index = <span class="hljs-number">0</span>, - keyset, - that = <span class="hljs-keyword">this</span>, - conditional = token.conditional, - buildLoop = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">index, len</span>) </span>{ - <span class="hljs-keyword">var</span> isConditional = conditional !== <span class="hljs-literal">undefined</span>; - <span class="hljs-keyword">return</span> { - index: index+<span class="hljs-number">1</span>, - index0: index, - revindex: isConditional?<span class="hljs-literal">undefined</span>:len-index, - revindex0: isConditional?<span class="hljs-literal">undefined</span>:len-index<span class="hljs-number">-1</span>, - first: (index === <span class="hljs-number">0</span>), - last: isConditional?<span class="hljs-literal">undefined</span>:(index === len<span class="hljs-number">-1</span>), - length: isConditional?<span class="hljs-literal">undefined</span>:len, - parent: context - }; - },</pre></div></div> - - </li> - - - <li id="section-189"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-189">¶</a> - </div> - <p>run once for each iteration of the loop</p> - - </div> - - <div class="content"><div class='highlight'><pre> loop = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key, value</span>) </span>{ - <span class="hljs-keyword">var</span> inner_context = Twig.ChildContext(context); - - inner_context[token.value_var] = value; - - <span class="hljs-keyword">if</span> (token.key_var) { - inner_context[token.key_var] = key; - }</pre></div></div> - - </li> - - - <li id="section-190"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-190">¶</a> - </div> - <p>Loop object</p> - - </div> - - <div class="content"><div class='highlight'><pre> inner_context.loop = buildLoop(index, len); - - <span class="hljs-keyword">if</span> (conditional === <span class="hljs-literal">undefined</span> || - Twig.expression.parse.apply(that, [conditional, inner_context])) - { - output.push(Twig.parse.apply(that, [token.output, inner_context])); - index += <span class="hljs-number">1</span>; - }</pre></div></div> - - </li> - - - <li id="section-191"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-191">¶</a> - </div> - <p>Delete loop-related variables from the context</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">delete</span> inner_context[<span class="hljs-string">'loop'</span>]; - <span class="hljs-keyword">delete</span> inner_context[token.value_var]; - <span class="hljs-keyword">delete</span> inner_context[token.key_var];</pre></div></div> - - </li> - - - <li id="section-192"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-192">¶</a> - </div> - <p>Merge in values that exist in context but have changed -in inner_context.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.merge(context, inner_context, <span class="hljs-literal">true</span>); - }; - - - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">'Array'</span>, result)) { - len = result.length; - Twig.forEach(result, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">var</span> key = index; - - loop(key, value); - }); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">'Object'</span>, result)) { - <span class="hljs-keyword">if</span> (result._keys !== <span class="hljs-literal">undefined</span>) { - keyset = result._keys; - } <span class="hljs-keyword">else</span> { - keyset = <span class="hljs-built_in">Object</span>.keys(result); - } - len = keyset.length; - Twig.forEach(keyset, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-193"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-193">¶</a> - </div> - <p>Ignore the _keys property, it’s internal to twig.js</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (key === <span class="hljs-string">"_keys"</span>) <span class="hljs-keyword">return</span>; - - loop(key, result[key]); - }); - }</pre></div></div> - - </li> - - - <li id="section-194"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-194">¶</a> - </div> - <p>Only allow else statements if no output was generated</p> - - </div> - - <div class="content"><div class='highlight'><pre> continue_chain = (output.length === <span class="hljs-number">0</span>); - - <span class="hljs-keyword">return</span> { - chain: continue_chain, - output: Twig.output.apply(<span class="hljs-keyword">this</span>, [output]) - }; - } - }, - { - <span class="hljs-comment">/** - * End if type logic tokens. - * - * Format: {% endif %} - */</span> - type: Twig.logic.type.endfor, - regex: <span class="hljs-regexp">/^endfor$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/** - * Set type logic tokens. - * - * Format: {% set key = expression %} - */</span> - type: Twig.logic.type.set, - regex: <span class="hljs-regexp">/^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*([\s\S]+)$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> key = token.match[<span class="hljs-number">1</span>].trim(), - expression = token.match[<span class="hljs-number">2</span>],</pre></div></div> - - </li> - - - <li id="section-195"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-195">¶</a> - </div> - <p>Compile the expression.</p> - - </div> - - <div class="content"><div class='highlight'><pre> expression_stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - token.key = key; - token.expression = expression_stack; - - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, continue_chain</span>) </span>{ - <span class="hljs-keyword">var</span> value = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.expression, context]), - key = token.key; - - context[key] = value; - - <span class="hljs-keyword">return</span> { - chain: continue_chain, - context: context - }; - } - }, - { - <span class="hljs-comment">/** - * Set capture type logic tokens. - * - * Format: {% set key %} - */</span> - type: Twig.logic.type.setcapture, - regex: <span class="hljs-regexp">/^set\s+([a-zA-Z0-9_,\s]+)$/</span>, - next: [ - Twig.logic.type.endset - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> key = token.match[<span class="hljs-number">1</span>].trim(); - - token.key = key; - - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, continue_chain</span>) </span>{ - - <span class="hljs-keyword">var</span> value = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]), - key = token.key;</pre></div></div> - - </li> - - - <li id="section-196"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-196">¶</a> - </div> - <p>set on both the global and local context</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">this</span>.context[key] = value; - context[key] = value; - - <span class="hljs-keyword">return</span> { - chain: continue_chain, - context: context - }; - } - }, - { - <span class="hljs-comment">/** - * End set type block logic tokens. - * - * Format: {% endset %} - */</span> - type: Twig.logic.type.endset, - regex: <span class="hljs-regexp">/^endset$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/** - * Filter logic tokens. - * - * Format: {% filter upper %} or {% filter lower|escape %} - */</span> - type: Twig.logic.type.filter, - regex: <span class="hljs-regexp">/^filter\s+(.+)$/</span>, - next: [ - Twig.logic.type.endfilter - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = <span class="hljs-string">"|"</span> + token.match[<span class="hljs-number">1</span>].trim();</pre></div></div> - - </li> - - - <li id="section-197"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-197">¶</a> - </div> - <p>Compile the expression.</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> unfiltered = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]), - stack = [{ - type: Twig.expression.type.string, - value: unfiltered - }].concat(token.stack); - - <span class="hljs-keyword">var</span> output = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [stack, context]); - - <span class="hljs-keyword">return</span> { - chain: chain, - output: output - }; - } - }, - { - <span class="hljs-comment">/** - * End filter logic tokens. - * - * Format: {% endfilter %} - */</span> - type: Twig.logic.type.endfilter, - regex: <span class="hljs-regexp">/^endfilter$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/** - * Block logic tokens. - * - * Format: {% block title %} - */</span> - type: Twig.logic.type.block, - regex: <span class="hljs-regexp">/^block\s+([a-zA-Z0-9_]+)$/</span>, - next: [ - Twig.logic.type.endblock - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - token.block = token.match[<span class="hljs-number">1</span>].trim(); - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> block_output, - output, - isImported = Twig.indexOf(<span class="hljs-keyword">this</span>.importedBlocks, token.block) > <span class="hljs-number">-1</span>, - hasParent = <span class="hljs-keyword">this</span>.blocks[token.block] && Twig.indexOf(<span class="hljs-keyword">this</span>.blocks[token.block], Twig.placeholders.parent) > <span class="hljs-number">-1</span>;</pre></div></div> - - </li> - - - <li id="section-198"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-198">¶</a> - </div> - <p>Don’t override previous blocks unless they’re imported with “use” -Loops should be exempted as well.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.blocks[token.block] === <span class="hljs-literal">undefined</span> || isImported || hasParent || context.loop || token.overwrite) { - <span class="hljs-keyword">if</span> (token.expression) {</pre></div></div> - - </li> - - - <li id="section-199"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-199">¶</a> - </div> - <p>Short blocks have output as an expression on the open tag (no body)</p> - - </div> - - <div class="content"><div class='highlight'><pre> block_output = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.string, - value: Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]) - }, context]); - } <span class="hljs-keyword">else</span> { - block_output = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.string, - value: Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]) - }, context]); - } - - <span class="hljs-keyword">if</span> (isImported) {</pre></div></div> - - </li> - - - <li id="section-200"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-200">¶</a> - </div> - <p>once the block is overridden, remove it from the list of imported blocks</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">this</span>.importedBlocks.splice(<span class="hljs-keyword">this</span>.importedBlocks.indexOf(token.block), <span class="hljs-number">1</span>); - } - - <span class="hljs-keyword">if</span> (hasParent) { - <span class="hljs-keyword">this</span>.blocks[token.block] = Twig.Markup(<span class="hljs-keyword">this</span>.blocks[token.block].replace(Twig.placeholders.parent, block_output)); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">this</span>.blocks[token.block] = block_output; - } - - <span class="hljs-keyword">this</span>.originalBlockTokens[token.block] = { - type: token.type, - block: token.block, - output: token.output, - overwrite: <span class="hljs-literal">true</span> - }; - }</pre></div></div> - - </li> - - - <li id="section-201"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-201">¶</a> - </div> - <p>Check if a child block has been set from a template extending this one.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.child.blocks[token.block]) { - output = <span class="hljs-keyword">this</span>.child.blocks[token.block]; - } <span class="hljs-keyword">else</span> { - output = <span class="hljs-keyword">this</span>.blocks[token.block]; - } - - <span class="hljs-keyword">return</span> { - chain: chain, - output: output - }; - } - }, - { - <span class="hljs-comment">/** - * Block shorthand logic tokens. - * - * Format: {% block title expression %} - */</span> - type: Twig.logic.type.shortblock, - regex: <span class="hljs-regexp">/^block\s+([a-zA-Z0-9_]+)\s+(.+)$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - token.expression = token.match[<span class="hljs-number">2</span>].trim(); - - token.output = Twig.expression.compile({ - type: Twig.expression.type.expression, - value: token.expression - }).stack; - - token.block = token.match[<span class="hljs-number">1</span>].trim(); - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">return</span> Twig.logic.handler[Twig.logic.type.block].parse.apply(<span class="hljs-keyword">this</span>, <span class="hljs-built_in">arguments</span>); - } - }, - { - <span class="hljs-comment">/** - * End block logic tokens. - * - * Format: {% endblock %} - */</span> - type: Twig.logic.type.endblock, - regex: <span class="hljs-regexp">/^endblock(?:\s+([a-zA-Z0-9_]+))?$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/** - * Block logic tokens. - * - * Format: {% extends "template.twig" %} - */</span> - type: Twig.logic.type.extends_, - regex: <span class="hljs-regexp">/^extends\s+(.+)$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = token.match[<span class="hljs-number">1</span>].trim(); - <span class="hljs-keyword">delete</span> token.match; - - token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-202"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-202">¶</a> - </div> - <p>Resolve filename</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> file = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]);</pre></div></div> - - </li> - - - <li id="section-203"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-203">¶</a> - </div> - <p>Set parent template</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">this</span>.extend = file; - - <span class="hljs-keyword">return</span> { - chain: chain, - output: <span class="hljs-string">''</span> - }; - } - }, - { - <span class="hljs-comment">/** - * Block logic tokens. - * - * Format: {% use "template.twig" %} - */</span> - type: Twig.logic.type.use, - regex: <span class="hljs-regexp">/^use\s+(.+)$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = token.match[<span class="hljs-number">1</span>].trim(); - <span class="hljs-keyword">delete</span> token.match; - - token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-204"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-204">¶</a> - </div> - <p>Resolve filename</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> file = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]);</pre></div></div> - - </li> - - - <li id="section-205"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-205">¶</a> - </div> - <p>Import blocks</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">this</span>.importBlocks(file); - - <span class="hljs-keyword">return</span> { - chain: chain, - output: <span class="hljs-string">''</span> - }; - } - }, - { - <span class="hljs-comment">/** - * Block logic tokens. - * - * Format: {% includes "template.twig" [with {some: 'values'} only] %} - */</span> - type: Twig.logic.type.include, - regex: <span class="hljs-regexp">/^include\s+(ignore missing\s+)?(.+?)\s*(?:with\s+([\S\s]+?))?\s*(only)?$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> match = token.match, - includeMissing = match[<span class="hljs-number">1</span>] !== <span class="hljs-literal">undefined</span>, - expression = match[<span class="hljs-number">2</span>].trim(), - withContext = match[<span class="hljs-number">3</span>], - only = ((match[<span class="hljs-number">4</span>] !== <span class="hljs-literal">undefined</span>) && match[<span class="hljs-number">4</span>].length); - - <span class="hljs-keyword">delete</span> token.match; - - token.only = only; - token.includeMissing = includeMissing; - - token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - <span class="hljs-keyword">if</span> (withContext !== <span class="hljs-literal">undefined</span>) { - token.withStack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: withContext.trim() - }]).stack; - } - - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-206"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-206">¶</a> - </div> - <p>Resolve filename</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> innerContext = {}, - withContext, - i, - template; - - <span class="hljs-keyword">if</span> (!token.only) { - innerContext = Twig.ChildContext(context); - } - - <span class="hljs-keyword">if</span> (token.withStack !== <span class="hljs-literal">undefined</span>) { - withContext = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.withStack, context]); - - <span class="hljs-keyword">for</span> (i <span class="hljs-keyword">in</span> withContext) { - <span class="hljs-keyword">if</span> (withContext.hasOwnProperty(i)) - innerContext[i] = withContext[i]; - } - } - - <span class="hljs-keyword">var</span> file = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, innerContext]); - - <span class="hljs-keyword">if</span> (file <span class="hljs-keyword">instanceof</span> Twig.Template) { - template = file; - } <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-207"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-207">¶</a> - </div> - <p>Import file</p> - - </div> - - <div class="content"><div class='highlight'><pre> template = <span class="hljs-keyword">this</span>.importFile(file); - } - - <span class="hljs-keyword">return</span> { - chain: chain, - output: template.render(innerContext) - }; - } - }, - { - type: Twig.logic.type.spaceless, - regex: <span class="hljs-regexp">/^spaceless$/</span>, - next: [ - Twig.logic.type.endspaceless - ], - open: <span class="hljs-literal">true</span>,</pre></div></div> - - </li> - - - <li id="section-208"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-208">¶</a> - </div> - <p>Parse the html and return it without any spaces between tags</p> - - </div> - - <div class="content"><div class='highlight'><pre> parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> <span class="hljs-comment">// Parse the output without any filter</span> - unfiltered = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, context]),</pre></div></div> - - </li> - - - <li id="section-209"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-209">¶</a> - </div> - <p>A regular expression to find closing and opening tags with spaces between them</p> - - </div> - - <div class="content"><div class='highlight'><pre> rBetweenTagSpaces = <span class="hljs-regexp">/>\s+</g</span>,</pre></div></div> - - </li> - - - <li id="section-210"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-210">¶</a> - </div> - <p>Replace all space between closing and opening html tags</p> - - </div> - - <div class="content"><div class='highlight'><pre> output = unfiltered.replace(rBetweenTagSpaces,<span class="hljs-string">'><'</span>).trim(); - - <span class="hljs-keyword">return</span> { - chain: chain, - output: output - }; - } - },</pre></div></div> - - </li> - - - <li id="section-211"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-211">¶</a> - </div> - <p>Add the {% endspaceless %} token</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.logic.type.endspaceless, - regex: <span class="hljs-regexp">/^endspaceless$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/** - * Macro logic tokens. - * - * Format: {% maro input(name, value, type, size) %} - * - */</span> - type: Twig.logic.type.macro, - regex: <span class="hljs-regexp">/^macro\s+([a-zA-Z0-9_]+)\s*\(\s*((?:[a-zA-Z0-9_]+(?:,\s*)?)*)\s*\)$/</span>, - next: [ - Twig.logic.type.endmacro - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> macroName = token.match[<span class="hljs-number">1</span>], - parameters = token.match[<span class="hljs-number">2</span>].split(<span class="hljs-regexp">/[\s,]+/</span>);</pre></div></div> - - </li> - - - <li id="section-212"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-212">¶</a> - </div> - <p>TODO: Clean up duplicate check</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>; i<parameters.length; i++) { - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> j=<span class="hljs-number">0</span>; j<parameters.length; j++){ - <span class="hljs-keyword">if</span> (parameters[i] === parameters[j] && i !== j) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Duplicate arguments for parameter: "</span>+ parameters[i]); - } - } - } - - token.macroName = macroName; - token.parameters = parameters; - - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> template = <span class="hljs-keyword">this</span>; - <span class="hljs-keyword">this</span>.macros[token.macroName] = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{</pre></div></div> - - </li> - - - <li id="section-213"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-213">¶</a> - </div> - <p>Pass global context and other macros</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> macroContext = { - _self: template.macros - }</pre></div></div> - - </li> - - - <li id="section-214"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-214">¶</a> - </div> - <p>Add parameters from context to macroContext</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>; i<token.parameters.length; i++) { - <span class="hljs-keyword">var</span> prop = token.parameters[i]; - <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">arguments</span>[i] !== <span class="hljs-string">'undefined'</span>) { - macroContext[prop] = <span class="hljs-built_in">arguments</span>[i]; - } <span class="hljs-keyword">else</span> { - macroContext[prop] = <span class="hljs-literal">undefined</span>; - } - }</pre></div></div> - - </li> - - - <li id="section-215"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-215">¶</a> - </div> - <p>Render</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> Twig.parse.apply(template, [token.output, macroContext]) - }; - - <span class="hljs-keyword">return</span> { - chain: chain, - output: <span class="hljs-string">''</span> - }; - - } - }, - { - <span class="hljs-comment">/** - * End macro logic tokens. - * - * Format: {% endmacro %} - */</span> - type: Twig.logic.type.endmacro, - regex: <span class="hljs-regexp">/^endmacro$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - }, - { - <span class="hljs-comment">/* - * import logic tokens. - * - * Format: {% import "template.twig" as form %} - */</span> - type: Twig.logic.type.import_, - regex: <span class="hljs-regexp">/^import\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = token.match[<span class="hljs-number">1</span>].trim(), - contextName = token.match[<span class="hljs-number">2</span>].trim(); - <span class="hljs-keyword">delete</span> token.match; - - token.expression = expression; - token.contextName = contextName; - - token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">if</span> (token.expression !== <span class="hljs-string">"_self"</span>) { - <span class="hljs-keyword">var</span> file = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]); - <span class="hljs-keyword">var</span> template = <span class="hljs-keyword">this</span>.importFile(file || token.expression); - context[token.contextName] = template.render({}, {output: <span class="hljs-string">'macros'</span>}); - } - <span class="hljs-keyword">else</span> { - context[token.contextName] = <span class="hljs-keyword">this</span>.macros; - } - - <span class="hljs-keyword">return</span> { - chain: chain, - output: <span class="hljs-string">''</span> - } - - } - }, - { - <span class="hljs-comment">/* - * from logic tokens. - * - * Format: {% from "template.twig" import func as form %} - */</span> - type: Twig.logic.type.from, - regex: <span class="hljs-regexp">/^from\s+(.+)\s+import\s+([a-zA-Z0-9_, ]+)$/</span>, - next: [ ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = token.match[<span class="hljs-number">1</span>].trim(), - macroExpressions = token.match[<span class="hljs-number">2</span>].trim().split(<span class="hljs-regexp">/[ ,]+/</span>), - macroNames = {}; - - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>; i<macroExpressions.length; i++) { - <span class="hljs-keyword">var</span> res = macroExpressions[i];</pre></div></div> - - </li> - - - <li id="section-216"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-216">¶</a> - </div> - <p>match function as variable</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> macroMatch = res.match(<span class="hljs-regexp">/^([a-zA-Z0-9_]+)\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/</span>); - <span class="hljs-keyword">if</span> (macroMatch) { - macroNames[macroMatch[<span class="hljs-number">1</span>].trim()] = macroMatch[<span class="hljs-number">2</span>].trim(); - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (res.match(<span class="hljs-regexp">/^([a-zA-Z0-9_]+)$/</span>)) { - macroNames[res] = res; - } - <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-217"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-217">¶</a> - </div> - <p>ignore import</p> - - </div> - - <div class="content"><div class='highlight'><pre> } - - } - - <span class="hljs-keyword">delete</span> token.match; - - token.expression = expression; - token.macroNames = macroNames; - - token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> macros; - - <span class="hljs-keyword">if</span> (token.expression !== <span class="hljs-string">"_self"</span>) { - <span class="hljs-keyword">var</span> file = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]); - <span class="hljs-keyword">var</span> template = <span class="hljs-keyword">this</span>.importFile(file || token.expression); - macros = template.render({}, {output: <span class="hljs-string">'macros'</span>}); - } - <span class="hljs-keyword">else</span> { - macros = <span class="hljs-keyword">this</span>.macros; - } - - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> macroName <span class="hljs-keyword">in</span> token.macroNames) { - <span class="hljs-keyword">if</span> (macros.hasOwnProperty(macroName)) { - context[token.macroNames[macroName]] = macros[macroName]; - } - } - - <span class="hljs-keyword">return</span> { - chain: chain, - output: <span class="hljs-string">''</span> - } - - } - }, - { - <span class="hljs-comment">/** - * The embed tag combines the behaviour of include and extends. - * It allows you to include another template's contents, just like include does. - * - * Format: {% embed "template.twig" [with {some: 'values'} only] %} - */</span> - type: Twig.logic.type.embed, - regex: <span class="hljs-regexp">/^embed\s+(ignore missing\s+)?(.+?)\s*(?:with\s+(.+?))?\s*(only)?$/</span>, - next: [ - Twig.logic.type.endembed - ], - open: <span class="hljs-literal">true</span>, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - <span class="hljs-keyword">var</span> match = token.match, - includeMissing = match[<span class="hljs-number">1</span>] !== <span class="hljs-literal">undefined</span>, - expression = match[<span class="hljs-number">2</span>].trim(), - withContext = match[<span class="hljs-number">3</span>], - only = ((match[<span class="hljs-number">4</span>] !== <span class="hljs-literal">undefined</span>) && match[<span class="hljs-number">4</span>].length); - - <span class="hljs-keyword">delete</span> token.match; - - token.only = only; - token.includeMissing = includeMissing; - - token.stack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - <span class="hljs-keyword">if</span> (withContext !== <span class="hljs-literal">undefined</span>) { - token.withStack = Twig.expression.compile.apply(<span class="hljs-keyword">this</span>, [{ - type: Twig.expression.type.expression, - value: withContext.trim() - }]).stack; - } - - <span class="hljs-keyword">return</span> token; - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-218"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-218">¶</a> - </div> - <p>Resolve filename</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> innerContext = {}, - withContext, - i, - template; - - <span class="hljs-keyword">if</span> (!token.only) { - <span class="hljs-keyword">for</span> (i <span class="hljs-keyword">in</span> context) { - <span class="hljs-keyword">if</span> (context.hasOwnProperty(i)) - innerContext[i] = context[i]; - } - } - - <span class="hljs-keyword">if</span> (token.withStack !== <span class="hljs-literal">undefined</span>) { - withContext = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.withStack, context]); - - <span class="hljs-keyword">for</span> (i <span class="hljs-keyword">in</span> withContext) { - <span class="hljs-keyword">if</span> (withContext.hasOwnProperty(i)) - innerContext[i] = withContext[i]; - } - } - - <span class="hljs-keyword">var</span> file = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, innerContext]); - - <span class="hljs-keyword">if</span> (file <span class="hljs-keyword">instanceof</span> Twig.Template) { - template = file; - } <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-219"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-219">¶</a> - </div> - <p>Import file</p> - - </div> - - <div class="content"><div class='highlight'><pre> template = <span class="hljs-keyword">this</span>.importFile(file); - }</pre></div></div> - - </li> - - - <li id="section-220"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-220">¶</a> - </div> - <p>reset previous blocks</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">this</span>.blocks = {};</pre></div></div> - - </li> - - - <li id="section-221"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-221">¶</a> - </div> - <p>parse tokens. output will be not used</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> output = Twig.parse.apply(<span class="hljs-keyword">this</span>, [token.output, innerContext]);</pre></div></div> - - </li> - - - <li id="section-222"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-222">¶</a> - </div> - <p>render tempalte with blocks defined in embed block</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> { - chain: chain, - output: template.render(innerContext, {<span class="hljs-string">'blocks'</span>:<span class="hljs-keyword">this</span>.blocks}) - }; - } - }, - <span class="hljs-comment">/* Add the {% endembed %} token - * - */</span> - { - type: Twig.logic.type.endembed, - regex: <span class="hljs-regexp">/^endembed$/</span>, - next: [ ], - open: <span class="hljs-literal">false</span> - } - - ]; - - - <span class="hljs-comment">/** - * Registry for logic handlers. - */</span> - Twig.logic.handler = {}; - - <span class="hljs-comment">/** - * Define a new token type, available at Twig.logic.type.{type} - */</span> - Twig.logic.extendType = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">type, value</span>) </span>{ - value = value || (<span class="hljs-string">"Twig.logic.type"</span> + type); - Twig.logic.type[type] = value; - }; - - <span class="hljs-comment">/** - * Extend the logic parsing functionality with a new token definition. - * - * // Define a new tag - * Twig.logic.extend({ - * type: Twig.logic.type.{type}, - * // The pattern to match for this token - * regex: ..., - * // What token types can follow this token, leave blank if any. - * next: [ ... ] - * // Create and return compiled version of the token - * compile: function(token) { ... } - * // Parse the compiled token with the context provided by the render call - * // and whether this token chain is complete. - * parse: function(token, context, chain) { ... } - * }); - * - * @param {Object} definition The new logic expression. - */</span> - Twig.logic.extend = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">definition</span>) </span>{ - - <span class="hljs-keyword">if</span> (!definition.type) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to extend logic definition. No type provided for "</span> + definition); - } <span class="hljs-keyword">else</span> { - Twig.logic.extendType(definition.type); - } - Twig.logic.handler[definition.type] = definition; - };</pre></div></div> - - </li> - - - <li id="section-223"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-223">¶</a> - </div> - <p>Extend with built-in expressions</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span> (Twig.logic.definitions.length > <span class="hljs-number">0</span>) { - Twig.logic.extend(Twig.logic.definitions.shift()); - } - - <span class="hljs-comment">/** - * 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. - */</span> - Twig.logic.compile = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">raw_token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = raw_token.value.trim(), - token = Twig.logic.tokenize.apply(<span class="hljs-keyword">this</span>, [expression]), - token_template = Twig.logic.handler[token.type];</pre></div></div> - - </li> - - - <li id="section-224"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-224">¶</a> - </div> - <p>Check if the token needs compiling</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_template.compile) { - token = token_template.compile.apply(<span class="hljs-keyword">this</span>, [token]); - Twig.log.trace(<span class="hljs-string">"Twig.logic.compile: "</span>, <span class="hljs-string">"Compiled logic token to "</span>, token); - } - - <span class="hljs-keyword">return</span> token; - }; - - <span class="hljs-comment">/** - * Tokenize logic expressions. This function matches token expressions against regular - * expressions provided in token definitions provided with Twig.logic.extend. - * - * @param {string} expression the logic token expression to tokenize - * (i.e. what's between {% and %}) - * - * @return {Object} The matched token with type set to the token type and match to the regex match. - */</span> - Twig.logic.tokenize = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">expression</span>) </span>{ - <span class="hljs-keyword">var</span> token = {}, - token_template_type = <span class="hljs-literal">null</span>, - token_type = <span class="hljs-literal">null</span>, - token_regex = <span class="hljs-literal">null</span>, - regex_array = <span class="hljs-literal">null</span>, - regex = <span class="hljs-literal">null</span>, - match = <span class="hljs-literal">null</span>;</pre></div></div> - - </li> - - - <li id="section-225"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-225">¶</a> - </div> - <p>Ignore whitespace around expressions.</p> - - </div> - - <div class="content"><div class='highlight'><pre> expression = expression.trim(); - - <span class="hljs-keyword">for</span> (token_template_type <span class="hljs-keyword">in</span> Twig.logic.handler) { - <span class="hljs-keyword">if</span> (Twig.logic.handler.hasOwnProperty(token_template_type)) {</pre></div></div> - - </li> - - - <li id="section-226"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-226">¶</a> - </div> - <p>Get the type and regex for this template type</p> - - </div> - - <div class="content"><div class='highlight'><pre> token_type = Twig.logic.handler[token_template_type].type; - token_regex = Twig.logic.handler[token_template_type].regex;</pre></div></div> - - </li> - - - <li id="section-227"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-227">¶</a> - </div> - <p>Handle multiple regular expressions per type.</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex_array = []; - <span class="hljs-keyword">if</span> (token_regex <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Array</span>) { - regex_array = token_regex; - } <span class="hljs-keyword">else</span> { - regex_array.push(token_regex); - }</pre></div></div> - - </li> - - - <li id="section-228"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-228">¶</a> - </div> - <p>Check regular expressions in the order they were specified in the definition.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span> (regex_array.length > <span class="hljs-number">0</span>) { - regex = regex_array.shift(); - match = regex.exec(expression.trim()); - <span class="hljs-keyword">if</span> (match !== <span class="hljs-literal">null</span>) { - token.type = token_type; - token.match = match; - Twig.log.trace(<span class="hljs-string">"Twig.logic.tokenize: "</span>, <span class="hljs-string">"Matched a "</span>, token_type, <span class="hljs-string">" regular expression of "</span>, match); - <span class="hljs-keyword">return</span> token; - } - } - } - }</pre></div></div> - - </li> - - - <li id="section-229"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-229">¶</a> - </div> - <p>No regex matches</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to parse '"</span> + expression.trim() + <span class="hljs-string">"'"</span>); - }; - - <span class="hljs-comment">/** - * Parse a logic token within a given context. - * - * What are logic chains? - * Logic chains represent a series of tokens that are connected, - * for example: - * {% if ... %} {% else %} {% endif %} - * - * The chain parameter is used to signify if a chain is open of closed. - * open: - * More tokens in this chain should be parsed. - * closed: - * This token chain has completed parsing and any additional - * tokens (else, elseif, etc...) should be ignored. - * - * @param {Object} token The compiled token. - * @param {Object} context The render context. - * @param {boolean} chain Is this an open logic chain. If false, that means a - * chain is closed and no further cases should be parsed. - */</span> - Twig.logic.parse = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token, context, chain</span>) </span>{ - <span class="hljs-keyword">var</span> output = <span class="hljs-string">''</span>, - token_template; - - context = context || { }; - - Twig.log.debug(<span class="hljs-string">"Twig.logic.parse: "</span>, <span class="hljs-string">"Parsing logic token "</span>, token); - - token_template = Twig.logic.handler[token.type]; - - <span class="hljs-keyword">if</span> (token_template.parse) { - output = token_template.parse.apply(<span class="hljs-keyword">this</span>, [token, context, chain]); - } - <span class="hljs-keyword">return</span> output; - }; - - <span class="hljs-keyword">return</span> Twig; - -})(Twig || { });</pre></div></div> - - </li> - - - <li id="section-230"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-230">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-231"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-231">¶</a> - </div> - <h2 id="twig-expression-js">twig.expression.js</h2> -<p>This file handles tokenizing, compiling and parsing expressions.</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>; - - <span class="hljs-comment">/** - * Namespace for expression handling. - */</span> - Twig.expression = { }; - - <span class="hljs-comment">/** - * Reserved word that can't be used as variable names. - */</span> - Twig.expression.reservedWords = [ - <span class="hljs-string">"true"</span>, <span class="hljs-string">"false"</span>, <span class="hljs-string">"null"</span>, <span class="hljs-string">"TRUE"</span>, <span class="hljs-string">"FALSE"</span>, <span class="hljs-string">"NULL"</span>, <span class="hljs-string">"_context"</span> - ]; - - <span class="hljs-comment">/** - * The type of tokens used in expressions. - */</span> - Twig.expression.type = { - comma: <span class="hljs-string">'Twig.expression.type.comma'</span>, - operator: { - unary: <span class="hljs-string">'Twig.expression.type.operator.unary'</span>, - binary: <span class="hljs-string">'Twig.expression.type.operator.binary'</span> - }, - string: <span class="hljs-string">'Twig.expression.type.string'</span>, - bool: <span class="hljs-string">'Twig.expression.type.bool'</span>, - array: { - start: <span class="hljs-string">'Twig.expression.type.array.start'</span>, - end: <span class="hljs-string">'Twig.expression.type.array.end'</span> - }, - object: { - start: <span class="hljs-string">'Twig.expression.type.object.start'</span>, - end: <span class="hljs-string">'Twig.expression.type.object.end'</span> - }, - parameter: { - start: <span class="hljs-string">'Twig.expression.type.parameter.start'</span>, - end: <span class="hljs-string">'Twig.expression.type.parameter.end'</span> - }, - key: { - period: <span class="hljs-string">'Twig.expression.type.key.period'</span>, - brackets: <span class="hljs-string">'Twig.expression.type.key.brackets'</span> - }, - filter: <span class="hljs-string">'Twig.expression.type.filter'</span>, - _function: <span class="hljs-string">'Twig.expression.type._function'</span>, - variable: <span class="hljs-string">'Twig.expression.type.variable'</span>, - number: <span class="hljs-string">'Twig.expression.type.number'</span>, - _null: <span class="hljs-string">'Twig.expression.type.null'</span>, - context: <span class="hljs-string">'Twig.expression.type.context'</span>, - test: <span class="hljs-string">'Twig.expression.type.test'</span> - }; - - Twig.expression.set = {</pre></div></div> - - </li> - - - <li id="section-232"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-232">¶</a> - </div> - <p>What can follow an expression (in general)</p> - - </div> - - <div class="content"><div class='highlight'><pre> 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 - ] - };</pre></div></div> - - </li> - - - <li id="section-233"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-233">¶</a> - </div> - <p>Most expressions allow a ‘.’ or ‘[‘ after them, so we provide a convenience set</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.expression.set.operations_extended = Twig.expression.set.operations.concat([ - Twig.expression.type.key.period, - Twig.expression.type.key.brackets]);</pre></div></div> - - </li> - - - <li id="section-234"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-234">¶</a> - </div> - <p>Some commonly used compile and parse functions.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.expression.fn = { - compile: { - push: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - output.push(token); - }, - push_both: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - output.push(token); - stack.push(token); - } - }, - parse: { - push: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - stack.push(token); - }, - push_value: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - stack.push(token.value); - } - } - };</pre></div></div> - - </li> - - - <li id="section-235"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-235">¶</a> - </div> - <p>The regular expressions and compile/parse logic used to match tokens in expressions.</p> -<p>Properties:</p> -<pre><code> type: The type <span class="hljs-keyword">of</span> expression <span class="hljs-keyword">this</span> matches - - regex: One or more regular expressions that matche the format <span class="hljs-keyword">of</span> the token. - - next: Valid tokens that can occur next <span class="hljs-keyword">in</span> the expression. -</code></pre><p>Functions:</p> -<pre><code> compile: A <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">that</span> <span class="hljs-title">compiles</span> <span class="hljs-title">the</span> <span class="hljs-title">raw</span> <span class="hljs-title">regular</span> <span class="hljs-title">expression</span> <span class="hljs-title">match</span> <span class="hljs-title">into</span> <span class="hljs-title">a</span> <span class="hljs-title">token</span>. - - <span class="hljs-title">parse</span>: <span class="hljs-title">A</span> <span class="hljs-title">function</span> <span class="hljs-title">that</span> <span class="hljs-title">parses</span> <span class="hljs-title">the</span> <span class="hljs-title">compiled</span> <span class="hljs-title">token</span> <span class="hljs-title">into</span> <span class="hljs-title">output</span>.</span> -</code></pre> - </div> - - <div class="content"><div class='highlight'><pre> Twig.expression.definitions = [ - { - type: Twig.expression.type.test, - regex: <span class="hljs-regexp">/^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*)/</span>, - next: Twig.expression.set.operations.concat([Twig.expression.type.parameter.start]), - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - token.filter = token.match[<span class="hljs-number">2</span>]; - token.modifier = token.match[<span class="hljs-number">1</span>]; - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">delete</span> token.value; - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> value = stack.pop(), - params = token.params && Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]), - result = Twig.test(token.filter, value, params); - - <span class="hljs-keyword">if</span> (token.modifier == <span class="hljs-string">'not'</span>) { - stack.push(!result); - } <span class="hljs-keyword">else</span> { - stack.push(result); - } - } - }, - { - type: Twig.expression.type.comma,</pre></div></div> - - </li> - - - <li id="section-236"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-236">¶</a> - </div> - <p>Match a comma</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^,/</span>, - next: Twig.expression.set.expressions.concat([Twig.expression.type.array.end, Twig.expression.type.object.end]), - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> i = stack.length - <span class="hljs-number">1</span>, - stack_token; - - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">delete</span> token.value;</pre></div></div> - - </li> - - - <li id="section-237"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-237">¶</a> - </div> - <p>pop tokens off the stack until the start of the object</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span>(;i >= <span class="hljs-number">0</span>; i--) { - stack_token = stack.pop(); - <span class="hljs-keyword">if</span> (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); - <span class="hljs-keyword">break</span>; - } - output.push(stack_token); - } - output.push(token); - } - }, - { - type: Twig.expression.type.operator.binary,</pre></div></div> - - </li> - - - <li id="section-238"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-238">¶</a> - </div> - <p>Match any of +, <em>, /, -, %, ~, <, <=, >, >=, !=, ==, *</em>, ?, :, and, or, not</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/(^[\+\-~%\?\:]|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^and\s+|^or\s+|^in\s+|^not in\s+|^\.\.)/</span>, - next: Twig.expression.set.expressions.concat([Twig.expression.type.operator.unary]), - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">delete</span> token.match; - - token.value = token.value.trim(); - <span class="hljs-keyword">var</span> value = token.value, - operator = Twig.expression.operator.lookup(value, token); - - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Operator: "</span>, operator, <span class="hljs-string">" from "</span>, value); - - <span class="hljs-keyword">while</span> (stack.length > <span class="hljs-number">0</span> && - (stack[stack.length<span class="hljs-number">-1</span>].type == Twig.expression.type.operator.unary || stack[stack.length<span class="hljs-number">-1</span>].type == Twig.expression.type.operator.binary) && - ( - (operator.associativity === Twig.expression.operator.leftToRight && - operator.precidence >= stack[stack.length<span class="hljs-number">-1</span>].precidence) || - - (operator.associativity === Twig.expression.operator.rightToLeft && - operator.precidence > stack[stack.length<span class="hljs-number">-1</span>].precidence) - ) - ) { - <span class="hljs-keyword">var</span> temp = stack.pop(); - output.push(temp); - } - - <span class="hljs-keyword">if</span> (value === <span class="hljs-string">":"</span>) {</pre></div></div> - - </li> - - - <li id="section-239"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-239">¶</a> - </div> - <p>Check if this is a ternary or object key being set</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (stack[stack.length - <span class="hljs-number">1</span>] && stack[stack.length<span class="hljs-number">-1</span>].value === <span class="hljs-string">"?"</span>) {</pre></div></div> - - </li> - - - <li id="section-240"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-240">¶</a> - </div> - <p>Continue as normal for a ternary</p> - - </div> - - <div class="content"><div class='highlight'><pre> } <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-241"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-241">¶</a> - </div> - <p>This is not a ternary so we push the token to the output where it can be handled - when the assocated object is closed.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> key_token = output.pop(); - - <span class="hljs-keyword">if</span> (key_token.type === Twig.expression.type.string || - key_token.type === Twig.expression.type.variable) { - token.key = key_token.value; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (key_token.type === Twig.expression.type.number) {</pre></div></div> - - </li> - - - <li id="section-242"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-242">¶</a> - </div> - <p>Convert integer keys into string keys</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.key = key_token.value.toString(); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (key_token.type === Twig.expression.type.parameter.end && - key_token.expression) { - token.params = key_token.params; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unexpected value before ':' of "</span> + key_token.type + <span class="hljs-string">" = "</span> + key_token.value); - } - - output.push(token); - <span class="hljs-keyword">return</span>; - } - } <span class="hljs-keyword">else</span> { - stack.push(operator); - } - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">if</span> (token.key) {</pre></div></div> - - </li> - - - <li id="section-243"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-243">¶</a> - </div> - <p>handle ternary ‘:’ operator</p> - - </div> - - <div class="content"><div class='highlight'><pre> stack.push(token); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (token.params) {</pre></div></div> - - </li> - - - <li id="section-244"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-244">¶</a> - </div> - <p>handle “{(expression):value}”</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.key = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]); - stack.push(token); - <span class="hljs-keyword">delete</span>(token.params); - } <span class="hljs-keyword">else</span> { - Twig.expression.operator.parse(token.value, stack); - } - } - }, - { - type: Twig.expression.type.operator.unary,</pre></div></div> - - </li> - - - <li id="section-245"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-245">¶</a> - </div> - <p>Match any of not</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/(^not\s+)/</span>, - next: Twig.expression.set.expressions, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">delete</span> token.match; - - token.value = token.value.trim(); - <span class="hljs-keyword">var</span> value = token.value, - operator = Twig.expression.operator.lookup(value, token); - - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Operator: "</span>, operator, <span class="hljs-string">" from "</span>, value); - - <span class="hljs-keyword">while</span> (stack.length > <span class="hljs-number">0</span> && - (stack[stack.length<span class="hljs-number">-1</span>].type == Twig.expression.type.operator.unary || stack[stack.length<span class="hljs-number">-1</span>].type == Twig.expression.type.operator.binary) && - ( - (operator.associativity === Twig.expression.operator.leftToRight && - operator.precidence >= stack[stack.length<span class="hljs-number">-1</span>].precidence) || - - (operator.associativity === Twig.expression.operator.rightToLeft && - operator.precidence > stack[stack.length<span class="hljs-number">-1</span>].precidence) - ) - ) { - <span class="hljs-keyword">var</span> temp = stack.pop(); - output.push(temp); - } - - stack.push(operator); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - Twig.expression.operator.parse(token.value, stack); - } - }, - { - <span class="hljs-comment">/** - * Match a string. This is anything between a pair of single or double quotes. - */</span> - type: Twig.expression.type.string,</pre></div></div> - - </li> - - - <li id="section-246"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-246">¶</a> - </div> - <p>See: <a href="http://blog.stevenlevithan.com/archives/match-quoted-string">http://blog.stevenlevithan.com/archives/match-quoted-string</a></p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^(["'])(?:(?=(\\?))\2[\s\S])*?\1/</span>, - next: Twig.expression.set.operations, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> value = token.value; - <span class="hljs-keyword">delete</span> token.match</pre></div></div> - - </li> - - - <li id="section-247"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-247">¶</a> - </div> - <p>Remove the quotes from the string</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (value.substring(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>) === <span class="hljs-string">'"'</span>) { - value = value.replace(<span class="hljs-string">'\\"'</span>, <span class="hljs-string">'"'</span>); - } <span class="hljs-keyword">else</span> { - value = value.replace(<span class="hljs-string">"\\'"</span>, <span class="hljs-string">"'"</span>); - } - token.value = value.substring(<span class="hljs-number">1</span>, value.length<span class="hljs-number">-1</span>).replace( <span class="hljs-regexp">/\\n/g</span>, <span class="hljs-string">"\n"</span> ).replace( <span class="hljs-regexp">/\\r/g</span>, <span class="hljs-string">"\r"</span> ); - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"String value: "</span>, token.value); - output.push(token); - }, - parse: Twig.expression.fn.parse.push_value - }, - { - <span class="hljs-comment">/** - * Match a parameter set start. - */</span> - type: Twig.expression.type.parameter.start, - regex: <span class="hljs-regexp">/^\(/</span>, - next: Twig.expression.set.expressions.concat([Twig.expression.type.parameter.end]), - compile: Twig.expression.fn.compile.push_both, - parse: Twig.expression.fn.parse.push - }, - { - <span class="hljs-comment">/** - * Match a parameter set end. - */</span> - type: Twig.expression.type.parameter.end, - regex: <span class="hljs-regexp">/^\)/</span>, - next: Twig.expression.set.operations_extended, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> stack_token, - end_token = token; - - stack_token = stack.pop(); - <span class="hljs-keyword">while</span>(stack.length > <span class="hljs-number">0</span> && stack_token.type != Twig.expression.type.parameter.start) { - output.push(stack_token); - stack_token = stack.pop(); - }</pre></div></div> - - </li> - - - <li id="section-248"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-248">¶</a> - </div> - <p>Move contents of parens into preceding filter</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> param_stack = []; - <span class="hljs-keyword">while</span>(token.type !== Twig.expression.type.parameter.start) {</pre></div></div> - - </li> - - - <li id="section-249"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-249">¶</a> - </div> - <p>Add token to arguments stack</p> - - </div> - - <div class="content"><div class='highlight'><pre> param_stack.unshift(token); - token = output.pop(); - } - param_stack.unshift(token); - - <span class="hljs-keyword">var</span> is_expression = <span class="hljs-literal">false</span>;</pre></div></div> - - </li> - - - <li id="section-250"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-250">¶</a> - </div> - <p>Get the token preceding the parameters</p> - - </div> - - <div class="content"><div class='highlight'><pre> token = output[output.length<span class="hljs-number">-1</span>]; - - <span class="hljs-keyword">if</span> (token === <span class="hljs-literal">undefined</span> || - (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 = <span class="hljs-literal">true</span>;</pre></div></div> - - </li> - - - <li id="section-251"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-251">¶</a> - </div> - <p>remove start and end token from stack</p> - - </div> - - <div class="content"><div class='highlight'><pre> param_stack.pop(); - param_stack.shift(); - - end_token.params = param_stack; - - output.push(end_token); - - } <span class="hljs-keyword">else</span> { - end_token.expression = <span class="hljs-literal">false</span>; - token.params = param_stack; - } - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> new_array = [], - array_ended = <span class="hljs-literal">false</span>, - value = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">if</span> (token.expression) { - value = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]) - stack.push(value); - - } <span class="hljs-keyword">else</span> { - - <span class="hljs-keyword">while</span> (stack.length > <span class="hljs-number">0</span>) { - value = stack.pop();</pre></div></div> - - </li> - - - <li id="section-252"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-252">¶</a> - </div> - <p>Push values into the array until the start of the array</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (value && value.type && value.type == Twig.expression.type.parameter.start) { - array_ended = <span class="hljs-literal">true</span>; - <span class="hljs-keyword">break</span>; - } - new_array.unshift(value); - } - - <span class="hljs-keyword">if</span> (!array_ended) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Expected end of parameter set."</span>); - } - - stack.push(new_array); - } - } - }, - { - <span class="hljs-comment">/** - * Match an array start. - */</span> - type: Twig.expression.type.array.start, - regex: <span class="hljs-regexp">/^\[/</span>, - next: Twig.expression.set.expressions.concat([Twig.expression.type.array.end]), - compile: Twig.expression.fn.compile.push_both, - parse: Twig.expression.fn.parse.push - }, - { - <span class="hljs-comment">/** - * Match an array end. - */</span> - type: Twig.expression.type.array.end, - regex: <span class="hljs-regexp">/^\]/</span>, - next: Twig.expression.set.operations_extended, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> i = stack.length - <span class="hljs-number">1</span>, - stack_token;</pre></div></div> - - </li> - - - <li id="section-253"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-253">¶</a> - </div> - <p>pop tokens off the stack until the start of the object</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span>(;i >= <span class="hljs-number">0</span>; i--) { - stack_token = stack.pop(); - <span class="hljs-keyword">if</span> (stack_token.type === Twig.expression.type.array.start) { - <span class="hljs-keyword">break</span>; - } - output.push(stack_token); - } - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> new_array = [], - array_ended = <span class="hljs-literal">false</span>, - value = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">while</span> (stack.length > <span class="hljs-number">0</span>) { - value = stack.pop();</pre></div></div> - - </li> - - - <li id="section-254"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-254">¶</a> - </div> - <p>Push values into the array until the start of the array</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (value.type && value.type == Twig.expression.type.array.start) { - array_ended = <span class="hljs-literal">true</span>; - <span class="hljs-keyword">break</span>; - } - new_array.unshift(value); - } - <span class="hljs-keyword">if</span> (!array_ended) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Expected end of array."</span>); - } - - stack.push(new_array); - } - },</pre></div></div> - - </li> - - - <li id="section-255"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-255">¶</a> - </div> - <p>Token that represents the start of a hash map ‘}’</p> -<p>Hash maps take the form: - { “key”: ‘value’, “another_key”: item }</p> -<p>Keys must be quoted (either single or double) and values can be any expression.</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.expression.type.object.start, - regex: <span class="hljs-regexp">/^\{/</span>, - next: Twig.expression.set.expressions.concat([Twig.expression.type.object.end]), - compile: Twig.expression.fn.compile.push_both, - parse: Twig.expression.fn.parse.push - },</pre></div></div> - - </li> - - - <li id="section-256"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-256">¶</a> - </div> - <p>Token that represents the end of a Hash Map ‘}’</p> -<p>This is where the logic for building the internal -representation of a hash map is defined.</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.expression.type.object.end, - regex: <span class="hljs-regexp">/^\}/</span>, - next: Twig.expression.set.operations_extended, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> i = stack.length<span class="hljs-number">-1</span>, - stack_token;</pre></div></div> - - </li> - - - <li id="section-257"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-257">¶</a> - </div> - <p>pop tokens off the stack until the start of the object</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span>(;i >= <span class="hljs-number">0</span>; i--) { - stack_token = stack.pop(); - <span class="hljs-keyword">if</span> (stack_token && stack_token.type === Twig.expression.type.object.start) { - <span class="hljs-keyword">break</span>; - } - output.push(stack_token); - } - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">end_token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> new_object = {}, - object_ended = <span class="hljs-literal">false</span>, - token = <span class="hljs-literal">null</span>, - token_key = <span class="hljs-literal">null</span>, - has_value = <span class="hljs-literal">false</span>, - value = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">while</span> (stack.length > <span class="hljs-number">0</span>) { - token = stack.pop();</pre></div></div> - - </li> - - - <li id="section-258"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-258">¶</a> - </div> - <p>Push values into the array until the start of the object</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token && token.type && token.type === Twig.expression.type.object.start) { - object_ended = <span class="hljs-literal">true</span>; - <span class="hljs-keyword">break</span>; - } - <span class="hljs-keyword">if</span> (token && token.type && (token.type === Twig.expression.type.operator.binary || token.type === Twig.expression.type.operator.unary) && token.key) { - <span class="hljs-keyword">if</span> (!has_value) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Missing value for key '"</span> + token.key + <span class="hljs-string">"' in object definition."</span>); - } - new_object[token.key] = value;</pre></div></div> - - </li> - - - <li id="section-259"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-259">¶</a> - </div> - <p>Preserve the order that elements are added to the map -This is necessary since JavaScript objects don’t -guarantee the order of keys</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (new_object._keys === <span class="hljs-literal">undefined</span>) new_object._keys = []; - new_object._keys.unshift(token.key);</pre></div></div> - - </li> - - - <li id="section-260"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-260">¶</a> - </div> - <p>reset value check</p> - - </div> - - <div class="content"><div class='highlight'><pre> value = <span class="hljs-literal">null</span>; - has_value = <span class="hljs-literal">false</span>; - - } <span class="hljs-keyword">else</span> { - has_value = <span class="hljs-literal">true</span>; - value = token; - } - } - <span class="hljs-keyword">if</span> (!object_ended) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unexpected end of object."</span>); - } - - stack.push(new_object); - } - },</pre></div></div> - - </li> - - - <li id="section-261"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-261">¶</a> - </div> - <p>Token representing a filter</p> -<p>Filters can follow any expression and take the form: - expression|filter(optional, args)</p> -<p>Filter parsing is done in the Twig.filters namespace.</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.expression.type.filter,</pre></div></div> - - </li> - - - <li id="section-262"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-262">¶</a> - </div> - <p>match a | then a letter or <em>, then any number of letters, numbers, </em> or -</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/</span>, - next: Twig.expression.set.operations_extended.concat([ - Twig.expression.type.parameter.start]), - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - token.value = token.match[<span class="hljs-number">1</span>]; - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> input = stack.pop(), - params = token.params && Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]); - - stack.push(Twig.filter.apply(<span class="hljs-keyword">this</span>, [token.value, input, params])); - } - }, - { - type: Twig.expression.type._function,</pre></div></div> - - </li> - - - <li id="section-263"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-263">¶</a> - </div> - <p>match any letter or <em>, then any number of letters, numbers, </em> or - followed by (</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/</span>, - next: Twig.expression.type.parameter.start, - transform: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">match, tokens</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-string">'('</span>; - }, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> fn = token.match[<span class="hljs-number">1</span>]; - token.fn = fn;</pre></div></div> - - </li> - - - <li id="section-264"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-264">¶</a> - </div> - <p>cleanup token</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">delete</span> token.value; - - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> params = token.params && Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]), - fn = token.fn, - value; - - <span class="hljs-keyword">if</span> (Twig.functions[fn]) {</pre></div></div> - - </li> - - - <li id="section-265"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-265">¶</a> - </div> - <p>Get the function from the built-in functions</p> - - </div> - - <div class="content"><div class='highlight'><pre> value = Twig.functions[fn].apply(<span class="hljs-keyword">this</span>, params); - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> context[fn] == <span class="hljs-string">'function'</span>) {</pre></div></div> - - </li> - - - <li id="section-266"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-266">¶</a> - </div> - <p>Get the function from the user/context defined functions</p> - - </div> - - <div class="content"><div class='highlight'><pre> value = context[fn].apply(context, params); - - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(fn + <span class="hljs-string">' function does not exist and is not defined in the context'</span>); - } - - stack.push(value); - } - },</pre></div></div> - - </li> - - - <li id="section-267"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-267">¶</a> - </div> - <p>Token representing a variable.</p> -<p>Variables can contain letters, numbers, underscores and -dashes, but must start with a letter or underscore.</p> -<p>Variables are retrieved from the render context and take -the value of ‘undefined’ if the given variable doesn’t -exist in the context.</p> - - </div> - - <div class="content"><div class='highlight'><pre> { - type: Twig.expression.type.variable,</pre></div></div> - - </li> - - - <li id="section-268"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-268">¶</a> - </div> - <p>match any letter or <em>, then any number of letters, numbers, </em> or -</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^[a-zA-Z_][a-zA-Z0-9_]*/</span>, - next: Twig.expression.set.operations_extended.concat([ - Twig.expression.type.parameter.start]), - compile: Twig.expression.fn.compile.push, - validate: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">match, tokens</span>) </span>{ - <span class="hljs-keyword">return</span> (Twig.indexOf(Twig.expression.reservedWords, match[<span class="hljs-number">0</span>]) < <span class="hljs-number">0</span>); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-269"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-269">¶</a> - </div> - <p>Get the variable from the context</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> value = Twig.expression.resolve(context[token.value], context); - stack.push(value); - } - }, - { - type: Twig.expression.type.key.period, - regex: <span class="hljs-regexp">/^\.([a-zA-Z0-9_]+)/</span>, - next: Twig.expression.set.operations_extended.concat([ - Twig.expression.type.parameter.start]), - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - token.key = token.match[<span class="hljs-number">1</span>]; - <span class="hljs-keyword">delete</span> token.match; - <span class="hljs-keyword">delete</span> token.value; - - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - <span class="hljs-keyword">var</span> params = token.params && Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]), - key = token.key, - object = stack.pop(), - value; - - <span class="hljs-keyword">if</span> (object === <span class="hljs-literal">null</span> || object === <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.options.strict_variables) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Can't access a key "</span> + key + <span class="hljs-string">" on an null or undefined object."</span>); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; - } - } - - <span class="hljs-keyword">var</span> capitalize = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{<span class="hljs-keyword">return</span> value.substr(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>).toUpperCase() + value.substr(<span class="hljs-number">1</span>);};</pre></div></div> - - </li> - - - <li id="section-270"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-270">¶</a> - </div> - <p>Get the variable from the context</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> object === <span class="hljs-string">'object'</span> && key <span class="hljs-keyword">in</span> object) { - value = object[key]; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (object[<span class="hljs-string">"get"</span>+capitalize(key)] !== <span class="hljs-literal">undefined</span>) { - value = object[<span class="hljs-string">"get"</span>+capitalize(key)]; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (object[<span class="hljs-string">"is"</span>+capitalize(key)] !== <span class="hljs-literal">undefined</span>) { - value = object[<span class="hljs-string">"is"</span>+capitalize(key)]; - } <span class="hljs-keyword">else</span> { - value = <span class="hljs-literal">undefined</span>; - } - stack.push(Twig.expression.resolve(value, object, params)); - } - }, - { - type: Twig.expression.type.key.brackets, - regex: <span class="hljs-regexp">/^\[([^\]]*)\]/</span>, - next: Twig.expression.set.operations_extended.concat([ - Twig.expression.type.parameter.start]), - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">var</span> match = token.match[<span class="hljs-number">1</span>]; - <span class="hljs-keyword">delete</span> token.value; - <span class="hljs-keyword">delete</span> token.match;</pre></div></div> - - </li> - - - <li id="section-271"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-271">¶</a> - </div> - <p>The expression stack for the key</p> - - </div> - - <div class="content"><div class='highlight'><pre> token.stack = Twig.expression.compile({ - value: match - }).stack; - - output.push(token); - }, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-272"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-272">¶</a> - </div> - <p>Evaluate key</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> params = token.params && Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.params, context]), - key = Twig.expression.parse.apply(<span class="hljs-keyword">this</span>, [token.stack, context]), - object = stack.pop(), - value; - - <span class="hljs-keyword">if</span> (object === <span class="hljs-literal">null</span> || object === <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.options.strict_variables) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Can't access a key "</span> + key + <span class="hljs-string">" on an null or undefined object."</span>); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; - } - }</pre></div></div> - - </li> - - - <li id="section-273"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-273">¶</a> - </div> - <p>Get the variable from the context</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> object === <span class="hljs-string">'object'</span> && key <span class="hljs-keyword">in</span> object) { - value = object[key]; - } <span class="hljs-keyword">else</span> { - value = <span class="hljs-literal">null</span>; - } - stack.push(Twig.expression.resolve(value, object, params)); - } - }, - { - <span class="hljs-comment">/** - * Match a null value. - */</span> - type: Twig.expression.type._null,</pre></div></div> - - </li> - - - <li id="section-274"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-274">¶</a> - </div> - <p>match a number</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^(null|NULL|none|NONE)/</span>, - next: Twig.expression.set.operations, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - <span class="hljs-keyword">delete</span> token.match; - token.value = <span class="hljs-literal">null</span>; - output.push(token); - }, - parse: Twig.expression.fn.parse.push_value - }, - { - <span class="hljs-comment">/** - * Match the context - */</span> - type: Twig.expression.type.context, - regex: <span class="hljs-regexp">/^_context/</span>, - next: Twig.expression.set.operations_extended.concat([ - Twig.expression.type.parameter.start]), - compile: Twig.expression.fn.compile.push, - parse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, context</span>) </span>{ - stack.push(context); - } - }, - { - <span class="hljs-comment">/** - * Match a number (integer or decimal) - */</span> - type: Twig.expression.type.number,</pre></div></div> - - </li> - - - <li id="section-275"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-275">¶</a> - </div> - <p>match a number</p> - - </div> - - <div class="content"><div class='highlight'><pre> regex: <span class="hljs-regexp">/^\-?\d+(\.\d+)?/</span>, - next: Twig.expression.set.operations, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - token.value = <span class="hljs-built_in">Number</span>(token.value); - output.push(token); - }, - parse: Twig.expression.fn.parse.push_value - }, - { - <span class="hljs-comment">/** - * Match a boolean - */</span> - type: Twig.expression.type.bool, - regex: <span class="hljs-regexp">/^(true|TRUE|false|FALSE)/</span>, - next: Twig.expression.set.operations, - compile: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">token, stack, output</span>) </span>{ - token.value = (token.match[<span class="hljs-number">0</span>].toLowerCase( ) === <span class="hljs-string">"true"</span>); - <span class="hljs-keyword">delete</span> token.match; - output.push(token); - }, - parse: Twig.expression.fn.parse.push_value - } - ]; - - <span class="hljs-comment">/** - * Resolve a context value. - * - * If the value is a function, it is executed with a context parameter. - * - * @param {string} key The context object key. - * @param {Object} context The render context. - */</span> - Twig.expression.resolve = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, context, params</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> value == <span class="hljs-string">'function'</span>) { - <span class="hljs-keyword">return</span> value.apply(context, params || []); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> value; - } - }; - - <span class="hljs-comment">/** - * Registry for logic handlers. - */</span> - Twig.expression.handler = {}; - - <span class="hljs-comment">/** - * Define a new expression type, available at Twig.logic.type.{type} - * - * @param {string} type The name of the new type. - */</span> - Twig.expression.extendType = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">type</span>) </span>{ - Twig.expression.type[type] = <span class="hljs-string">"Twig.expression.type."</span> + type; - }; - - <span class="hljs-comment">/** - * Extend the expression parsing functionality with a new definition. - * - * Token definitions follow this format: - * { - * type: One of Twig.expression.type.[type], either pre-defined or added using - * Twig.expression.extendType - * - * next: Array of types from Twig.expression.type that can follow this token, - * - * regex: A regex or array of regex's that should match the token. - * - * compile: function(token, stack, output) called when this token is being compiled. - * Should return an object with stack and output set. - * - * parse: function(token, stack, context) called when this token is being parsed. - * Should return an object with stack and context set. - * } - * - * @param {Object} definition A token definition. - */</span> - Twig.expression.extend = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">definition</span>) </span>{ - <span class="hljs-keyword">if</span> (!definition.type) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to extend logic definition. No type provided for "</span> + definition); - } - Twig.expression.handler[definition.type] = definition; - };</pre></div></div> - - </li> - - - <li id="section-276"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-276">¶</a> - </div> - <p>Extend with built-in expressions</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span> (Twig.expression.definitions.length > <span class="hljs-number">0</span>) { - Twig.expression.extend(Twig.expression.definitions.shift()); - } - - <span class="hljs-comment">/** - * Break an expression into tokens defined in Twig.expression.definitions. - * - * @param {string} expression The string to tokenize. - * - * @return {Array} An array of tokens. - */</span> - Twig.expression.tokenize = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">expression</span>) </span>{ - <span class="hljs-keyword">var</span> tokens = [],</pre></div></div> - - </li> - - - <li id="section-277"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-277">¶</a> - </div> - <p>Keep an offset of the location in the expression for error messages.</p> - - </div> - - <div class="content"><div class='highlight'><pre> exp_offset = <span class="hljs-number">0</span>,</pre></div></div> - - </li> - - - <li id="section-278"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-278">¶</a> - </div> - <p>The valid next tokens of the previous token</p> - - </div> - - <div class="content"><div class='highlight'><pre> next = <span class="hljs-literal">null</span>,</pre></div></div> - - </li> - - - <li id="section-279"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-279">¶</a> - </div> - <p>Match information</p> - - </div> - - <div class="content"><div class='highlight'><pre> type, regex, regex_array,</pre></div></div> - - </li> - - - <li id="section-280"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-280">¶</a> - </div> - <p>The possible next token for the match</p> - - </div> - - <div class="content"><div class='highlight'><pre> token_next,</pre></div></div> - - </li> - - - <li id="section-281"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-281">¶</a> - </div> - <p>Has a match been found from the definitions</p> - - </div> - - <div class="content"><div class='highlight'><pre> match_found, invalid_matches = [], match_function; - - match_function = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> match = <span class="hljs-built_in">Array</span>.prototype.slice.apply(<span class="hljs-built_in">arguments</span>), - string = match.pop(), - offset = match.pop(); - - Twig.log.trace(<span class="hljs-string">"Twig.expression.tokenize"</span>, - <span class="hljs-string">"Matched a "</span>, type, <span class="hljs-string">" regular expression of "</span>, match); - - <span class="hljs-keyword">if</span> (next && Twig.indexOf(next, type) < <span class="hljs-number">0</span>) { - invalid_matches.push( - type + <span class="hljs-string">" cannot follow a "</span> + tokens[tokens.length - <span class="hljs-number">1</span>].type + - <span class="hljs-string">" at template:"</span> + exp_offset + <span class="hljs-string">" near '"</span> + match[<span class="hljs-number">0</span>].substring(<span class="hljs-number">0</span>, <span class="hljs-number">20</span>) + - <span class="hljs-string">"...'"</span> - );</pre></div></div> - - </li> - - - <li id="section-282"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-282">¶</a> - </div> - <p>Not a match, don’t change the expression</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> match[<span class="hljs-number">0</span>]; - }</pre></div></div> - - </li> - - - <li id="section-283"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-283">¶</a> - </div> - <p>Validate the token if a validation function is provided</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (Twig.expression.handler[type].validate && - !Twig.expression.handler[type].validate(match, tokens)) { - <span class="hljs-keyword">return</span> match[<span class="hljs-number">0</span>]; - } - - invalid_matches = []; - - tokens.push({ - type: type, - value: match[<span class="hljs-number">0</span>], - match: match - }); - - match_found = <span class="hljs-literal">true</span>; - next = token_next; - exp_offset += match[<span class="hljs-number">0</span>].length;</pre></div></div> - - </li> - - - <li id="section-284"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-284">¶</a> - </div> - <p>Does the token need to return output back to the expression string -e.g. a function match of cycle( might return the ‘(‘ back to the expression -This allows look-ahead to differentiate between token types (e.g. functions and variable names)</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (Twig.expression.handler[type].transform) { - <span class="hljs-keyword">return</span> Twig.expression.handler[type].transform(match, tokens); - } - <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>; - }; - - Twig.log.debug(<span class="hljs-string">"Twig.expression.tokenize"</span>, <span class="hljs-string">"Tokenizing expression "</span>, expression); - - <span class="hljs-keyword">while</span> (expression.length > <span class="hljs-number">0</span>) { - expression = expression.trim(); - <span class="hljs-keyword">for</span> (type <span class="hljs-keyword">in</span> Twig.expression.handler) { - <span class="hljs-keyword">if</span> (Twig.expression.handler.hasOwnProperty(type)) { - token_next = Twig.expression.handler[type].next; - regex = Twig.expression.handler[type].regex;</pre></div></div> - - </li> - - - <li id="section-285"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-285">¶</a> - </div> - <p>Twig.log.trace(“Checking type “, type, “ on “, expression);</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (regex <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Array</span>) { - regex_array = regex; - } <span class="hljs-keyword">else</span> { - regex_array = [regex]; - } - - match_found = <span class="hljs-literal">false</span>; - <span class="hljs-keyword">while</span> (regex_array.length > <span class="hljs-number">0</span>) { - regex = regex_array.pop(); - expression = expression.replace(regex, match_function); - }</pre></div></div> - - </li> - - - <li id="section-286"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-286">¶</a> - </div> - <p>An expression token has been matched. Break the for loop and start trying to - match the next template (if expression isn’t empty.)</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (match_found) { - <span class="hljs-keyword">break</span>; - } - } - } - <span class="hljs-keyword">if</span> (!match_found) { - <span class="hljs-keyword">if</span> (invalid_matches.length > <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(invalid_matches.join(<span class="hljs-string">" OR "</span>)); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to parse '"</span> + expression + <span class="hljs-string">"' at template position"</span> + exp_offset); - } - } - } - - Twig.log.trace(<span class="hljs-string">"Twig.expression.tokenize"</span>, <span class="hljs-string">"Tokenized to "</span>, tokens); - <span class="hljs-keyword">return</span> tokens; - }; - - <span class="hljs-comment">/** - * Compile an expression token. - * - * @param {Object} raw_token The uncompiled token. - * - * @return {Object} The compiled token. - */</span> - Twig.expression.compile = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">raw_token</span>) </span>{ - <span class="hljs-keyword">var</span> expression = raw_token.value,</pre></div></div> - - </li> - - - <li id="section-287"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-287">¶</a> - </div> - <p>Tokenize expression</p> - - </div> - - <div class="content"><div class='highlight'><pre> tokens = Twig.expression.tokenize(expression), - token = <span class="hljs-literal">null</span>, - output = [], - stack = [], - token_template = <span class="hljs-literal">null</span>; - - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Compiling "</span>, expression);</pre></div></div> - - </li> - - - <li id="section-288"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-288">¶</a> - </div> - <p>Push tokens into RPN stack using the Sunting-yard algorithm -See <a href="http://en.wikipedia.org/wiki/Shunting_yard_algorithm">http://en.wikipedia.org/wiki/Shunting_yard_algorithm</a></p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">while</span> (tokens.length > <span class="hljs-number">0</span>) { - token = tokens.shift(); - token_template = Twig.expression.handler[token.type]; - - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Compiling "</span>, token);</pre></div></div> - - </li> - - - <li id="section-289"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-289">¶</a> - </div> - <p>Compile the template</p> - - </div> - - <div class="content"><div class='highlight'><pre> token_template.compile && token_template.compile(token, stack, output); - - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Stack is"</span>, stack); - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Output is"</span>, output); - } - - <span class="hljs-keyword">while</span>(stack.length > <span class="hljs-number">0</span>) { - output.push(stack.pop()); - } - - Twig.log.trace(<span class="hljs-string">"Twig.expression.compile: "</span>, <span class="hljs-string">"Final output is"</span>, output); - - raw_token.stack = output; - <span class="hljs-keyword">delete</span> raw_token.value; - - <span class="hljs-keyword">return</span> raw_token; - }; - - - <span class="hljs-comment">/** - * Parse an RPN expression stack within a context. - * - * @param {Array} tokens An array of compiled expression tokens. - * @param {Object} context The render context to parse the tokens with. - * - * @return {Object} The result of parsing all the tokens. The result - * can be anything, String, Array, Object, etc... based on - * the given expression. - */</span> - Twig.expression.parse = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">tokens, context</span>) </span>{ - <span class="hljs-keyword">var</span> that = <span class="hljs-keyword">this</span>;</pre></div></div> - - </li> - - - <li id="section-290"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-290">¶</a> - </div> - <p>If the token isn’t an array, make it one.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!(tokens <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Array</span>)) { - tokens = [tokens]; - }</pre></div></div> - - </li> - - - <li id="section-291"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-291">¶</a> - </div> - <p>The output stack</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> stack = [], - token_template = <span class="hljs-literal">null</span>; - - Twig.forEach(tokens, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">token</span>) </span>{ - token_template = Twig.expression.handler[token.type]; - - token_template.parse && token_template.parse.apply(that, [token, stack, context]); - });</pre></div></div> - - </li> - - - <li id="section-292"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-292">¶</a> - </div> - <p>Pop the final value off the stack</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> stack.pop(); - }; - - <span class="hljs-keyword">return</span> Twig; - -})( Twig || { } );</pre></div></div> - - </li> - - - <li id="section-293"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-293">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-294"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-294">¶</a> - </div> - <h2 id="twig-expression-operator-js">twig.expression.operator.js</h2> -<p>This file handles operator lookups and parsing.</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>; - - <span class="hljs-comment">/** - * Operator associativity constants. - */</span> - Twig.expression.operator = { - leftToRight: <span class="hljs-string">'leftToRight'</span>, - rightToLeft: <span class="hljs-string">'rightToLeft'</span> - }; - - <span class="hljs-keyword">var</span> containment = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a, b</span>) </span>{ - <span class="hljs-keyword">if</span> (b === <span class="hljs-literal">undefined</span> || b === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (b.indexOf !== <span class="hljs-literal">undefined</span>) {</pre></div></div> - - </li> - - - <li id="section-295"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-295">¶</a> - </div> - <p>String</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> a === b || a !== <span class="hljs-string">''</span> && b.indexOf(a) > <span class="hljs-number">-1</span>; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">var</span> el; - <span class="hljs-keyword">for</span> (el <span class="hljs-keyword">in</span> b) { - <span class="hljs-keyword">if</span> (b.hasOwnProperty(el) && b[el] === a) { - <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; - } - } - <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; - } - }; - - <span class="hljs-comment">/** - * 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. - */</span> - Twig.expression.operator.lookup = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">operator, token</span>) </span>{ - <span class="hljs-keyword">switch</span> (operator) { - <span class="hljs-keyword">case</span> <span class="hljs-string">".."</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'not in'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'in'</span>: - token.precidence = <span class="hljs-number">20</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">','</span>: - token.precidence = <span class="hljs-number">18</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>;</pre></div></div> - - </li> - - - <li id="section-296"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-296">¶</a> - </div> - <p>Ternary</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">case</span> <span class="hljs-string">'?'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">':'</span>: - token.precidence = <span class="hljs-number">16</span>; - token.associativity = Twig.expression.operator.rightToLeft; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'or'</span>: - token.precidence = <span class="hljs-number">14</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'and'</span>: - token.precidence = <span class="hljs-number">13</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'=='</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'!='</span>: - token.precidence = <span class="hljs-number">9</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'<'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'<='</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'>'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'>='</span>: - token.precidence = <span class="hljs-number">8</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - - <span class="hljs-keyword">case</span> <span class="hljs-string">'~'</span>: <span class="hljs-comment">// String concatination</span> - <span class="hljs-keyword">case</span> <span class="hljs-string">'+'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'-'</span>: - token.precidence = <span class="hljs-number">6</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'//'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'**'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'*'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'/'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'%'</span>: - token.precidence = <span class="hljs-number">5</span>; - token.associativity = Twig.expression.operator.leftToRight; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'not'</span>: - token.precidence = <span class="hljs-number">3</span>; - token.associativity = Twig.expression.operator.rightToLeft; - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">default</span>: - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(operator + <span class="hljs-string">" is an unknown operator."</span>); - } - token.operator = operator; - <span class="hljs-keyword">return</span> token; - }; - - <span class="hljs-comment">/** - * Handle operations on the RPN stack. - * - * Returns the updated stack. - */</span> - Twig.expression.operator.parse = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">operator, stack</span>) </span>{ - Twig.log.trace(<span class="hljs-string">"Twig.expression.operator.parse: "</span>, <span class="hljs-string">"Handling "</span>, operator); - <span class="hljs-keyword">var</span> a, b, c; - <span class="hljs-keyword">switch</span> (operator) { - <span class="hljs-keyword">case</span> <span class="hljs-string">':'</span>:</pre></div></div> - - </li> - - - <li id="section-297"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-297">¶</a> - </div> - <p>Ignore</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'?'</span>: - c = stack.pop(); <span class="hljs-comment">// false expr</span> - b = stack.pop(); <span class="hljs-comment">// true expr</span> - a = stack.pop(); <span class="hljs-comment">// conditional</span> - <span class="hljs-keyword">if</span> (a) { - stack.push(b); - } <span class="hljs-keyword">else</span> { - stack.push(c); - } - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'+'</span>: - b = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - a = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - stack.push(a + b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'-'</span>: - b = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - a = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - stack.push(a - b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'*'</span>: - b = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - a = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - stack.push(a * b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'/'</span>: - b = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - a = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - stack.push(a / b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'//'</span>: - b = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - a = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - stack.push(<span class="hljs-built_in">parseInt</span>(a / b)); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'%'</span>: - b = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - a = <span class="hljs-built_in">parseFloat</span>(stack.pop()); - stack.push(a % b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'~'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push( (a != <span class="hljs-literal">null</span> ? a.toString() : <span class="hljs-string">""</span>) - + (b != <span class="hljs-literal">null</span> ? b.toString() : <span class="hljs-string">""</span>) ); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'not'</span>: - <span class="hljs-keyword">case</span> <span class="hljs-string">'!'</span>: - stack.push(!stack.pop()); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'<'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a < b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'<='</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a <= b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'>'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a > b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'>='</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a >= b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'==='</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a === b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'=='</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a == b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'!=='</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a !== b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'!='</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a != b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'or'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a || b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'and'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(a && b); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'**'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push(<span class="hljs-built_in">Math</span>.pow(a, b)); - <span class="hljs-keyword">break</span>; - - - <span class="hljs-keyword">case</span> <span class="hljs-string">'not in'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push( !containment(a, b) ); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'in'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push( containment(a, b) ); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">case</span> <span class="hljs-string">'..'</span>: - b = stack.pop(); - a = stack.pop(); - stack.push( Twig.functions.range(a, b) ); - <span class="hljs-keyword">break</span>; - - <span class="hljs-keyword">default</span>: - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(operator + <span class="hljs-string">" is an unknown operator."</span>); - } - }; - - <span class="hljs-keyword">return</span> Twig; - -})( Twig || { } );</pre></div></div> - - </li> - - - <li id="section-298"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-298">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-299"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-299">¶</a> - </div> - <h2 id="twig-filters-js">twig.filters.js</h2> -<p>This file handles parsing filters.</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-300"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-300">¶</a> - </div> - <p>Determine object type</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">is</span>(<span class="hljs-params">type, obj</span>) </span>{ - <span class="hljs-keyword">var</span> clas = <span class="hljs-built_in">Object</span>.prototype.toString.call(obj).slice(<span class="hljs-number">8</span>, <span class="hljs-number">-1</span>); - <span class="hljs-keyword">return</span> obj !== <span class="hljs-literal">undefined</span> && obj !== <span class="hljs-literal">null</span> && clas === type; - } - - Twig.filters = {</pre></div></div> - - </li> - - - <li id="section-301"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-301">¶</a> - </div> - <p>String Filters</p> - - </div> - - <div class="content"><div class='highlight'><pre> upper: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">typeof</span> value !== <span class="hljs-string">"string"</span> ) { - <span class="hljs-keyword">return</span> value; - } - - <span class="hljs-keyword">return</span> value.toUpperCase(); - }, - lower: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">typeof</span> value !== <span class="hljs-string">"string"</span> ) { - <span class="hljs-keyword">return</span> value; - } - - <span class="hljs-keyword">return</span> value.toLowerCase(); - }, - capitalize: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">typeof</span> value !== <span class="hljs-string">"string"</span> ) { - <span class="hljs-keyword">return</span> value; - } - - <span class="hljs-keyword">return</span> value.substr(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>).toUpperCase() + value.toLowerCase().substr(<span class="hljs-number">1</span>); - }, - title: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">typeof</span> value !== <span class="hljs-string">"string"</span> ) { - <span class="hljs-keyword">return</span> value; - } - - <span class="hljs-keyword">return</span> value.toLowerCase().replace( <span class="hljs-regexp">/(^|\s)([a-z])/g</span> , <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">m, p1, p2</span>)</span>{ - <span class="hljs-keyword">return</span> p1 + p2.toUpperCase(); - }); - }, - length: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Array"</span>, value) || <span class="hljs-keyword">typeof</span> value === <span class="hljs-string">"string"</span>) { - <span class="hljs-keyword">return</span> value.length; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Object"</span>, value)) { - <span class="hljs-keyword">if</span> (value._keys === <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(value).length; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> value._keys.length; - } - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; - } - },</pre></div></div> - - </li> - - - <li id="section-302"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-302">¶</a> - </div> - <p>Array/Object Filters</p> - - </div> - - <div class="content"><div class='highlight'><pre> reverse: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Array"</span>, value)) { - <span class="hljs-keyword">return</span> value.reverse(); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"String"</span>, value)) { - <span class="hljs-keyword">return</span> value.split(<span class="hljs-string">""</span>).reverse().join(<span class="hljs-string">""</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Object"</span>, value)) { - <span class="hljs-keyword">var</span> keys = value._keys || <span class="hljs-built_in">Object</span>.keys(value).reverse(); - value._keys = keys; - <span class="hljs-keyword">return</span> value; - } - }, - sort: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Array"</span>, value)) { - <span class="hljs-keyword">return</span> value.sort(); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (is(<span class="hljs-string">'Object'</span>, value)) {</pre></div></div> - - </li> - - - <li id="section-303"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-303">¶</a> - </div> - <p>Sorting objects isn’t obvious since the order of -returned keys isn’t guaranteed in JavaScript. -Because of this we use a “hidden” key called _keys to -store the keys in the order we want to return them.</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">delete</span> value._keys; - <span class="hljs-keyword">var</span> keys = <span class="hljs-built_in">Object</span>.keys(value), - sorted_keys = keys.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a, b</span>) </span>{ - <span class="hljs-keyword">var</span> a1, a2;</pre></div></div> - - </li> - - - <li id="section-304"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-304">¶</a> - </div> - <p>if a and b are comparable, we’re fine :-)</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span>((value[a] > value[b]) == !(value[a] <= value[b])) { - <span class="hljs-keyword">return</span> value[a] > value[b] ? <span class="hljs-number">1</span> : - value[a] < value[b] ? <span class="hljs-number">-1</span> : - <span class="hljs-number">0</span>; - }</pre></div></div> - - </li> - - - <li id="section-305"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-305">¶</a> - </div> - <p>if a and b can be parsed as numbers, we can compare -their numeric value</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">isNaN</span>(a1 = <span class="hljs-built_in">parseFloat</span>(value[a])) && - !<span class="hljs-built_in">isNaN</span>(b1 = <span class="hljs-built_in">parseFloat</span>(value[b]))) { - <span class="hljs-keyword">return</span> a1 > b1 ? <span class="hljs-number">1</span> : - a1 < b1 ? <span class="hljs-number">-1</span> : - <span class="hljs-number">0</span>; - }</pre></div></div> - - </li> - - - <li id="section-306"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-306">¶</a> - </div> - <p>if one of the values is a string, we convert the -other value to string as well</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> value[a] == <span class="hljs-string">'string'</span>) { - <span class="hljs-keyword">return</span> value[a] > value[b].toString() ? <span class="hljs-number">1</span> : - value[a] < value[b].toString() ? <span class="hljs-number">-1</span> : - <span class="hljs-number">0</span>; - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> value[b] == <span class="hljs-string">'string'</span>) { - <span class="hljs-keyword">return</span> value[a].toString() > value[b] ? <span class="hljs-number">1</span> : - value[a].toString() < value[b] ? <span class="hljs-number">-1</span> : - <span class="hljs-number">0</span>; - }</pre></div></div> - - </li> - - - <li id="section-307"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-307">¶</a> - </div> - <p>everything failed - return ‘null’ as sign, that -the values are not comparable</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; - } - }); - value._keys = sorted_keys; - <span class="hljs-keyword">return</span> value; - } - }, - keys: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">var</span> keyset = value._keys || <span class="hljs-built_in">Object</span>.keys(value), - output = []; - - Twig.forEach(keyset, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{ - <span class="hljs-keyword">if</span> (key === <span class="hljs-string">"_keys"</span>) <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Ignore the _keys property</span> - <span class="hljs-keyword">if</span> (value.hasOwnProperty(key)) { - output.push(key); - } - }); - <span class="hljs-keyword">return</span> output; - }, - url_encode: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">var</span> result = <span class="hljs-built_in">encodeURIComponent</span>(value); - result = result.replace(<span class="hljs-string">"'"</span>, <span class="hljs-string">"%27"</span>); - <span class="hljs-keyword">return</span> result; - }, - join: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">var</span> join_str = <span class="hljs-string">""</span>, - output = [], - keyset = <span class="hljs-literal">null</span>; - - <span class="hljs-keyword">if</span> (params && params[<span class="hljs-number">0</span>]) { - join_str = params[<span class="hljs-number">0</span>]; - } - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Array"</span>, value)) { - output = value; - } <span class="hljs-keyword">else</span> { - keyset = value._keys || <span class="hljs-built_in">Object</span>.keys(value); - Twig.forEach(keyset, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{ - <span class="hljs-keyword">if</span> (key === <span class="hljs-string">"_keys"</span>) <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Ignore the _keys property</span> - <span class="hljs-keyword">if</span> (value.hasOwnProperty(key)) { - output.push(value[key]); - } - }); - } - <span class="hljs-keyword">return</span> output.join(join_str); - }, - <span class="hljs-string">"default"</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (params !== <span class="hljs-literal">undefined</span> && params.length > <span class="hljs-number">1</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"default filter expects one argument"</span>); - } - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span> || value === <span class="hljs-string">''</span> ) { - <span class="hljs-keyword">if</span> (params === <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>; - } - - <span class="hljs-keyword">return</span> params[<span class="hljs-number">0</span>]; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> value; - } - }, - json_encode: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span>(value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">return</span> <span class="hljs-string">"null"</span>; - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((<span class="hljs-keyword">typeof</span> value == <span class="hljs-string">'object'</span>) && (is(<span class="hljs-string">"Array"</span>, value))) { - output = []; - - Twig.forEach(value, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">v</span>) </span>{ - output.push(Twig.filters.json_encode(v)); - }); - - <span class="hljs-keyword">return</span> <span class="hljs-string">"["</span> + output.join(<span class="hljs-string">","</span>) + <span class="hljs-string">"]"</span>; - } - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> value == <span class="hljs-string">'object'</span>) { - <span class="hljs-keyword">var</span> keyset = value._keys || <span class="hljs-built_in">Object</span>.keys(value), - output = []; - - Twig.forEach(keyset, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{ - output.push(<span class="hljs-built_in">JSON</span>.stringify(key) + <span class="hljs-string">":"</span> + Twig.filters.json_encode(value[key])); - }); - - <span class="hljs-keyword">return</span> <span class="hljs-string">"{"</span> + output.join(<span class="hljs-string">","</span>) + <span class="hljs-string">"}"</span>; - } - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(value); - } - }, - merge: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">var</span> obj = [], - arr_index = <span class="hljs-number">0</span>, - keyset = [];</pre></div></div> - - </li> - - - <li id="section-308"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-308">¶</a> - </div> - <p>Check to see if all the objects being merged are arrays</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!is(<span class="hljs-string">"Array"</span>, value)) {</pre></div></div> - - </li> - - - <li id="section-309"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-309">¶</a> - </div> - <p>Create obj as an Object</p> - - </div> - - <div class="content"><div class='highlight'><pre> obj = { }; - } <span class="hljs-keyword">else</span> { - Twig.forEach(params, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">param</span>) </span>{ - <span class="hljs-keyword">if</span> (!is(<span class="hljs-string">"Array"</span>, param)) { - obj = { }; - } - }); - } - <span class="hljs-keyword">if</span> (!is(<span class="hljs-string">"Array"</span>, obj)) { - obj._keys = []; - } - - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Array"</span>, value)) { - Twig.forEach(value, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">val</span>) </span>{ - <span class="hljs-keyword">if</span> (obj._keys) obj._keys.push(arr_index); - obj[arr_index] = val; - arr_index++; - }); - } <span class="hljs-keyword">else</span> { - keyset = value._keys || <span class="hljs-built_in">Object</span>.keys(value); - Twig.forEach(keyset, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{ - obj[key] = value[key]; - obj._keys.push(key);</pre></div></div> - - </li> - - - <li id="section-310"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-310">¶</a> - </div> - <p>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.</p> -<p>Example {{ [“a”, “b”]|merge({“4”:”value”}, [“c”, “d”]) -Without this, d would have an index of “4” and overwrite the value - of “value”</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> int_key = <span class="hljs-built_in">parseInt</span>(key, <span class="hljs-number">10</span>); - <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(int_key) && int_key >= arr_index) { - arr_index = int_key + <span class="hljs-number">1</span>; - } - }); - }</pre></div></div> - - </li> - - - <li id="section-311"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-311">¶</a> - </div> - <p>mixin the merge arrays</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.forEach(params, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">param</span>) </span>{ - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Array"</span>, param)) { - Twig.forEach(param, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">val</span>) </span>{ - <span class="hljs-keyword">if</span> (obj._keys) obj._keys.push(arr_index); - obj[arr_index] = val; - arr_index++; - }); - } <span class="hljs-keyword">else</span> { - keyset = param._keys || <span class="hljs-built_in">Object</span>.keys(param); - Twig.forEach(keyset, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key</span>) </span>{ - <span class="hljs-keyword">if</span> (!obj[key]) obj._keys.push(key); - obj[key] = param[key]; - - <span class="hljs-keyword">var</span> int_key = <span class="hljs-built_in">parseInt</span>(key, <span class="hljs-number">10</span>); - <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(int_key) && int_key >= arr_index) { - arr_index = int_key + <span class="hljs-number">1</span>; - } - }); - } - }); - <span class="hljs-keyword">if</span> (params.length === <span class="hljs-number">0</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Filter merge expects at least one parameter"</span>); - } - - <span class="hljs-keyword">return</span> obj; - }, - date: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">var</span> date = Twig.functions.date(value); - <span class="hljs-keyword">var</span> format = params && params.length ? params[<span class="hljs-number">0</span>] : <span class="hljs-string">'F j, Y H:i'</span>; - <span class="hljs-keyword">return</span> Twig.lib.formatDate(date, format); - }, - - date_modify: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">return</span>; - } - <span class="hljs-keyword">if</span> (params === <span class="hljs-literal">undefined</span> || params.length !== <span class="hljs-number">1</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"date_modify filter expects 1 argument"</span>); - } - - <span class="hljs-keyword">var</span> modifyText = params[<span class="hljs-number">0</span>], time; - - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Date"</span>, value)) { - time = Twig.lib.strtotime(modifyText, value.getTime() / <span class="hljs-number">1000</span>); - } - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"String"</span>, value)) { - time = Twig.lib.strtotime(modifyText, Twig.lib.strtotime(value)); - } - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Number"</span>, value)) { - time = Twig.lib.strtotime(modifyText, value); - } - - <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(time * <span class="hljs-number">1000</span>); - }, - - replace: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span>||value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">var</span> pairs = params[<span class="hljs-number">0</span>], - tag; - <span class="hljs-keyword">for</span> (tag <span class="hljs-keyword">in</span> pairs) { - <span class="hljs-keyword">if</span> (pairs.hasOwnProperty(tag) && tag !== <span class="hljs-string">"_keys"</span>) { - value = Twig.lib.replaceAll(value, tag, pairs[tag]); - } - } - <span class="hljs-keyword">return</span> value; - }, - - format: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">return</span> Twig.lib.vsprintf(value, params); - }, - - striptags: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">return</span> Twig.lib.strip_tags(value); - }, - - <span class="hljs-built_in">escape</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span>|| value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">var</span> strategy = <span class="hljs-string">"html"</span>; - <span class="hljs-keyword">if</span>(params && params.length && params[<span class="hljs-number">0</span>] !== <span class="hljs-literal">true</span>) - strategy = params[<span class="hljs-number">0</span>]; - - <span class="hljs-keyword">if</span>(strategy == <span class="hljs-string">"html"</span>) { - <span class="hljs-keyword">var</span> raw_value = value.toString().replace(<span class="hljs-regexp">/&/g</span>, <span class="hljs-string">"&amp;"</span>) - .replace(<span class="hljs-regexp">/</g</span>, <span class="hljs-string">"&lt;"</span>) - .replace(<span class="hljs-regexp">/>/g</span>, <span class="hljs-string">"&gt;"</span>) - .replace(<span class="hljs-regexp">/"/g</span>, <span class="hljs-string">"&quot;"</span>) - .replace(<span class="hljs-regexp">/'/g</span>, <span class="hljs-string">"&#039;"</span>); - <span class="hljs-keyword">return</span> Twig.Markup(raw_value, <span class="hljs-string">'html'</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(strategy == <span class="hljs-string">"js"</span>) { - <span class="hljs-keyword">var</span> raw_value = value.toString(); - <span class="hljs-keyword">var</span> result = <span class="hljs-string">""</span>; - - <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < raw_value.length; i++) { - <span class="hljs-keyword">if</span>(raw_value[i].match(<span class="hljs-regexp">/^[a-zA-Z0-9,\._]$/</span>)) - result += raw_value[i]; - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">var</span> char_code = raw_value.charCodeAt(i); - - <span class="hljs-keyword">if</span>(char_code < <span class="hljs-number">0x80</span>) - result += <span class="hljs-string">"\\x"</span> + char_code.toString(<span class="hljs-number">16</span>).toUpperCase(); - <span class="hljs-keyword">else</span> - result += Twig.lib.sprintf(<span class="hljs-string">"\\u%04s"</span>, char_code.toString(<span class="hljs-number">16</span>).toUpperCase()); - } - } - - <span class="hljs-keyword">return</span> Twig.Markup(result, <span class="hljs-string">'js'</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(strategy == <span class="hljs-string">"css"</span>) { - <span class="hljs-keyword">var</span> raw_value = value.toString(); - <span class="hljs-keyword">var</span> result = <span class="hljs-string">""</span>; - - <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < raw_value.length; i++) { - <span class="hljs-keyword">if</span>(raw_value[i].match(<span class="hljs-regexp">/^[a-zA-Z0-9]$/</span>)) - result += raw_value[i]; - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">var</span> char_code = raw_value.charCodeAt(i); - result += <span class="hljs-string">"\\"</span> + char_code.toString(<span class="hljs-number">16</span>).toUpperCase() + <span class="hljs-string">" "</span>; - } - } - - <span class="hljs-keyword">return</span> Twig.Markup(result, <span class="hljs-string">'css'</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(strategy == <span class="hljs-string">"url"</span>) { - <span class="hljs-keyword">var</span> result = Twig.filters.url_encode(value); - <span class="hljs-keyword">return</span> Twig.Markup(result, <span class="hljs-string">'url'</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(strategy == <span class="hljs-string">"html_attr"</span>) { - <span class="hljs-keyword">var</span> raw_value = value.toString(); - <span class="hljs-keyword">var</span> result = <span class="hljs-string">""</span>; - - <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < raw_value.length; i++) { - <span class="hljs-keyword">if</span>(raw_value[i].match(<span class="hljs-regexp">/^[a-zA-Z0-9,\.\-_]$/</span>)) - result += raw_value[i]; - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(raw_value[i].match(<span class="hljs-regexp">/^[&<>"]$/</span>)) - result += raw_value[i].replace(<span class="hljs-regexp">/&/g</span>, <span class="hljs-string">"&amp;"</span>) - .replace(<span class="hljs-regexp">/</g</span>, <span class="hljs-string">"&lt;"</span>) - .replace(<span class="hljs-regexp">/>/g</span>, <span class="hljs-string">"&gt;"</span>) - .replace(<span class="hljs-regexp">/"/g</span>, <span class="hljs-string">"&quot;"</span>); - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">var</span> char_code = raw_value.charCodeAt(i);</pre></div></div> - - </li> - - - <li id="section-312"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-312">¶</a> - </div> - <p>The following replaces characters undefined in HTML with -the hex entity for the Unicode replacement character.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span>(char_code <= <span class="hljs-number">0x1f</span> && char_code != <span class="hljs-number">0x09</span> && char_code != <span class="hljs-number">0x0a</span> && char_code != <span class="hljs-number">0x0d</span>) - result += <span class="hljs-string">"&#xFFFD;"</span>; - <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(char_code < <span class="hljs-number">0x80</span>) - result += Twig.lib.sprintf(<span class="hljs-string">"&#x%02s;"</span>, char_code.toString(<span class="hljs-number">16</span>).toUpperCase()); - <span class="hljs-keyword">else</span> - result += Twig.lib.sprintf(<span class="hljs-string">"&#x%04s;"</span>, char_code.toString(<span class="hljs-number">16</span>).toUpperCase()); - } - } - - <span class="hljs-keyword">return</span> Twig.Markup(result, <span class="hljs-string">'html_attr'</span>); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"escape strategy unsupported"</span>); - } - }, - - <span class="hljs-comment">/* Alias of escape */</span> - <span class="hljs-string">"e"</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">return</span> Twig.filters.escape(value, params); - }, - - nl2br: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - <span class="hljs-keyword">var</span> linebreak_tag = <span class="hljs-string">"BACKSLASH_n_replace"</span>, - br = <span class="hljs-string">"<br />"</span> + linebreak_tag; - - value = Twig.filters.escape(value) - .replace(<span class="hljs-regexp">/\r\n/g</span>, br) - .replace(<span class="hljs-regexp">/\r/g</span>, br) - .replace(<span class="hljs-regexp">/\n/g</span>, br); - - value = Twig.lib.replaceAll(value, linebreak_tag, <span class="hljs-string">"\n"</span>); - - <span class="hljs-keyword">return</span> Twig.Markup(value); - }, - - <span class="hljs-comment">/** - * Adapted from: http://phpjs.org/functions/number_format:481 - */</span> - number_format: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">var</span> number = value, - decimals = (params && params[<span class="hljs-number">0</span>]) ? params[<span class="hljs-number">0</span>] : <span class="hljs-literal">undefined</span>, - dec = (params && params[<span class="hljs-number">1</span>] !== <span class="hljs-literal">undefined</span>) ? params[<span class="hljs-number">1</span>] : <span class="hljs-string">"."</span>, - sep = (params && params[<span class="hljs-number">2</span>] !== <span class="hljs-literal">undefined</span>) ? params[<span class="hljs-number">2</span>] : <span class="hljs-string">","</span>; - - number = (number + <span class="hljs-string">''</span>).replace(<span class="hljs-regexp">/[^0-9+\-Ee.]/g</span>, <span class="hljs-string">''</span>); - <span class="hljs-keyword">var</span> n = !<span class="hljs-built_in">isFinite</span>(+number) ? <span class="hljs-number">0</span> : +number, - prec = !<span class="hljs-built_in">isFinite</span>(+decimals) ? <span class="hljs-number">0</span> : <span class="hljs-built_in">Math</span>.abs(decimals), - s = <span class="hljs-string">''</span>, - toFixedFix = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">n, prec</span>) </span>{ - <span class="hljs-keyword">var</span> k = <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">10</span>, prec); - <span class="hljs-keyword">return</span> <span class="hljs-string">''</span> + <span class="hljs-built_in">Math</span>.round(n * k) / k; - };</pre></div></div> - - </li> - - - <li id="section-313"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-313">¶</a> - </div> - <p>Fix for IE parseFloat(0.55).toFixed(0) = 0;</p> - - </div> - - <div class="content"><div class='highlight'><pre> s = (prec ? toFixedFix(n, prec) : <span class="hljs-string">''</span> + <span class="hljs-built_in">Math</span>.round(n)).split(<span class="hljs-string">'.'</span>); - <span class="hljs-keyword">if</span> (s[<span class="hljs-number">0</span>].length > <span class="hljs-number">3</span>) { - s[<span class="hljs-number">0</span>] = s[<span class="hljs-number">0</span>].replace(<span class="hljs-regexp">/\B(?=(?:\d{3})+(?!\d))/g</span>, sep); - } - <span class="hljs-keyword">if</span> ((s[<span class="hljs-number">1</span>] || <span class="hljs-string">''</span>).length < prec) { - s[<span class="hljs-number">1</span>] = s[<span class="hljs-number">1</span>] || <span class="hljs-string">''</span>; - s[<span class="hljs-number">1</span>] += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(prec - s[<span class="hljs-number">1</span>].length + <span class="hljs-number">1</span>).join(<span class="hljs-string">'0'</span>); - } - <span class="hljs-keyword">return</span> s.join(dec); - }, - - trim: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span>|| value === <span class="hljs-literal">null</span>){ - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">var</span> str = Twig.filters.escape( <span class="hljs-string">''</span> + value ), - whitespace; - <span class="hljs-keyword">if</span> ( params && params[<span class="hljs-number">0</span>] ) { - whitespace = <span class="hljs-string">''</span> + params[<span class="hljs-number">0</span>]; - } <span class="hljs-keyword">else</span> { - whitespace = <span class="hljs-string">' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'</span>; - } - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < str.length; i++) { - <span class="hljs-keyword">if</span> (whitespace.indexOf(str.charAt(i)) === <span class="hljs-number">-1</span>) { - str = str.substring(i); - <span class="hljs-keyword">break</span>; - } - } - <span class="hljs-keyword">for</span> (i = str.length - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i--) { - <span class="hljs-keyword">if</span> (whitespace.indexOf(str.charAt(i)) === <span class="hljs-number">-1</span>) { - str = str.substring(<span class="hljs-number">0</span>, i + <span class="hljs-number">1</span>); - <span class="hljs-keyword">break</span>; - } - } - <span class="hljs-keyword">return</span> whitespace.indexOf(str.charAt(<span class="hljs-number">0</span>)) === <span class="hljs-number">-1</span> ? str : <span class="hljs-string">''</span>; - }, - - truncate: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">var</span> length = <span class="hljs-number">30</span>, - preserve = <span class="hljs-literal">false</span>, - separator = <span class="hljs-string">'...'</span>; - - value = value + <span class="hljs-string">''</span>; - <span class="hljs-keyword">if</span> (params) { - <span class="hljs-keyword">if</span> (params[<span class="hljs-number">0</span>]) { - length = params[<span class="hljs-number">0</span>]; - } - <span class="hljs-keyword">if</span> (params[<span class="hljs-number">1</span>]) { - preserve = params[<span class="hljs-number">1</span>]; - } - <span class="hljs-keyword">if</span> (params[<span class="hljs-number">2</span>]) { - separator = params[<span class="hljs-number">2</span>]; - } - } - - <span class="hljs-keyword">if</span> (value.length > length) { - - <span class="hljs-keyword">if</span> (preserve) { - length = value.indexOf(<span class="hljs-string">' '</span>, length); - <span class="hljs-keyword">if</span> (length === <span class="hljs-number">-1</span>) { - <span class="hljs-keyword">return</span> value; - } - } - - value = value.substr(<span class="hljs-number">0</span>, length) + separator; - } - - <span class="hljs-keyword">return</span> value; - }, - - slice: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">return</span>; - } - <span class="hljs-keyword">if</span> (params === <span class="hljs-literal">undefined</span> || params.length < <span class="hljs-number">1</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"slice filter expects at least 1 argument"</span>); - }</pre></div></div> - - </li> - - - <li id="section-314"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-314">¶</a> - </div> - <p>default to start of string</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> start = params[<span class="hljs-number">0</span>] || <span class="hljs-number">0</span>;</pre></div></div> - - </li> - - - <li id="section-315"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-315">¶</a> - </div> - <p>default to length of string</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> length = params.length > <span class="hljs-number">1</span> ? params[<span class="hljs-number">1</span>] : value.length;</pre></div></div> - - </li> - - - <li id="section-316"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-316">¶</a> - </div> - <p>handle negative start values</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> startIndex = start >= <span class="hljs-number">0</span> ? start : <span class="hljs-built_in">Math</span>.max( value.length + start, <span class="hljs-number">0</span> ); - - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Array"</span>, value)) { - <span class="hljs-keyword">var</span> output = []; - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = startIndex; i < startIndex + length && i < value.length; i++) { - output.push(value[i]); - } - <span class="hljs-keyword">return</span> output; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"String"</span>, value)) { - <span class="hljs-keyword">return</span> value.substr(startIndex, length); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"slice filter expects value to be an array or string"</span>); - } - }, - - abs: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">return</span>; - } - - <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.abs(value); - }, - - first: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Array"</span>, value)) { - <span class="hljs-keyword">return</span> value[<span class="hljs-number">0</span>]; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (is(<span class="hljs-string">"Object"</span>, value)) { - <span class="hljs-keyword">if</span> (<span class="hljs-string">'_keys'</span> <span class="hljs-keyword">in</span> value) { - <span class="hljs-keyword">return</span> value[value._keys[<span class="hljs-number">0</span>]]; - } - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">typeof</span> value === <span class="hljs-string">"string"</span> ) { - <span class="hljs-keyword">return</span> value.substr(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>); - } - - <span class="hljs-keyword">return</span>; - }, - - split: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">undefined</span> || value === <span class="hljs-literal">null</span>) { - <span class="hljs-keyword">return</span>; - } - <span class="hljs-keyword">if</span> (params === <span class="hljs-literal">undefined</span> || params.length < <span class="hljs-number">1</span> || params.length > <span class="hljs-number">2</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"split filter expects 1 or 2 argument"</span>); - } - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"String"</span>, value)) { - <span class="hljs-keyword">var</span> delimiter = params[<span class="hljs-number">0</span>], - limit = params[<span class="hljs-number">1</span>], - split = value.split(delimiter); - - <span class="hljs-keyword">if</span> (limit === <span class="hljs-literal">undefined</span>) { - - <span class="hljs-keyword">return</span> split; - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (limit < <span class="hljs-number">0</span>) { - - <span class="hljs-keyword">return</span> value.split(delimiter, split.length + limit); - - } <span class="hljs-keyword">else</span> { - - <span class="hljs-keyword">var</span> limitedSplit = []; - - <span class="hljs-keyword">if</span> (delimiter == <span class="hljs-string">''</span>) {</pre></div></div> - - </li> - - - <li id="section-317"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-317">¶</a> - </div> - <p>empty delimiter -“aabbcc”|split(‘’, 2) - -> [‘aa’, ‘bb’, ‘cc’]</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">while</span>(split.length > <span class="hljs-number">0</span>) { - <span class="hljs-keyword">var</span> temp = <span class="hljs-string">""</span>; - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>; i<limit && split.length > <span class="hljs-number">0</span>; i++) { - temp += split.shift(); - } - limitedSplit.push(temp); - } - - } <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-318"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-318">¶</a> - </div> - <p>non-empty delimiter -“one,two,three,four,five”|split(‘,’, 3) - -> [‘one’, ‘two’, ‘three,four,five’]</p> - - </div> - - <div class="content"><div class='highlight'><pre> - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>; i<limit<span class="hljs-number">-1</span> && split.length > <span class="hljs-number">0</span>; i++) { - limitedSplit.push(split.shift()); - } - - <span class="hljs-keyword">if</span> (split.length > <span class="hljs-number">0</span>) { - limitedSplit.push(split.join(delimiter)); - } - } - - <span class="hljs-keyword">return</span> limitedSplit; - } - - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"split filter expects value to be a string"</span>); - } - }, - last: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">'Object'</span>, value)) { - <span class="hljs-keyword">var</span> keys; - - <span class="hljs-keyword">if</span> (value._keys === <span class="hljs-literal">undefined</span>) { - keys = <span class="hljs-built_in">Object</span>.keys(value); - } <span class="hljs-keyword">else</span> { - keys = value._keys; - } - - <span class="hljs-keyword">return</span> value[keys[keys.length - <span class="hljs-number">1</span>]]; - }</pre></div></div> - - </li> - - - <li id="section-319"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-319">¶</a> - </div> - <p>string|array</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> value[value.length - <span class="hljs-number">1</span>]; - }, - raw: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> Twig.Markup(value); - }, - batch: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">items, params</span>) </span>{ - <span class="hljs-keyword">var</span> size = params.shift(), - fill = params.shift(), - result, - last, - missing; - - <span class="hljs-keyword">if</span> (!Twig.lib.is(<span class="hljs-string">"Array"</span>, items)) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"batch filter expects items to be an array"</span>); - } - - <span class="hljs-keyword">if</span> (!Twig.lib.is(<span class="hljs-string">"Number"</span>, size)) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"batch filter expects size to be a number"</span>); - } - - size = <span class="hljs-built_in">Math</span>.ceil(size); - - result = Twig.lib.chunkArray(items, size); - - <span class="hljs-keyword">if</span> (fill && items.length % size != <span class="hljs-number">0</span>) { - last = result.pop(); - missing = size - last.length; - - <span class="hljs-keyword">while</span> (missing--) { - last.push(fill); - } - - result.push(last); - } - - <span class="hljs-keyword">return</span> result; - }, - round: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - params = params || []; - - <span class="hljs-keyword">var</span> precision = params.length > <span class="hljs-number">0</span> ? params[<span class="hljs-number">0</span>] : <span class="hljs-number">0</span>, - method = params.length > <span class="hljs-number">1</span> ? params[<span class="hljs-number">1</span>] : <span class="hljs-string">"common"</span>; - - value = <span class="hljs-built_in">parseFloat</span>(value); - - <span class="hljs-keyword">if</span>(precision && !Twig.lib.is(<span class="hljs-string">"Number"</span>, precision)) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"round filter expects precision to be a number"</span>); - } - - <span class="hljs-keyword">if</span> (method === <span class="hljs-string">"common"</span>) { - <span class="hljs-keyword">return</span> Twig.lib.round(value, precision); - } - - <span class="hljs-keyword">if</span>(!Twig.lib.is(<span class="hljs-string">"Function"</span>, <span class="hljs-built_in">Math</span>[method])) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"round filter expects method to be 'floor', 'ceil', or 'common'"</span>); - } - - <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>[method](value * <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">10</span>, precision)) / <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">10</span>, precision); - } - }; - - Twig.filter = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">filter, value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (!Twig.filters[filter]) { - <span class="hljs-keyword">throw</span> <span class="hljs-string">"Unable to find filter "</span> + filter; - } - <span class="hljs-keyword">return</span> Twig.filters[filter].apply(<span class="hljs-keyword">this</span>, [value, params]); - }; - - Twig.filter.extend = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">filter, definition</span>) </span>{ - Twig.filters[filter] = definition; - }; - - <span class="hljs-keyword">return</span> Twig; - -})(Twig || { });</pre></div></div> - - </li> - - - <li id="section-320"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-320">¶</a> - </div> - <pre><code>Twig.js - <span class="hljs-number">2012</span> Hadrien Lanneau -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-321"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-321">¶</a> - </div> - <h2 id="twig-functions-js">twig.functions.js</h2> -<p>This file handles parsing filters.</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ - <span class="hljs-comment">/** - * @constant - * @type {string} - */</span> - <span class="hljs-keyword">var</span> TEMPLATE_NOT_FOUND_MESSAGE = <span class="hljs-string">'Template "{name}" is not defined.'</span>;</pre></div></div> - - </li> - - - <li id="section-322"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-322">¶</a> - </div> - <p>Determine object type</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">is</span>(<span class="hljs-params">type, obj</span>) </span>{ - <span class="hljs-keyword">var</span> clas = <span class="hljs-built_in">Object</span>.prototype.toString.call(obj).slice(<span class="hljs-number">8</span>, <span class="hljs-number">-1</span>); - <span class="hljs-keyword">return</span> obj !== <span class="hljs-literal">undefined</span> && obj !== <span class="hljs-literal">null</span> && clas === type; - } - - Twig.functions = {</pre></div></div> - - </li> - - - <li id="section-323"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-323">¶</a> - </div> - <p> attribute, block, constant, date, dump, parent, random,.</p> - - </div> - - </li> - - - <li id="section-324"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-324">¶</a> - </div> - <p>Range function from <a href="http://phpjs.org/functions/range:499">http://phpjs.org/functions/range:499</a> -Used under an MIT License</p> - - </div> - - <div class="content"><div class='highlight'><pre> range: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">low, high, step</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-325"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-325">¶</a> - </div> - <p><a href="http://kevin.vanzonneveld.net">http://kevin.vanzonneveld.net</a></p> -<ul> -<li>original by: Waldo Malqui Silva</li> -<li>example 1: range ( 0, 12 );</li> -<li>returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]</li> -<li>example 2: range( 0, 100, 10 );</li> -<li>returns 2: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]</li> -<li>example 3: range( ‘a’, ‘i’ );</li> -<li>returns 3: [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’]</li> -<li>example 4: range( ‘c’, ‘a’ );</li> -<li>returns 4: [‘c’, ‘b’, ‘a’]</li> -</ul> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> matrix = []; - <span class="hljs-keyword">var</span> inival, endval, plus; - <span class="hljs-keyword">var</span> walker = step || <span class="hljs-number">1</span>; - <span class="hljs-keyword">var</span> chars = <span class="hljs-literal">false</span>; - - <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(low) && !<span class="hljs-built_in">isNaN</span>(high)) { - inival = <span class="hljs-built_in">parseInt</span>(low, <span class="hljs-number">10</span>); - endval = <span class="hljs-built_in">parseInt</span>(high, <span class="hljs-number">10</span>); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(low) && <span class="hljs-built_in">isNaN</span>(high)) { - chars = <span class="hljs-literal">true</span>; - inival = low.charCodeAt(<span class="hljs-number">0</span>); - endval = high.charCodeAt(<span class="hljs-number">0</span>); - } <span class="hljs-keyword">else</span> { - inival = (<span class="hljs-built_in">isNaN</span>(low) ? <span class="hljs-number">0</span> : low); - endval = (<span class="hljs-built_in">isNaN</span>(high) ? <span class="hljs-number">0</span> : high); - } - - plus = ((inival > endval) ? <span class="hljs-literal">false</span> : <span class="hljs-literal">true</span>); - <span class="hljs-keyword">if</span> (plus) { - <span class="hljs-keyword">while</span> (inival <= endval) { - matrix.push(((chars) ? <span class="hljs-built_in">String</span>.fromCharCode(inival) : inival)); - inival += walker; - } - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">while</span> (inival >= endval) { - matrix.push(((chars) ? <span class="hljs-built_in">String</span>.fromCharCode(inival) : inival)); - inival -= walker; - } - } - - <span class="hljs-keyword">return</span> matrix; - }, - cycle: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">arr, i</span>) </span>{ - <span class="hljs-keyword">var</span> pos = i % arr.length; - <span class="hljs-keyword">return</span> arr[pos]; - }, - dump: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">var</span> EOL = <span class="hljs-string">'\n'</span>, - indentChar = <span class="hljs-string">' '</span>, - indentTimes = <span class="hljs-number">0</span>, - out = <span class="hljs-string">''</span>, - args = <span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>), - indent = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">times</span>) </span>{ - <span class="hljs-keyword">var</span> ind = <span class="hljs-string">''</span>; - <span class="hljs-keyword">while</span> (times > <span class="hljs-number">0</span>) { - times--; - ind += indentChar; - } - <span class="hljs-keyword">return</span> ind; - }, - displayVar = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">variable</span>) </span>{ - out += indent(indentTimes); - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span>(variable) === <span class="hljs-string">'object'</span>) { - dumpVar(variable); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span>(variable) === <span class="hljs-string">'function'</span>) { - out += <span class="hljs-string">'function()'</span> + EOL; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span>(variable) === <span class="hljs-string">'string'</span>) { - out += <span class="hljs-string">'string('</span> + variable.length + <span class="hljs-string">') "'</span> + variable + <span class="hljs-string">'"'</span> + EOL; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span>(variable) === <span class="hljs-string">'number'</span>) { - out += <span class="hljs-string">'number('</span> + variable + <span class="hljs-string">')'</span> + EOL; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span>(variable) === <span class="hljs-string">'boolean'</span>) { - out += <span class="hljs-string">'bool('</span> + variable + <span class="hljs-string">')'</span> + EOL; - } - }, - dumpVar = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">variable</span>) </span>{ - <span class="hljs-keyword">var</span> i; - <span class="hljs-keyword">if</span> (variable === <span class="hljs-literal">null</span>) { - out += <span class="hljs-string">'NULL'</span> + EOL; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (variable === <span class="hljs-literal">undefined</span>) { - out += <span class="hljs-string">'undefined'</span> + EOL; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> variable === <span class="hljs-string">'object'</span>) { - out += indent(indentTimes) + <span class="hljs-keyword">typeof</span>(variable); - indentTimes++; - out += <span class="hljs-string">'('</span> + (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">obj</span>) </span>{ - <span class="hljs-keyword">var</span> size = <span class="hljs-number">0</span>, key; - <span class="hljs-keyword">for</span> (key <span class="hljs-keyword">in</span> obj) { - <span class="hljs-keyword">if</span> (obj.hasOwnProperty(key)) { - size++; - } - } - <span class="hljs-keyword">return</span> size; - })(variable) + <span class="hljs-string">') {'</span> + EOL; - <span class="hljs-keyword">for</span> (i <span class="hljs-keyword">in</span> variable) { - out += indent(indentTimes) + <span class="hljs-string">'['</span> + i + <span class="hljs-string">']=> '</span> + EOL; - displayVar(variable[i]); - } - indentTimes--; - out += indent(indentTimes) + <span class="hljs-string">'}'</span> + EOL; - } <span class="hljs-keyword">else</span> { - displayVar(variable); - } - };</pre></div></div> - - </li> - - - <li id="section-326"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-326">¶</a> - </div> - <p>handle no argument case by dumping the entire render context</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (args.length == <span class="hljs-number">0</span>) args.push(<span class="hljs-keyword">this</span>.context); - - Twig.forEach(args, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">variable</span>) </span>{ - dumpVar(variable); - }); - - <span class="hljs-keyword">return</span> out; - }, - date: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">date, time</span>) </span>{ - <span class="hljs-keyword">var</span> dateObj; - <span class="hljs-keyword">if</span> (date === <span class="hljs-literal">undefined</span>) { - dateObj = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(); - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Date"</span>, date)) { - dateObj = date; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"String"</span>, date)) { - <span class="hljs-keyword">if</span> (date.match(<span class="hljs-regexp">/^[0-9]+$/</span>)) { - dateObj = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(date * <span class="hljs-number">1000</span>); - } - <span class="hljs-keyword">else</span> { - dateObj = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(Twig.lib.strtotime(date) * <span class="hljs-number">1000</span>); - } - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">"Number"</span>, date)) {</pre></div></div> - - </li> - - - <li id="section-327"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-327">¶</a> - </div> - <p>timestamp</p> - - </div> - - <div class="content"><div class='highlight'><pre> dateObj = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(date * <span class="hljs-number">1000</span>); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to parse date "</span> + date); - } - <span class="hljs-keyword">return</span> dateObj; - }, - block: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">block</span>) </span>{ - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.originalBlockTokens[block]) { - <span class="hljs-keyword">return</span> Twig.logic.parse.apply(<span class="hljs-keyword">this</span>, [<span class="hljs-keyword">this</span>.originalBlockTokens[block], <span class="hljs-keyword">this</span>.context]).output; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.blocks[block]; - } - }, - parent: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{</pre></div></div> - - </li> - - - <li id="section-328"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-328">¶</a> - </div> - <p>Add a placeholder</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> Twig.placeholders.parent; - }, - attribute: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">object, method, params</span>) </span>{ - <span class="hljs-keyword">if</span> (Twig.lib.is(<span class="hljs-string">'Object'</span>, object)) { - <span class="hljs-keyword">if</span> (object.hasOwnProperty(method)) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> object[method] === <span class="hljs-string">"function"</span>) { - <span class="hljs-keyword">return</span> object[method].apply(<span class="hljs-literal">undefined</span>, params); - } - <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> object[method]; - } - } - }</pre></div></div> - - </li> - - - <li id="section-329"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-329">¶</a> - </div> - <p>Array will return element 0-index</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> object[method] || <span class="hljs-literal">undefined</span>; - }, - max: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">values</span>) </span>{ - <span class="hljs-keyword">if</span>(Twig.lib.is(<span class="hljs-string">"Object"</span>, values)) { - <span class="hljs-keyword">delete</span> values[<span class="hljs-string">"_keys"</span>]; - <span class="hljs-keyword">return</span> Twig.lib.max(values); - } - - <span class="hljs-keyword">return</span> Twig.lib.max.apply(<span class="hljs-literal">null</span>, <span class="hljs-built_in">arguments</span>); - }, - min: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">values</span>) </span>{ - <span class="hljs-keyword">if</span>(Twig.lib.is(<span class="hljs-string">"Object"</span>, values)) { - <span class="hljs-keyword">delete</span> values[<span class="hljs-string">"_keys"</span>]; - <span class="hljs-keyword">return</span> Twig.lib.min(values); - } - - <span class="hljs-keyword">return</span> Twig.lib.min.apply(<span class="hljs-literal">null</span>, <span class="hljs-built_in">arguments</span>); - }, - template_from_string: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">template</span>) </span>{ - <span class="hljs-keyword">if</span> (template === <span class="hljs-literal">undefined</span>) { - template = <span class="hljs-string">''</span>; - } - <span class="hljs-keyword">return</span> Twig.Templates.parsers.twig({ - options: <span class="hljs-keyword">this</span>.options, - data: template - }); - }, - random: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">var</span> LIMIT_INT31 = <span class="hljs-number">0x80000000</span>; - - <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRandomNumber</span>(<span class="hljs-params">n</span>) </span>{ - <span class="hljs-keyword">var</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * LIMIT_INT31); - <span class="hljs-keyword">var</span> limits = [<span class="hljs-number">0</span>, n]; - <span class="hljs-keyword">var</span> min = <span class="hljs-built_in">Math</span>.min.apply(<span class="hljs-literal">null</span>, limits), - max = <span class="hljs-built_in">Math</span>.max.apply(<span class="hljs-literal">null</span>, limits); - <span class="hljs-keyword">return</span> min + <span class="hljs-built_in">Math</span>.floor((max - min + <span class="hljs-number">1</span>) * random / LIMIT_INT31); - } - - <span class="hljs-keyword">if</span>(Twig.lib.is(<span class="hljs-string">"Number"</span>, value)) { - <span class="hljs-keyword">return</span> getRandomNumber(value); - } - - <span class="hljs-keyword">if</span>(Twig.lib.is(<span class="hljs-string">"String"</span>, value)) { - <span class="hljs-keyword">return</span> value.charAt(getRandomNumber(value.length<span class="hljs-number">-1</span>)); - } - - <span class="hljs-keyword">if</span>(Twig.lib.is(<span class="hljs-string">"Array"</span>, value)) { - <span class="hljs-keyword">return</span> value[getRandomNumber(value.length<span class="hljs-number">-1</span>)]; - } - - <span class="hljs-keyword">if</span>(Twig.lib.is(<span class="hljs-string">"Object"</span>, value)) { - <span class="hljs-keyword">var</span> keys = <span class="hljs-built_in">Object</span>.keys(value); - <span class="hljs-keyword">return</span> value[keys[getRandomNumber(keys.length<span class="hljs-number">-1</span>)]]; - } - - <span class="hljs-keyword">return</span> getRandomNumber(LIMIT_INT31<span class="hljs-number">-1</span>); - }, - - <span class="hljs-comment">/** - * Returns the content of a template without rendering it - * @param {string} name - * @param {boolean} [ignore_missing=false] - * @returns {string} - */</span> - source: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">name, ignore_missing</span>) </span>{ - <span class="hljs-keyword">var</span> templateSource; - <span class="hljs-keyword">var</span> templateFound = <span class="hljs-literal">false</span>; - <span class="hljs-keyword">var</span> isNodeEnvironment = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">'undefined'</span> && <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span>.exports !== <span class="hljs-string">'undefined'</span> && <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> === <span class="hljs-string">'undefined'</span>; - <span class="hljs-keyword">var</span> loader; - <span class="hljs-keyword">var</span> path;</pre></div></div> - - </li> - - - <li id="section-330"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-330">¶</a> - </div> - <p>if we are running in a node.js environment, set the loader to ‘fs’ and ensure the -path is relative to the CWD of the running script -else, set the loader to ‘ajax’ and set the path to the value of name</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (isNodeEnvironment) { - loader = <span class="hljs-string">'fs'</span>; - path = __dirname + <span class="hljs-string">'/'</span> + name; - } <span class="hljs-keyword">else</span> { - loader = <span class="hljs-string">'ajax'</span>; - path = name; - }</pre></div></div> - - </li> - - - <li id="section-331"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-331">¶</a> - </div> - <p>build the params object</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> params = { - id: name, - path: path, - method: loader, - parser: <span class="hljs-string">'source'</span>, - <span class="hljs-keyword">async</span>: <span class="hljs-literal">false</span>, - fetchTemplateSource: <span class="hljs-literal">true</span> - };</pre></div></div> - - </li> - - - <li id="section-332"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-332">¶</a> - </div> - <p>default ignore_missing to false</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> ignore_missing === <span class="hljs-string">'undefined'</span>) { - ignore_missing = <span class="hljs-literal">false</span>; - }</pre></div></div> - - </li> - - - <li id="section-333"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-333">¶</a> - </div> - <p>try to load the remote template</p> -<p>on exception, log it</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">try</span> { - templateSource = Twig.Templates.loadRemote(name, params);</pre></div></div> - - </li> - - - <li id="section-334"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-334">¶</a> - </div> - <p>if the template is undefined or null, set the template to an empty string and do NOT flip the -boolean indicating we found the template</p> -<p>else, all is good! flip the boolean indicating we found the template</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> templateSource === <span class="hljs-string">'undefined'</span> || templateSource === <span class="hljs-literal">null</span>) { - templateSource = <span class="hljs-string">''</span>; - } <span class="hljs-keyword">else</span> { - templateFound = <span class="hljs-literal">true</span>; - } - } <span class="hljs-keyword">catch</span> (e) { - Twig.log.debug(<span class="hljs-string">'Twig.functions.source: '</span>, <span class="hljs-string">'Problem loading template '</span>, e); - }</pre></div></div> - - </li> - - - <li id="section-335"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-335">¶</a> - </div> - <p>if the template was NOT found AND we are not ignoring missing templates, return the same message -that is returned by the PHP implementation of the twig source() function</p> -<p>else, return the template source</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!templateFound && !ignore_missing) { - <span class="hljs-keyword">return</span> TEMPLATE_NOT_FOUND_MESSAGE.replace(<span class="hljs-string">'{name}'</span>, name); - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">return</span> templateSource; - } - } - }; - - Twig._function = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_function, value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (!Twig.functions[_function]) { - <span class="hljs-keyword">throw</span> <span class="hljs-string">"Unable to find function "</span> + _function; - } - <span class="hljs-keyword">return</span> Twig.functions[_function](value, params); - }; - - Twig._function.extend = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_function, definition</span>) </span>{ - Twig.functions[_function] = definition; - }; - - <span class="hljs-keyword">return</span> Twig; - -})(Twig || { });</pre></div></div> - - </li> - - - <li id="section-336"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-336">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-337"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-337">¶</a> - </div> - <h2 id="twig-path-js">twig.path.js</h2> -<p>This file handles path parsing</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>; - - <span class="hljs-comment">/** - * Namespace for path handling. - */</span> - Twig.path = {}; - - <span class="hljs-comment">/** - * Generate the canonical version of a url based on the given base path and file path and in - * the previously registered namespaces. - * - * @param {string} template The Twig Template - * @param {string} file The file path, may be relative and may contain namespaces. - * - * @return {string} The canonical version of the path - */</span> - Twig.path.parsePath = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">template, file</span>) </span>{ - <span class="hljs-keyword">var</span> namespaces = <span class="hljs-literal">null</span>, - file = file || <span class="hljs-string">""</span>; - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> template === <span class="hljs-string">'object'</span> && <span class="hljs-keyword">typeof</span> template.options === <span class="hljs-string">'object'</span>) { - namespaces = template.options.namespaces; - } - - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> namespaces === <span class="hljs-string">'object'</span> && (file.indexOf(<span class="hljs-string">'::'</span>) > <span class="hljs-number">0</span>) || file.indexOf(<span class="hljs-string">'@'</span>) >= <span class="hljs-number">0</span>){ - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> k <span class="hljs-keyword">in</span> namespaces){ - <span class="hljs-keyword">if</span> (namespaces.hasOwnProperty(k)) { - file = file.replace(k + <span class="hljs-string">'::'</span>, namespaces[k]); - file = file.replace(<span class="hljs-string">'@'</span> + k, namespaces[k]); - } - } - - <span class="hljs-keyword">return</span> file; - } - - <span class="hljs-keyword">return</span> Twig.path.relativePath(template, file); - }; - - <span class="hljs-comment">/** - * Generate the relative canonical version of a url based on the given base path and file path. - * - * @param {Twig.Template} template The Twig.Template. - * @param {string} file The file path, relative to the base path. - * - * @return {string} The canonical version of the path. - */</span> - Twig.path.relativePath = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">template, file</span>) </span>{ - <span class="hljs-keyword">var</span> base, - base_path, - sep_chr = <span class="hljs-string">"/"</span>, - new_path = [], - file = file || <span class="hljs-string">""</span>, - val; - - <span class="hljs-keyword">if</span> (template.url) { - <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> template.base !== <span class="hljs-string">'undefined'</span>) { - base = template.base + ((template.base.charAt(template.base.length<span class="hljs-number">-1</span>) === <span class="hljs-string">'/'</span>) ? <span class="hljs-string">''</span> : <span class="hljs-string">'/'</span>); - } <span class="hljs-keyword">else</span> { - base = template.url; - } - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (template.path) {</pre></div></div> - - </li> - - - <li id="section-338"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-338">¶</a> - </div> - <p>Get the system-specific path separator</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>), - sep = path.sep || sep_chr, - relative = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">"^\\.{1,2}"</span> + sep.replace(<span class="hljs-string">"\\"</span>, <span class="hljs-string">"\\\\"</span>)); - file = file.replace(<span class="hljs-regexp">/\//g</span>, sep); - - <span class="hljs-keyword">if</span> (template.base !== <span class="hljs-literal">undefined</span> && file.match(relative) == <span class="hljs-literal">null</span>) { - file = file.replace(template.base, <span class="hljs-string">''</span>); - base = template.base + sep; - } <span class="hljs-keyword">else</span> { - base = path.normalize(template.path); - } - - base = base.replace(sep+sep, sep); - sep_chr = sep; - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((template.name || template.id) && template.method && template.method !== <span class="hljs-string">'fs'</span> && template.method !== <span class="hljs-string">'ajax'</span>) {</pre></div></div> - - </li> - - - <li id="section-339"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-339">¶</a> - </div> - <p>Custom registered loader</p> - - </div> - - <div class="content"><div class='highlight'><pre> base = template.base || template.name || template.id; - } <span class="hljs-keyword">else</span> { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Cannot extend an inline template."</span>); - } - - base_path = base.split(sep_chr);</pre></div></div> - - </li> - - - <li id="section-340"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-340">¶</a> - </div> - <p>Remove file from url</p> - - </div> - - <div class="content"><div class='highlight'><pre> base_path.pop(); - base_path = base_path.concat(file.split(sep_chr)); - - <span class="hljs-keyword">while</span> (base_path.length > <span class="hljs-number">0</span>) { - val = base_path.shift(); - <span class="hljs-keyword">if</span> (val == <span class="hljs-string">"."</span>) {</pre></div></div> - - </li> - - - <li id="section-341"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-341">¶</a> - </div> - <p>Ignore</p> - - </div> - - <div class="content"><div class='highlight'><pre> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (val == <span class="hljs-string">".."</span> && new_path.length > <span class="hljs-number">0</span> && new_path[new_path.length<span class="hljs-number">-1</span>] != <span class="hljs-string">".."</span>) { - new_path.pop(); - } <span class="hljs-keyword">else</span> { - new_path.push(val); - } - } - - <span class="hljs-keyword">return</span> new_path.join(sep_chr); - }; - - <span class="hljs-keyword">return</span> Twig; -}) (Twig || { });</pre></div></div> - - </li> - - - <li id="section-342"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-342">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-343"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-343">¶</a> - </div> - <h2 id="twig-tests-js">twig.tests.js</h2> -<p>This file handles expression tests. (is empty, is not defined, etc…)</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>; - Twig.tests = { - empty: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">if</span> (value === <span class="hljs-literal">null</span> || value === <span class="hljs-literal">undefined</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;</pre></div></div> - - </li> - - - <li id="section-344"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-344">¶</a> - </div> - <p>Handler numbers</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> value === <span class="hljs-string">"number"</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; <span class="hljs-comment">// numbers are never "empty"</span></pre></div></div> - - </li> - - - <li id="section-345"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-345">¶</a> - </div> - <p>Handle strings and arrays</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (value.length && value.length > <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</pre></div></div> - - </li> - - - <li id="section-346"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-346">¶</a> - </div> - <p>Handle objects</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> key <span class="hljs-keyword">in</span> value) { - <span class="hljs-keyword">if</span> (value.hasOwnProperty(key)) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; - } - <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; - }, - odd: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> value % <span class="hljs-number">2</span> === <span class="hljs-number">1</span>; - }, - even: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> value % <span class="hljs-number">2</span> === <span class="hljs-number">0</span>; - }, - divisibleby: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">return</span> value % params[<span class="hljs-number">0</span>] === <span class="hljs-number">0</span>; - }, - defined: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> value !== <span class="hljs-literal">undefined</span>; - }, - none: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> value === <span class="hljs-literal">null</span>; - }, - <span class="hljs-string">'null'</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.none(value); <span class="hljs-comment">// Alias of none</span> - }, - sameas: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value, params</span>) </span>{ - <span class="hljs-keyword">return</span> value === params[<span class="hljs-number">0</span>]; - }, - iterable: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">value</span>) </span>{ - <span class="hljs-keyword">return</span> value && (Twig.lib.is(<span class="hljs-string">"Array"</span>, value) || Twig.lib.is(<span class="hljs-string">"Object"</span>, value)); - } - <span class="hljs-comment">/* - constant ? - */</span> - }; - - Twig.test = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">test, value, params</span>) </span>{ - <span class="hljs-keyword">if</span> (!Twig.tests[test]) { - <span class="hljs-keyword">throw</span> <span class="hljs-string">"Test "</span> + test + <span class="hljs-string">" is not defined."</span>; - } - <span class="hljs-keyword">return</span> Twig.tests[test](value, params); - }; - - Twig.test.extend = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">test, definition</span>) </span>{ - Twig.tests[test] = definition; - }; - - <span class="hljs-keyword">return</span> Twig; -})( Twig || { } );</pre></div></div> - - </li> - - - <li id="section-347"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-347">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-348"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-348">¶</a> - </div> - <h2 id="twig-exports-js">twig.exports.js</h2> -<p>This file provides extension points and other hooks into the twig functionality.</p> - - </div> - - <div class="content"><div class='highlight'><pre> -<span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ -<span class="hljs-meta"> "use strict"</span>; - Twig.exports = { - VERSION: Twig.VERSION - }; - - <span class="hljs-comment">/** - * 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. - */</span> - Twig.exports.twig = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">twig</span>(<span class="hljs-params">params</span>) </span>{ -<span class="hljs-meta"> 'use strict'</span>; - <span class="hljs-keyword">var</span> id = params.id, - options = { - strict_variables: params.strict_variables || <span class="hljs-literal">false</span>,</pre></div></div> - - </li> - - - <li id="section-349"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-349">¶</a> - </div> - <p>TODO: turn autoscape on in the next major version</p> - - </div> - - <div class="content"><div class='highlight'><pre> autoescape: params.autoescape != <span class="hljs-literal">null</span> && params.autoescape || <span class="hljs-literal">false</span>, - allowInlineIncludes: params.allowInlineIncludes || <span class="hljs-literal">false</span>, - rethrow: params.rethrow || <span class="hljs-literal">false</span>, - namespaces: params.namespaces - }; - - <span class="hljs-keyword">if</span> (Twig.cache && id) { - Twig.validateId(id); - } - - <span class="hljs-keyword">if</span> (params.debug !== <span class="hljs-literal">undefined</span>) { - Twig.debug = params.debug; - } - <span class="hljs-keyword">if</span> (params.trace !== <span class="hljs-literal">undefined</span>) { - Twig.trace = params.trace; - } - - <span class="hljs-keyword">if</span> (params.data !== <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">return</span> Twig.Templates.parsers.twig({ - data: params.data, - path: params.hasOwnProperty(<span class="hljs-string">'path'</span>) ? params.path : <span class="hljs-literal">undefined</span>, - <span class="hljs-built_in">module</span>: params.module, - id: id, - options: options - }); - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (params.ref !== <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">if</span> (params.id !== <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Both ref and id cannot be set on a twig.js template."</span>); - } - <span class="hljs-keyword">return</span> Twig.Templates.load(params.ref); - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (params.method !== <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">if</span> (!Twig.Templates.isRegisteredLoader(params.method)) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">'Loader for "'</span> + params.method + <span class="hljs-string">'" is not defined.'</span>); - } - <span class="hljs-keyword">return</span> Twig.Templates.loadRemote(params.name || params.href || params.path || id || <span class="hljs-literal">undefined</span>, { - id: id, - method: params.method, - parser: params.parser || <span class="hljs-string">'twig'</span>, - base: params.base, - <span class="hljs-built_in">module</span>: params.module, - precompiled: params.precompiled, - <span class="hljs-keyword">async</span>: params.async, - options: options - - }, params.load, params.error); - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (params.href !== <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">return</span> Twig.Templates.loadRemote(params.href, { - id: id, - method: <span class="hljs-string">'ajax'</span>, - parser: params.parser || <span class="hljs-string">'twig'</span>, - base: params.base, - <span class="hljs-built_in">module</span>: params.module, - precompiled: params.precompiled, - <span class="hljs-keyword">async</span>: params.async, - options: options - - }, params.load, params.error); - - } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (params.path !== <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">return</span> Twig.Templates.loadRemote(params.path, { - id: id, - method: <span class="hljs-string">'fs'</span>, - parser: params.parser || <span class="hljs-string">'twig'</span>, - base: params.base, - <span class="hljs-built_in">module</span>: params.module, - precompiled: params.precompiled, - <span class="hljs-keyword">async</span>: params.async, - options: options - - }, params.load, params.error); - } - };</pre></div></div> - - </li> - - - <li id="section-350"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-350">¶</a> - </div> - <p>Extend Twig with a new filter.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.extendFilter = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">filter, definition</span>) </span>{ - Twig.filter.extend(filter, definition); - };</pre></div></div> - - </li> - - - <li id="section-351"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-351">¶</a> - </div> - <p>Extend Twig with a new function.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.extendFunction = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fn, definition</span>) </span>{ - Twig._function.extend(fn, definition); - };</pre></div></div> - - </li> - - - <li id="section-352"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-352">¶</a> - </div> - <p>Extend Twig with a new test.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.extendTest = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">test, definition</span>) </span>{ - Twig.test.extend(test, definition); - };</pre></div></div> - - </li> - - - <li id="section-353"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-353">¶</a> - </div> - <p>Extend Twig with a new definition.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.extendTag = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">definition</span>) </span>{ - Twig.logic.extend(definition); - };</pre></div></div> - - </li> - - - <li id="section-354"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-354">¶</a> - </div> - <p>Provide an environment for extending Twig core. -Calls fn with the internal Twig object.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.extend = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fn</span>) </span>{ - fn(Twig); - }; - - - <span class="hljs-comment">/** - * Provide an extension for use with express 2. - * - * @param {string} markup The template markup. - * @param {array} options The express options. - * - * @return {string} The rendered template. - */</span> - Twig.exports.compile = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">markup, options</span>) </span>{ - <span class="hljs-keyword">var</span> id = options.filename, - path = options.filename, - template;</pre></div></div> - - </li> - - - <li id="section-355"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-355">¶</a> - </div> - <p>Try to load the template from the cache</p> - - </div> - - <div class="content"><div class='highlight'><pre> template = <span class="hljs-keyword">new</span> Twig.Template({ - data: markup, - path: path, - id: id, - options: options.settings[<span class="hljs-string">'twig options'</span>] - }); <span class="hljs-comment">// Twig.Templates.load(id) ||</span> - - <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{ - <span class="hljs-keyword">return</span> template.render(context); - }; - }; - - <span class="hljs-comment">/** - * Provide an extension for use with express 3. - * - * @param {string} path The location of the template file on disk. - * @param {Object|Function} The options or callback. - * @param {Function} fn callback. - * - * @throws Twig.Error - */</span> - Twig.exports.renderFile = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">path, options, fn</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-356"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-356">¶</a> - </div> - <p>handle callback in options</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> options === <span class="hljs-string">'function'</span>) { - fn = options; - options = {}; - } - - options = options || {}; - - <span class="hljs-keyword">var</span> settings = options.settings || {}; - - <span class="hljs-keyword">var</span> params = { - path: path, - base: settings.views, - load: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">template</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-357"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-357">¶</a> - </div> - <p>render and return template</p> - - </div> - - <div class="content"><div class='highlight'><pre> fn(<span class="hljs-literal">null</span>, template.render(options)); - } - };</pre></div></div> - - </li> - - - <li id="section-358"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-358">¶</a> - </div> - <p>mixin any options provided to the express app.</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> view_options = settings[<span class="hljs-string">'twig options'</span>]; - - <span class="hljs-keyword">if</span> (view_options) { - <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> option <span class="hljs-keyword">in</span> view_options) { - <span class="hljs-keyword">if</span> (view_options.hasOwnProperty(option)) { - params[option] = view_options[option]; - } - } - } - - Twig.exports.twig(params); - };</pre></div></div> - - </li> - - - <li id="section-359"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-359">¶</a> - </div> - <p>Express 3 handler</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.__express = Twig.exports.renderFile; - - <span class="hljs-comment">/** - * 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 - */</span> - Twig.exports.cache = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">cache</span>) </span>{ - Twig.cache = cache; - };</pre></div></div> - - </li> - - - <li id="section-360"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-360">¶</a> - </div> - <p>We need to export the path module so we can effectively test it</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.exports.path = Twig.path; - - <span class="hljs-keyword">return</span> Twig; -}) (Twig || { });</pre></div></div> - - </li> - - - <li id="section-361"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-361">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-362"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-362">¶</a> - </div> - <h2 id="twig-compiler-js">twig.compiler.js</h2> -<p>This file handles compiling templates into JS</p> - - </div> - - <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ - <span class="hljs-comment">/** - * Namespace for compilation. - */</span> - Twig.compiler = { - <span class="hljs-built_in">module</span>: {} - };</pre></div></div> - - </li> - - - <li id="section-363"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-363">¶</a> - </div> - <p>Compile a Twig Template to output.</p> - - </div> - - <div class="content"><div class='highlight'><pre> Twig.compiler.compile = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">template, options</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-364"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-364">¶</a> - </div> - <p>Get tokens</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> tokens = <span class="hljs-built_in">JSON</span>.stringify(template.tokens) - , id = template.id - , output; - - <span class="hljs-keyword">if</span> (options.module) { - <span class="hljs-keyword">if</span> (Twig.compiler.module[options.module] === <span class="hljs-literal">undefined</span>) { - <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to find module type "</span> + options.module); - } - output = Twig.compiler.module[options.module](id, tokens, options.twig); - } <span class="hljs-keyword">else</span> { - output = Twig.compiler.wrap(id, tokens); - } - <span class="hljs-keyword">return</span> output; - }; - - Twig.compiler.module = { - amd: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">id, tokens, pathToTwig</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-string">'define(["'</span> + pathToTwig + <span class="hljs-string">'"], function (Twig) {\n\tvar twig, templates;\ntwig = Twig.twig;\ntemplates = '</span> + Twig.compiler.wrap(id, tokens) + <span class="hljs-string">'\n\treturn templates;\n});'</span>; - } - , node: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">id, tokens</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-string">'var twig = require("twig").twig;\n'</span> - + <span class="hljs-string">'exports.template = '</span> + Twig.compiler.wrap(id, tokens) - } - , cjs2: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">id, tokens, pathToTwig</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-string">'module.declare([{ twig: "'</span> + pathToTwig + <span class="hljs-string">'" }], function (require, exports, module) {\n'</span> - + <span class="hljs-string">'\tvar twig = require("twig").twig;\n'</span> - + <span class="hljs-string">'\texports.template = '</span> + Twig.compiler.wrap(id, tokens) - + <span class="hljs-string">'\n});'</span> - } - }; - - Twig.compiler.wrap = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">id, tokens</span>) </span>{ - <span class="hljs-keyword">return</span> <span class="hljs-string">'twig({id:"'</span>+id.replace(<span class="hljs-string">'"'</span>, <span class="hljs-string">'\\"'</span>)+<span class="hljs-string">'", data:'</span>+tokens+<span class="hljs-string">', precompiled: true});\n'</span>; - }; - - <span class="hljs-keyword">return</span> Twig; -})(Twig || {});</pre></div></div> - - </li> - - - <li id="section-365"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-365">¶</a> - </div> - <pre><code>Twig.js -Available under the BSD <span class="hljs-number">2</span>-Clause License -https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> -</code></pre> - </div> - - </li> - - - <li id="section-366"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-366">¶</a> - </div> - <h2 id="twig-module-js">twig.module.js</h2> -<p>Provide a CommonJS/AMD/Node module export.</p> - - </div> - - <div class="content"><div class='highlight'><pre> -<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">'undefined'</span> && <span class="hljs-built_in">module</span>.declare) {</pre></div></div> - - </li> - - - <li id="section-367"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-367">¶</a> - </div> - <p>Provide a CommonJS Modules/2.0 draft 8 module</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-built_in">module</span>.declare([], <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">require, exports, module</span>) </span>{</pre></div></div> - - </li> - - - <li id="section-368"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-368">¶</a> - </div> - <p>Add exports from the Twig exports</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> (key <span class="hljs-keyword">in</span> Twig.exports) { - <span class="hljs-keyword">if</span> (Twig.exports.hasOwnProperty(key)) { - exports[key] = Twig.exports[key]; - } - } - }); -} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> define == <span class="hljs-string">'function'</span> && define.amd) { - define(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ - <span class="hljs-keyword">return</span> Twig.exports; - }); -} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">'undefined'</span> && <span class="hljs-built_in">module</span>.exports) {</pre></div></div> - - </li> - - - <li id="section-369"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-369">¶</a> - </div> - <p>Provide a CommonJS Modules/1.1 module</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-built_in">module</span>.exports = Twig.exports; -} <span class="hljs-keyword">else</span> {</pre></div></div> - - </li> - - - <li id="section-370"> - <div class="annotation"> - - <div class="pilwrap "> - <a class="pilcrow" href="#section-370">¶</a> - </div> - <p>Export for browser use</p> - - </div> - - <div class="content"><div class='highlight'><pre> <span class="hljs-built_in">window</span>.twig = Twig.exports.twig; - <span class="hljs-built_in">window</span>.Twig = Twig.exports; -}</pre></div></div> - - </li> - - </ul> - </div> -</body> -</html> diff --git a/node_modules/twig/lib/compile.js b/node_modules/twig/lib/compile.js deleted file mode 100644 index 15a6ebe..0000000 --- a/node_modules/twig/lib/compile.js +++ /dev/null @@ -1,99 +0,0 @@ -var Twig = require("../twig") - , twig = Twig.twig - , PATHS = require("./paths") - , MINIMATCH = require("minimatch") - , FS = require("fs") - , WALK = require("walk"); - -exports.defaults = { - compress: false - , pattern: "*\\.twig" - , recursive: false -}; - -exports.compile = function(options, files) { - // Create output template directory if necessary - if (options.output) { - PATHS.mkdir(options.output); - } - - files.forEach(function(file) { - FS.stat(file, function(err, stats) { - if (stats.isDirectory()) { - parseTemplateFolder(file, options.pattern); - } else if (stats.isFile()) { - parseTemplateFile(file); - } else { - console.log("ERROR " + file + ": Unable to stat file"); - } - }); - }); - - function parseTemplateFolder(directory, pattern) { - directory = PATHS.strip_slash(directory); - - // Get the files in the directory - // Walker options - var walker = WALK.walk(directory, { followLinks: false }) - , files = []; - - walker.on('file', function(root, stat, next) { - // normalize (remove / from end if present) - root = PATHS.strip_slash(root); - - // match against file pattern - var name = stat.name - , file = root + '/' + name; - if (MINIMATCH(name, pattern)) { - parseTemplateFile(file, directory); - files.push(file); - } - next(); - }); - - walker.on('end', function() { - // console.log(files); - }); - } - - function parseTemplateFile(file, base) { - if (base) base = PATHS.strip_slash(base); - var split_file = file.split("/") - , output_file_name = split_file.pop() - , output_file_base = PATHS.findBase(file) - , output_directory = options.output - , output_base = PATHS.removePath(base, output_file_base) - , output_id - , output_file; - - if (output_directory) { - // Create template directory - if (output_base !== "") { - PATHS.mkdir(output_directory + "/" + output_base); - output_base += "/"; - } - output_id = output_directory + "/" + output_base + output_file_name; - output_file = output_id + ".js" - } else { - output_id = file; - output_file = output_id + ".js" - } - - var tpl = twig({ - id: output_id - , path: file - , load: function(template) { - // compile! - var output = template.compile(options); - - FS.writeFile(output_file, output, 'utf8', function(err) { - if (err) { - console.log("Unable to compile " + file + ", error " + err); - } else { - console.log("Compiled " + file + "\t-> " + output_file); - } - }); - } - }); - } -};
\ No newline at end of file diff --git a/node_modules/twig/lib/paths.js b/node_modules/twig/lib/paths.js deleted file mode 100644 index 6e61c5a..0000000 --- a/node_modules/twig/lib/paths.js +++ /dev/null @@ -1,86 +0,0 @@ -var FS = require("fs") - , sep_chr = '/'; - -exports.relativePath = function(base, file) { - var base_path = exports.normalize(base.split(sep_chr)), - new_path = [], - val; - - // Remove file from url - base_path.pop(); - base_path = base_path.concat(file.split(sep_chr)); - - while (base_path.length > 0) { - val = base_path.shift(); - if (val == ".") { - // Ignore - } 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); -}; - -exports.findBase = function(file) { - var paths = exports.normalize(file.split(sep_chr)); - // we want everything before the file - if (paths.length > 1) { - // get rid of the filename - paths.pop(); - return paths.join(sep_chr) + sep_chr; - } else { - // we're in the file directory - return ""; - } -}; - -exports.removePath = function(path, file) { - if (!path) return ""; - - var base_path = exports.normalize(path.split(sep_chr)) - , file_path = exports.normalize(file.split(sep_chr)) - , val - , file_val; - - // strip base path off of file path - while(base_path.length > 0) { - val = base_path.shift(); - if (val !== '') { - file_val = file_path.shift(); - } - } - return file_path.join(sep_chr); -}; - -exports.normalize = function(file_arr) { - var new_arr = [] - , val; - while(file_arr.length > 0) { - val = file_arr.shift(); - if (val !== '') { - new_arr.push(val); - } - } - return new_arr; -}; - -exports.strip_slash = function(path) { - if (path.substr(-1) == '/') path = path.substring(0, path.length-1); - return path; -}; - -exports.mkdir = function(dir) { - try { - FS.mkdirSync(dir); - } catch (err) { - if (err.code == "EEXIST") { - // ignore if it's a "EEXIST" exeption - } else { - console.log(err); - throw err; - } - } -}; diff --git a/node_modules/twig/package.json b/node_modules/twig/package.json deleted file mode 100644 index 082cbfe..0000000 --- a/node_modules/twig/package.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "_from": "twig@~0.10.3", - "_id": "twig@0.10.3", - "_inBundle": false, - "_integrity": "sha1-Z2BOCOGSDr8vr4CpAeJWGJyKPGc=", - "_location": "/twig", - "_phantomChildren": {}, - "_requested": { - "type": "range", - "registry": true, - "raw": "twig@~0.10.3", - "name": "twig", - "escapedName": "twig", - "rawSpec": "~0.10.3", - "saveSpec": null, - "fetchSpec": "~0.10.3" - }, - "_requiredBy": [ - "/" - ], - "_resolved": "https://registry.npmjs.org/twig/-/twig-0.10.3.tgz", - "_shasum": "67604e08e1920ebf2faf80a901e256189c8a3c67", - "_spec": "twig@~0.10.3", - "_where": "/home/marvin/IdeaProjects/untitled", - "author": { - "name": "John Roepke", - "email": "john@justjohn.us", - "url": "http://john.sh/" - }, - "bin": { - "twigjs": "./bin/twigjs" - }, - "browser": { - "fs": false - }, - "bugs": { - "url": "https://github.com/twigjs/twig.js/issues" - }, - "bundleDependencies": false, - "dependencies": { - "locutus": "^2.0.5", - "minimatch": "3.0.x", - "walk": "2.3.x" - }, - "deprecated": false, - "description": "JS port of the Twig templating language.", - "devDependencies": { - "chai": "^3.5.0", - "mocha": "3.1.x", - "should": "11.1.x", - "sinon": "^1.17.6", - "sinon-chai": "^2.8.0", - "tokenizer": "1.1.x", - "webpack": "^1.13.3" - }, - "engines": { - "node": "*" - }, - "homepage": "https://github.com/twigjs/twig.js", - "licenses": [ - { - "type": "BSD-2-Clause", - "url": "https://raw.github.com/twigjs/twig.js/master/LICENSE" - } - ], - "main": "twig.js", - "name": "twig", - "repository": { - "type": "git", - "url": "git://github.com/twigjs/twig.js.git" - }, - "scripts": { - "build": "npm run build-node && npm run build-browser", - "build-browser": "WEBPACK_ENV=browser webpack", - "build-node": "webpack", - "postversion": "git push origin master && git push origin master --tags", - "preversion": "npm test && git diff --exit-code --quiet", - "test": "npm run build && mocha -r should" - }, - "version": "0.10.3" -} diff --git a/node_modules/twig/test-ext/checkout.sh b/node_modules/twig/test-ext/checkout.sh deleted file mode 100755 index 514b286..0000000 --- a/node_modules/twig/test-ext/checkout.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -if [ ! -d $DIR/twig.php ]; then - git clone git://github.com/fabpot/Twig.git $DIR/twig.php -else - cd $DIR/twig.php - git pull -fi diff --git a/node_modules/twig/test-ext/test.original.js b/node_modules/twig/test-ext/test.original.js deleted file mode 100644 index 27304e2..0000000 --- a/node_modules/twig/test-ext/test.original.js +++ /dev/null @@ -1,314 +0,0 @@ -/*jshint node: true */ - -( function( ) { - "use strict"; - var fs = require( "fs" ), - path = require("path"), - twigPhpDir = "test-ext/twig.php", - - runTest = function( ) { - describe( "Twig original test ->", function( ) { - var - Twig = Twig || require("../twig"), - twig = twig || Twig.twig, - walk = function( dir, done ) { - var results = [], - list = fs.readdirSync( dir ), - i = 0; - - ( function next( ) { - var file = list[i++], - stat; - if ( !file ) { - return done( null, results ); - } - - file = dir + '/' + file; - stat = fs.statSync( file ); - if ( stat && stat.isDirectory( ) ) { - walk( file, function( err, res ) { - results = results.concat( res ); - next( ); - } ); - } else { - results.push( file ); - next( ); - } - } )( ); - }, - testFile = function( filepath ) { - var tmp = fs.readFileSync( filepath, "utf8" ), - tmp2 = tmp.split( /^--/gm ), - res = {}; - - tmp2.forEach( function( data ) { - if ( !data ) { - return; - } - - var name = ( "" + data.match( /^[A-Z]+--/ ) || "" ).replace( /--$/, '' ); - - if ( name ) { - res[ name ] = data.substring( name.length + 3 ).trim( ); - } - } ); - - res.filepath = filepath; - - return res; - }; - - walk( twigPhpDir + "/test", function( err, files ) { - var testFiles = []; - - files.forEach( function( filepath ) { - if ( filepath.substring( filepath.length - 5 ) === ".test" ) { - testFiles.push( testFile( filepath ) ); - } - } ); - - testFiles.forEach( function( data, idx ) { - var Tokenizer = require( "tokenizer" ), - t = new Tokenizer( ), - res, - str = data.DATA, - currentObject - ; - - if ( !data.DATA || data.DATA.match( /^class|\$|^date_default_timezone_set|new Twig|new SimpleXMLElement|new ArrayObject|new ArrayIterator/ ) ) { - if ( data.EXCEPTION ) { - it( data.filepath + " -> " + data.TEST.trim( ), function( ) { - try { - twig( { - "data" : data.TEMPLATE.trim( ) - } ) - .render( res ) - .trim( ); - } catch( exp ) { - return; - } - - throw "Should have thrown an exception: " + data.EXCEPTION; - } ); - } else { - it( data.filepath + " -> " + data.TEST.trim( ), function( ) { - throw "Unsupported"; - } ); - } - delete testFiles[ idx ]; - return; - } - - t.on( "token", function( token, type ) { - // console.log( token, type, res, currentObject ); - - var tmpObject, - match, - tmpLength, - tmpString, - parentObject, - i - ; - - if ( type === 'symbol' ) { - if ( token === "return" ) { - return; - } - } - - if ( type === "whitespace" ) { - return; - } - - if ( type === "semicolon" ) { - return; - } - - if ( type === 'array' ) { - if ( !currentObject ) { - - currentObject = { - "type" : "array", - "keys" : [], - "values" : [] - }; - } else { - tmpObject = currentObject; - currentObject = { - "type" : "array", - "keys" : [], - "values" : [], - "parent" : tmpObject - }; - } - - return; - } - - if ( type === "string" ) { - match = token.match( /^'(([^']*| \\')*)'$/ ); - if ( match && match[1] ) { - tmpString = match[1]; - } else { - match = token.match( /^"(([^"]*| \\")*)"$/ ); - if ( match && match[1] ) { - tmpString = match[1]; - } - } - - if ( tmpString ) { - currentObject.last_value = tmpString.replace( /\\n/g, "\n" ).replace( /\\r/g, "\r" ); - return; - } - } - - if ( type === "number" ) { - currentObject.last_value = parseFloat( token, 10 ); - return; - } - - if ( type === "comma" ) { - if ( currentObject.last_value ) { - if ( currentObject.type === "array" ) { - tmpLength = currentObject.values.length; - currentObject.keys.push( tmpLength ); - currentObject.values.push( currentObject.last_value ); - delete currentObject.last_value; - - return; - } else if ( currentObject.type === "object" ) { - currentObject.keys.push( currentObject.last_key ); - currentObject.values.push( currentObject.last_value ); - delete currentObject.last_key; - delete currentObject.last_value; - return; - } - } - } - - if ( type === "key-label" ) { - currentObject.type = "object"; - currentObject.last_key = currentObject.last_value; - delete currentObject.last_value; - - return; - } - - if ( type === "end-object" ) { - if ( currentObject.type === "array" ) { - if ( currentObject.last_value ) { - tmpLength = currentObject.values.length; - currentObject.keys.push( tmpLength ); - currentObject.values.push( currentObject.last_value ); - delete currentObject.last_value; - } - } else if ( currentObject.type === "object" ) { - if ( currentObject.last_key && currentObject.last_value ) { - currentObject.keys.push( currentObject.last_key ); - currentObject.values.push( currentObject.last_value ); - delete currentObject.last_key; - delete currentObject.last_value; - } - } - - if ( currentObject.type === "array" ) { - tmpObject = []; - } else if ( currentObject.type === "object" ) { - tmpObject = {}; - } - - for ( i = 0; i < currentObject.values.length; i+=1 ) { - tmpObject[ currentObject.keys[i] ] = currentObject.values[i]; - } - - if ( currentObject.parent ) { - parentObject = currentObject.parent; - delete currentObject.parent; - delete currentObject.last_key; - delete currentObject.last_value; - currentObject = parentObject; - currentObject.last_value = tmpObject; - } else { - res = tmpObject; - currentObject = undefined; - } - - return; - } - } ); - - t.on( "end", function( ) { - /* - if ( data.TEST.trim( ) === 'Twig supports the in operator' ) { - console.log( data.TEST.trim( ), res, data.DATA ); - console.log( "data.TEMPLATE" ); - console.log( data.TEMPLATE ); - console.log( data.TEMPLATE.substr( -1 ) ); - console.log( "data.EXPECT" ); - console.log( data.EXPECT ); - console.log( "data.RESULT" ); - console.log( - twig( { - "data" : data.TEMPLATE - } ) - .render( res ) - ); - } - */ - - it( data.filepath + " -> " + data.TEST.trim( ), function( ) { - twig( { - "data" : data.TEMPLATE.trim( ) - } ) - .render( res ) - .trim( ) - .should - .equal( data.EXPECT ); - } ); - } ); - - t.on( "error", function( err ) { - // console.log( err ); - } ); - - t.addRule( /^'([^']|\\')*'$/, 'string' ); - t.addRule( /^"([^"]|\\")*"$/, 'string' ); - t.addRule( /^'([^']|\\')*$/, 'maybe-string' ); - t.addRule( /^"([^"]|\\")*$/, 'maybe-string' ); - t.addRule( /^=$/, 'equal' ); - t.addRule( /^=>$/, 'key-label' ); - t.addRule( /^-\?\d+(\.\d+)?$/, 'number' ); - t.addRule( /^\d+$/, 'number' ); - t.addRule( /^\d+\.$/, 'maybe-float' ); - t.addRule( /^(true|false)$/, 'bool' ); - t.addRule( /^null$/, 'null' ); - t.addRule( /^array\s*\($/, 'array' ); - t.addRule( /^\($/, 'begin-object' ); - t.addRule( /^\)$/, 'end-object' ); - t.addRule( /^\[$/, 'begin-array' ); - t.addRule( /^\]$/, 'end-array' ); - t.addRule( /^,$/, 'comma' ); - t.addRule( /^'$/, 'ping' ); - t.addRule( /^;$/, 'semicolon' ); - t.addRule( /^"$/, 'double-ping' ); - t.addRule( /^\w+$/, "symbol" ); - t.addRule( /^(\s)+$/, 'whitespace' ); - - t.write( str + " " ); - t.end( ); - } ); - } ); - } ); - }, - - setup = function( ) { - var existsSync = fs.existsSync || path.existsSync; - - if ( existsSync( twigPhpDir ) ) { - runTest( ); - return; - } - }; - - setup( ); -} )( ); diff --git a/node_modules/twig/twig.js b/node_modules/twig/twig.js deleted file mode 100644 index d860b59..0000000 --- a/node_modules/twig/twig.js +++ /dev/null @@ -1,7223 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory((function webpackLoadOptionalExternalModule() { try { return require("fs"); } catch(e) {} }()), require("path")); - else if(typeof define === 'function' && define.amd) - define(["fs", "path"], factory); - else if(typeof exports === 'object') - exports["Twig"] = factory((function webpackLoadOptionalExternalModule() { try { return require("fs"); } catch(e) {} }()), require("path")); - else - root["Twig"] = factory(root["fs"], root["path"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_19__, __WEBPACK_EXTERNAL_MODULE_20__) { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * Twig.js - * - * @copyright 2011-2016 John Roepke and the Twig.js Contributors - * @license Available under the BSD 2-Clause License - * @link https://github.com/twigjs/twig.js - */ - - var Twig = { - VERSION: '0.10.2' - }; - - __webpack_require__(1)(Twig); - __webpack_require__(2)(Twig); - __webpack_require__(3)(Twig); - __webpack_require__(5)(Twig); - __webpack_require__(6)(Twig); - __webpack_require__(7)(Twig); - __webpack_require__(17)(Twig); - __webpack_require__(18)(Twig); - __webpack_require__(21)(Twig); - __webpack_require__(22)(Twig); - __webpack_require__(23)(Twig); - __webpack_require__(24)(Twig); - __webpack_require__(25)(Twig); - __webpack_require__(26)(Twig); - - module.exports = Twig.exports; - - -/***/ }, -/* 1 */ -/***/ function(module, exports) { - - // ## twig.core.js - // - // This file handles template level tokenizing, compiling and parsing. - module.exports = function (Twig) { - "use strict"; - - Twig.trace = false; - Twig.debug = false; - - // Default caching to true for the improved performance it offers - Twig.cache = true; - - Twig.placeholders = { - 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); - } - 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) { // 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) { - // console.log("indexOf not found1 ", JSON.stringify(searchElement), JSON.stringify(arr)); - 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; - } - } - if (arr == searchElement) { - return 0; - } - // console.log("indexOf not found2 ", JSON.stringify(searchElement), JSON.stringify(arr)); - - return -1; - } - - Twig.forEach = function (arr, callback, thisArg) { - if (Array.prototype.forEach ) { - return arr.forEach(callback, thisArg); - } - - var T, k; - - if ( arr == null ) { - throw new TypeError( " this is null or not defined" ); - } - - // 1. Let O be the result of calling ToObject passing the |this| value as the argument. - var O = Object(arr); - - // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". - // 3. Let len be ToUint32(lenValue). - var len = O.length >>> 0; // Hack to convert O.length to a UInt32 - - // 4. If IsCallable(callback) is false, throw a TypeError exception. - // See: http://es5.github.com/#x9.11 - if ( {}.toString.call(callback) != "[object Function]" ) { - throw new TypeError( callback + " is not a function" ); - } - - // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. - if ( thisArg ) { - T = thisArg; - } - - // 6. Let k be 0 - k = 0; - - // 7. Repeat, while k < len - while( k < len ) { - - var kValue; - - // a. Let Pk be ToString(k). - // This is implicit for LHS operands of the in operator - // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. - // This step can be combined with c - // c. If kPresent is true, then - if ( k in O ) { - - // i. Let kValue be the result of calling the Get internal method of O with argument Pk. - kValue = O[ k ]; - - // ii. Call the Call internal method of callback with T as the this value and - // argument list containing kValue, k, and O. - callback.call( T, kValue, k, O ); - } - // d. Increase k by 1. - k++; - } - // 8. return undefined - }; - - Twig.merge = function(target, source, onlyChanged) { - Twig.forEach(Object.keys(source), function (key) { - if (onlyChanged && !(key in target)) { - return; - } - - target[key] = source[key] - }); - - return target; - }; - - /** - * Exception thrown by twig.js. - */ - 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; - - 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));}} - }; - - - if (typeof console !== "undefined") { - if (typeof console.error !== "undefined") { - Twig.log.error = function() { - console.error.apply(console, arguments); - } - } else if (typeof console.log !== "undefined") { - Twig.log.error = function() { - console.log.apply(console, arguments); - } - } - } else { - Twig.log.error = function(){}; - } - - /** - * Wrapper for child context objects in Twig. - * - * @param {Object} context Values to initialize the context with. - */ - Twig.ChildContext = function(context) { - var ChildContext = function ChildContext() {}; - ChildContext.prototype = context; - return new ChildContext(); - }; - - /** - * 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_whitespace_pre: 'output_whitespace_pre', - output_whitespace_post: 'output_whitespace_post', - output_whitespace_both: 'output_whitespace_both', - logic_whitespace_pre: 'logic_whitespace_pre', - logic_whitespace_post: 'logic_whitespace_post', - logic_whitespace_both: 'logic_whitespace_both' - }; - - /** - * Token syntax definitions. - */ - Twig.token.definitions = [ - { - type: Twig.token.type.raw, - open: '{% raw %}', - close: '{% endraw %}' - }, - { - type: Twig.token.type.raw, - open: '{% verbatim %}', - close: '{% endverbatim %}' - }, - // *Whitespace type tokens* - // - // These typically take the form `{{- expression -}}` or `{{- expression }}` or `{{ expression -}}`. - { - type: Twig.token.type.output_whitespace_pre, - open: '{{-', - close: '}}' - }, - { - type: Twig.token.type.output_whitespace_post, - open: '{{', - close: '-}}' - }, - { - type: Twig.token.type.output_whitespace_both, - open: '{{-', - close: '-}}' - }, - { - type: Twig.token.type.logic_whitespace_pre, - open: '{%-', - close: '%}' - }, - { - type: Twig.token.type.logic_whitespace_post, - open: '{%', - close: '-%}' - }, - { - type: Twig.token.type.logic_whitespace_both, - open: '{%-', - close: '-%}' - }, - // *Output type tokens* - // - // These typically take the form `{{ expression }}`. - { - type: Twig.token.type.output, - open: '{{', - close: '}}' - }, - // *Logic type tokens* - // - // These typically take a form like `{% if expression %}` or `{% endif %}` - { - type: Twig.token.type.logic, - open: '{%', - close: '%}' - }, - // *Comment type tokens* - // - // These take the form `{# anything #}` - { - type: Twig.token.type.comment, - 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.findStart = function (template) { - var output = { - position: null, - close_position: null, - def: null - }, - i, - token_template, - first_key_position, - close_key_position; - - for (i=0;i<Twig.token.definitions.length;i++) { - token_template = Twig.token.definitions[i]; - first_key_position = template.indexOf(token_template.open); - close_key_position = template.indexOf(token_template.close); - - Twig.log.trace("Twig.token.findStart: ", "Searching for ", token_template.open, " found at ", first_key_position); - - //Special handling for mismatched tokens - if (first_key_position >= 0) { - //This token matches the template - if (token_template.open.length !== token_template.close.length) { - //This token has mismatched closing and opening tags - if (close_key_position < 0) { - //This token's closing tag does not match the template - continue; - } - } - } - // Does this token occur before any other types? - if (first_key_position >= 0 && (output.position === null || first_key_position < output.position)) { - output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } else if (first_key_position >= 0 && output.position !== null && first_key_position === output.position) { - /*This token exactly matches another token, - greedily match to check if this token has a greater specificity*/ - if (token_template.open.length > output.def.open.length) { - //This token's opening tag is more specific than the previous match - output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } else if (token_template.open.length === output.def.open.length) { - if (token_template.close.length > output.def.close.length) { - //This token's opening tag is as specific as the previous match, - //but the closing tag has greater specificity - if (close_key_position >= 0 && close_key_position < output.close_position) { - //This token's closing tag exists in the template, - //and it occurs sooner than the previous match - output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } - } else if (close_key_position >= 0 && close_key_position < output.close_position) { - //This token's closing tag is not more specific than the previous match, - //but it occurs sooner than the previous match - output.position = first_key_position; - output.def = token_template; - output.close_position = close_key_position; - } - } - } - } - - delete output['close_position']; - - return output; - }; - - Twig.token.findEnd = function (template, token_def, start) { - var end = null, - found = false, - offset = 0, - - // String position variables - str_pos = null, - str_found = null, - pos = null, - end_offset = null, - this_str_pos = null, - end_str_pos = null, - - // For loop variables - i, - l; - - while (!found) { - str_pos = null; - str_found = null; - pos = template.indexOf(token_def.close, offset); - - if (pos >= 0) { - end = pos; - found = true; - } else { - // throw an exception - throw new Twig.Error("Unable to find closing bracket '" + token_def.close + - "'" + " opened near template position " + start); - } - - // Ignore quotes within comments; just look for the next comment close sequence, - // regardless of what comes before it. https://github.com/justjohn/twig.js/issues/95 - if (token_def.type === Twig.token.type.comment) { - break; - } - // Ignore quotes within raw tag - // Fixes #283 - if (token_def.type === Twig.token.type.raw) { - break; - } - - l = Twig.token.strings.length; - 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)) { - str_pos = this_str_pos; - str_found = Twig.token.strings[i]; - } - } - - // 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) { - end_str_pos = template.indexOf(str_found, end_offset); - if (end_str_pos < 0) { - throw "Unclosed string in template"; - } - // Ignore escaped quotes - if (template.substr(end_str_pos - 1, 1) !== "\\") { - offset = end_str_pos + 1; - break; - } else { - end_offset = end_str_pos + 1; - } - } - } - } - return end; - }; - - /** - * Convert a template into high-level tokens. - */ - Twig.tokenize = function (template) { - var tokens = [], - // An offset for reporting errors locations in the template. - error_offset = 0, - - // The start and type of the first token found in the template. - found_token = null, - // The end position of the matched token. - end = null; - - while (template.length > 0) { - // Find the first occurance of any token type in the template - found_token = Twig.token.findStart(template); - - Twig.log.trace("Twig.tokenize: ", "Found token: ", found_token); - - if (found_token.position !== null) { - // Add a raw type token for anything before the start of the token - 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; - - // Find the end of the token - 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 (template.substr( end + found_token.def.close.length, 1 ) === "\n") { - switch (found_token.def.type) { - case "logic_whitespace_pre": - case "logic_whitespace_post": - case "logic_whitespace_both": - case "logic": - // Newlines directly after logic tokens are ignored - end += 1; - break; - } - } - - template = template.substr(end + found_token.def.close.length); - - // Increment the position in the template - error_offset += end + found_token.def.close.length; - - } else { - // No more tokens -> add the rest of the template as a raw-type token - tokens.push({ - type: Twig.token.type.raw, - value: template - }); - template = ''; - } - } - - return tokens; - }; - - - Twig.compile = function (tokens) { - try { - - // Output and intermediate stacks - var output = [], - stack = [], - // The tokens between open and close tags - intermediate_output = [], - - token = null, - logic_token = null, - unclosed_token = null, - // Temporary previous token. - prev_token = null, - // Temporary previous output. - prev_output = null, - // Temporary previous intermediate output. - prev_intermediate_output = null, - // The previous token's template - prev_template = null, - // Token lookahead - next_token = null, - // The output token - tok_output = null, - - // Logic Token values - type = null, - open = null, - next = null; - - var compile_output = function(token) { - Twig.expression.compile.apply(this, [token]); - if (stack.length > 0) { - intermediate_output.push(token); - } else { - output.push(token); - } - }; - - var compile_logic = function(token) { - // Compile the logic 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); - - // Not a standalone token, check logic stack to see if this is expected - 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); - } - } - - // This token requires additional tokens to complete the logic structure. - if (next !== undefined && next.length > 0) { - Twig.log.trace("Twig.compile: ", "Pushing ", logic_token, " to logic stack."); - - if (stack.length > 0) { - // Put any currently held output into the output list of the logic operator - // currently at the head of the stack before we push a new one on. - 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 = []; - } - - // Push the new logic token onto the logic stack - stack.push(logic_token); - - } else if (open !== undefined && open) { - tok_output = { - type: Twig.token.type.logic, - token: logic_token - }; - // Standalone token (like {% set ... %} - if (stack.length > 0) { - intermediate_output.push(tok_output); - } else { - output.push(tok_output); - } - } - }; - - while (tokens.length > 0) { - token = tokens.shift(); - prev_output = output[output.length - 1]; - prev_intermediate_output = intermediate_output[intermediate_output.length - 1]; - next_token = tokens[0]; - 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: - compile_logic.call(this, token); - break; - - // Do nothing, comments should be ignored - case Twig.token.type.comment: - break; - - case Twig.token.type.output: - compile_output.call(this, token); - break; - - //Kill whitespace ahead and behind this token - case Twig.token.type.logic_whitespace_pre: - case Twig.token.type.logic_whitespace_post: - case Twig.token.type.logic_whitespace_both: - case Twig.token.type.output_whitespace_pre: - case Twig.token.type.output_whitespace_post: - case Twig.token.type.output_whitespace_both: - if (token.type !== Twig.token.type.output_whitespace_post && token.type !== Twig.token.type.logic_whitespace_post) { - if (prev_output) { - //If the previous output is raw, pop it off - if (prev_output.type === Twig.token.type.raw) { - output.pop(); - - //If the previous output is not just whitespace, trim it - if (prev_output.value.match(/^\s*$/) === null) { - prev_output.value = prev_output.value.trim(); - //Repush the previous output - output.push(prev_output); - } - } - } - - if (prev_intermediate_output) { - //If the previous intermediate output is raw, pop it off - if (prev_intermediate_output.type === Twig.token.type.raw) { - intermediate_output.pop(); - - //If the previous output is not just whitespace, trim it - if (prev_intermediate_output.value.match(/^\s*$/) === null) { - prev_intermediate_output.value = prev_intermediate_output.value.trim(); - //Repush the previous intermediate output - intermediate_output.push(prev_intermediate_output); - } - } - } - } - - //Compile this token - switch (token.type) { - case Twig.token.type.output_whitespace_pre: - case Twig.token.type.output_whitespace_post: - case Twig.token.type.output_whitespace_both: - compile_output.call(this, token); - break; - case Twig.token.type.logic_whitespace_pre: - case Twig.token.type.logic_whitespace_post: - case Twig.token.type.logic_whitespace_both: - compile_logic.call(this, token); - break; - } - - if (token.type !== Twig.token.type.output_whitespace_pre && token.type !== Twig.token.type.logic_whitespace_pre) { - if (next_token) { - //If the next token is raw, shift it out - if (next_token.type === Twig.token.type.raw) { - tokens.shift(); - - //If the next token is not just whitespace, trim it - if (next_token.value.match(/^\s*$/) === null) { - next_token.value = next_token.value.trim(); - //Unshift the next token - tokens.unshift(next_token); - } - } - } - } - - break; - } - - Twig.log.trace("Twig.compile: ", " Output: ", output, - " Logic Stack: ", stack, - " Pending Output: ", intermediate_output ); - } - - // Verify that there are no logic tokens left in the stack. - 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) { - if (this.options.rethrow) { - throw ex - } - else { - Twig.log.error("Error compiling twig template " + this.id + ": "); - if (ex.stack) { - Twig.log.error(ex.stack); - } else { - Twig.log.error(ex.toString()); - } - } - } - }; - - /** - * Parse a compiled template. - * - * @param {Array} tokens The compiled tokens. - * @param {Object} context The render context. - * - * @return {string} The parsed template. - */ - Twig.parse = function (tokens, context) { - try { - var output = [], - // Track logic chains - chain = true, - that = this; - - Twig.forEach(tokens, function parseToken(token) { - Twig.log.debug("Twig.parse: ", "Parsing token: ", token); - - switch (token.type) { - case Twig.token.type.raw: - output.push(Twig.filters.raw(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: - // Do nothing, comments should be ignored - break; - - //Fall through whitespace to output - case Twig.token.type.output_whitespace_pre: - case Twig.token.type.output_whitespace_post: - case Twig.token.type.output_whitespace_both: - case Twig.token.type.output: - Twig.log.debug("Twig.parse: ", "Output token: ", token.stack); - // Parse the given expression in the given context - output.push(Twig.expression.parse.apply(that, [token.stack, context])); - break; - } - }); - return Twig.output.apply(this, [output]); - } catch (ex) { - if (this.options.rethrow) { - throw ex; - } - else { - Twig.log.error("Error parsing twig template " + this.id + ": "); - if (ex.stack) { - Twig.log.error(ex.stack); - } else { - Twig.log.error(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; - - // Tokenize - Twig.log.debug("Twig.prepare: ", "Tokenizing ", data); - raw_tokens = Twig.tokenize.apply(this, [data]); - - // Compile - Twig.log.debug("Twig.prepare: ", "Compiling ", raw_tokens); - tokens = Twig.compile.apply(this, [raw_tokens]); - - Twig.log.debug("Twig.prepare: ", "Compiled ", tokens); - - return tokens; - }; - - /** - * Join the output token's stack and escape it if needed - * - * @param {Array} Output token's stack - * - * @return {string|String} Autoescaped output - */ - Twig.output = function(output) { - if (!this.options.autoescape) { - return output.join(""); - } - - var strategy = 'html'; - if(typeof this.options.autoescape == 'string') - strategy = this.options.autoescape; - - // [].map would be better but it's not supported by IE8- - var escaped_output = []; - Twig.forEach(output, function (str) { - if (str && (str.twig_markup !== true && str.twig_markup != strategy)) { - str = Twig.filters.escape(str, [ strategy ]); - } - escaped_output.push(str); - }); - return Twig.Markup(escaped_output.join("")); - } - - // Namespace for template storage and retrieval - Twig.Templates = { - /** - * Registered template loaders - use Twig.Templates.registerLoader to add supported loaders - * @type {Object} - */ - loaders: {}, - - /** - * Registered template parsers - use Twig.Templates.registerParser to add supported parsers - * @type {Object} - */ - parsers: {}, - - /** - * Cached / loaded templates - * @type {Object} - */ - registry: {} - }; - - /** - * Is this id valid for a twig template? - * - * @param {string} id The ID to check. - * - * @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.cache && Twig.Templates.registry.hasOwnProperty(id)) { - throw new Twig.Error("There is already a template with the ID " + id); - } - return true; - } - - /** - * Register a template loader - * - * @example - * Twig.extend(function(Twig) { - * Twig.Templates.registerLoader('custom_loader', function(location, params, callback, error_callback) { - * // ... load the template ... - * params.data = loadedTemplateData; - * // create and return the template - * var template = new Twig.Template(params); - * if (typeof callback === 'function') { - * callback(template); - * } - * return template; - * }); - * }); - * - * @param {String} method_name The method this loader is intended for (ajax, fs) - * @param {Function} func The function to execute when loading the template - * @param {Object|undefined} scope Optional scope parameter to bind func to - * - * @throws Twig.Error - * - * @return {void} - */ - Twig.Templates.registerLoader = function(method_name, func, scope) { - if (typeof func !== 'function') { - throw new Twig.Error('Unable to add loader for ' + method_name + ': Invalid function reference given.'); - } - if (scope) { - func = func.bind(scope); - } - this.loaders[method_name] = func; - }; - - /** - * Remove a registered loader - * - * @param {String} method_name The method name for the loader you wish to remove - * - * @return {void} - */ - Twig.Templates.unRegisterLoader = function(method_name) { - if (this.isRegisteredLoader(method_name)) { - delete this.loaders[method_name]; - } - }; - - /** - * See if a loader is registered by its method name - * - * @param {String} method_name The name of the loader you are looking for - * - * @return {boolean} - */ - Twig.Templates.isRegisteredLoader = function(method_name) { - return this.loaders.hasOwnProperty(method_name); - }; - - /** - * Register a template parser - * - * @example - * Twig.extend(function(Twig) { - * Twig.Templates.registerParser('custom_parser', function(params) { - * // this template source can be accessed in params.data - * var template = params.data - * - * // ... custom process that modifies the template - * - * // return the parsed template - * return template; - * }); - * }); - * - * @param {String} method_name The method this parser is intended for (twig, source) - * @param {Function} func The function to execute when parsing the template - * @param {Object|undefined} scope Optional scope parameter to bind func to - * - * @throws Twig.Error - * - * @return {void} - */ - Twig.Templates.registerParser = function(method_name, func, scope) { - if (typeof func !== 'function') { - throw new Twig.Error('Unable to add parser for ' + method_name + ': Invalid function regerence given.'); - } - - if (scope) { - func = func.bind(scope); - } - - this.parsers[method_name] = func; - }; - - /** - * Remove a registered parser - * - * @param {String} method_name The method name for the parser you wish to remove - * - * @return {void} - */ - Twig.Templates.unRegisterParser = function(method_name) { - if (this.isRegisteredParser(method_name)) { - delete this.parsers[method_name]; - } - }; - - /** - * See if a parser is registered by its method name - * - * @param {String} method_name The name of the parser you are looking for - * - * @return {boolean} - */ - Twig.Templates.isRegisteredParser = function(method_name) { - return this.parsers.hasOwnProperty(method_name); - }; - - /** - * 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.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; - } - return Twig.Templates.registry[id]; - }; - - /** - * Load a template from a remote location using AJAX and saves in with the given ID. - * - * Available parameters: - * - * async: Should the HTTP request be performed asynchronously. - * Defaults to true. - * method: What method should be used to load the template - * (fs or ajax) - * parser: What method should be used to parse the template - * (twig or source) - * precompiled: Has the template already been compiled. - * - * @param {string} location The remote URL to load as a template. - * @param {Object} params The template parameters. - * @param {function} callback A callback triggered when the template finishes loading. - * @param {function} error_callback A callback triggered if an error occurs loading the template. - * - * - */ - Twig.Templates.loadRemote = function(location, params, callback, error_callback) { - var loader; - - // Default to async - if (params.async === undefined) { - params.async = true; - } - - // Default to the URL so the template is cached. - if (params.id === undefined) { - params.id = location; - } - - // Check for existing template - if (Twig.cache && Twig.Templates.registry.hasOwnProperty(params.id)) { - // A template is already saved with the given id. - if (typeof callback === 'function') { - callback(Twig.Templates.registry[params.id]); - } - // TODO: if async, return deferred promise - return Twig.Templates.registry[params.id]; - } - - //if the parser name hasn't been set, default it to twig - params.parser = params.parser || 'twig'; - - // Assume 'fs' if the loader is not defined - loader = this.loaders[params.method] || this.loaders.fs; - return loader.apply(this, arguments); - }; - - // Determine object 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: { - * data: The template, either pre-compiled tokens or a string template - * id: The name of this template - * blocks: Any pre-existing block from a child template - * } - * - * @param {Object} params The template parameters. - */ - 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, - name = params.name, - method = params.method, - // parser options - options = params.options; - - // # 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) - // options: { - // Compiler/parser options - // - // strict_variables: true/false - // Should missing variable/keys emit an error message. If false, they default to null. - // } - // } - // - - this.id = id; - this.method = method; - this.base = base; - this.path = path; - this.url = url; - this.name = name; - 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.importedBlocks = []; - this.originalBlockTokens = {}; - this.child = { - blocks: blocks || {} - }; - this.extend = null; - }; - - Twig.Template.prototype.render = function (context, params) { - params = params || {}; - - var output, - url; - - this.context = context || {}; - - // Clear any previous state - 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]); - - // Does this template extend another - if (this.extend) { - var ext_template; - - // check if the template is provided inline - if ( this.options.allowInlineIncludes ) { - ext_template = Twig.Templates.load(this.extend); - if ( ext_template ) { - ext_template.options = this.options; - } - } - - // check for the template file via include - if (!ext_template) { - url = Twig.path.parsePath(this, this.extend); - - ext_template = Twig.Templates.loadRemote(url, { - method: this.getLoaderMethod(), - 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.options.allowInlineIncludes) { - file = this.path ? this.path + '/' + file : file; - sub_template = Twig.Templates.load(file); - - if (!sub_template) { - sub_template = Twig.Templates.loadRemote(url, { - id: file, - method: this.getLoaderMethod(), - async: false, - path: file, - options: this.options - }); - - if (!sub_template) { - throw new Twig.Error("Unable to find the template " + file); - } - } - - sub_template.options = this.options; - - return sub_template; - } - - url = Twig.path.parsePath(this, file); - - // Load blocks from an external file - sub_template = Twig.Templates.loadRemote(url, { - method: this.getLoaderMethod(), - 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); - - // Mixin blocks - Twig.forEach(Object.keys(sub_template.blocks), function(key) { - if (override || that.blocks[key] === undefined) { - that.blocks[key] = sub_template.blocks[key]; - that.importedBlocks.push(key); - } - }); - }; - - Twig.Template.prototype.importMacros = function(file) { - var url = Twig.path.parsePath(this, file); - - // load remote template - var remoteTemplate = Twig.Templates.loadRemote(url, { - method: this.getLoaderMethod(), - async: false, - id: url - }); - - return remoteTemplate; - }; - - Twig.Template.prototype.getLoaderMethod = function() { - if (this.path) { - return 'fs'; - } - if (this.url) { - return 'ajax'; - } - return this.method || 'fs'; - }; - - Twig.Template.prototype.compile = function(options) { - // compile the template into raw JS - return Twig.compiler.compile(this, options); - }; - - /** - * Create safe output - * - * @param {string} Content safe to output - * - * @return {String} Content wrapped into a String - */ - - Twig.Markup = function(content, strategy) { - if(typeof strategy == 'undefined') { - strategy = true; - } - - if (typeof content === 'string' && content.length > 0) { - content = new String(content); - content.twig_markup = strategy; - } - return content; - }; - - return Twig; - - }; - - -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - // ## twig.compiler.js - // - // This file handles compiling templates into JS - module.exports = function (Twig) { - /** - * Namespace for compilation. - */ - Twig.compiler = { - module: {} - }; - - // Compile a Twig Template to output. - Twig.compiler.compile = function(template, options) { - // Get tokens - var tokens = JSON.stringify(template.tokens) - , id = template.id - , output; - - if (options.module) { - if (Twig.compiler.module[options.module] === undefined) { - throw new Twig.Error("Unable to find module type " + options.module); - } - 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\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) - } - , 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'; - }; - - return Twig; - }; - - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - // ## twig.expression.js - // - // This file handles tokenizing, compiling and parsing expressions. - module.exports = function (Twig) { - "use strict"; - - /** - * Namespace for expression handling. - */ - Twig.expression = { }; - - __webpack_require__(4)(Twig); - - /** - * Reserved word that can't be used as variable names. - */ - Twig.expression.reservedWords = [ - "true", "false", "null", "TRUE", "FALSE", "NULL", "_context", "and", "or", "in", "not in", "if" - ]; - - /** - * The type of tokens used in expressions. - */ - 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', - slice: 'Twig.expression.type.slice', - 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' - }, - subexpression: { - start: 'Twig.expression.type.subexpression.start', - end: 'Twig.expression.type.subexpression.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 = { - // What can follow an expression (in general) - 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.subexpression.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.type.subexpression.start - ] - }; - - // Most expressions allow a '.' or '[' after them, so we provide a convenience set - Twig.expression.set.operations_extended = Twig.expression.set.operations.concat([ - Twig.expression.type.key.period, - Twig.expression.type.key.brackets, - Twig.expression.type.slice]); - - // Some commonly used compile and parse functions. - 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); - } - } - }; - - // The regular expressions and compile/parse logic used to match tokens in expressions. - // - // Properties: - // - // 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. - // - // 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_]*(\s?as)?)/, - 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, - // Match a 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; - - // pop tokens off the stack until the start of the object - for(;i >= 0; i--) { - stack_token = stack.pop(); - if (stack_token.type === Twig.expression.type.object.start - || stack_token.type === Twig.expression.type.parameter.start - || stack_token.type === Twig.expression.type.array.start) { - stack.push(stack_token); - break; - } - output.push(stack_token); - } - output.push(token); - } - }, - { - /** - * Match a number (integer or decimal) - */ - type: Twig.expression.type.number, - // match a number - regex: /^\-?\d+(\.\d+)?/, - next: Twig.expression.set.operations, - compile: function(token, stack, output) { - token.value = Number(token.value); - output.push(token); - }, - parse: Twig.expression.fn.parse.push_value - }, - { - type: Twig.expression.type.operator.binary, - // Match any of ?:, +, *, /, -, %, ~, <, <=, >, >=, !=, ==, **, ?, :, and, or, in, not in - // and, or, in, not in can be followed by a space or parenthesis - regex: /(^\?\:|^[\+\-~%\?]|^[\:](?!\d\])|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^(and)[\(|\s+]|^(or)[\(|\s+]|^(in)[\(|\s+]|^(not in)[\(|\s+]|^\.\.)/, - next: Twig.expression.set.expressions.concat([Twig.expression.type.operator.unary]), - transform: function(match, tokens) { - switch(match[0]) { - case 'and(': - case 'or(': - case 'in(': - case 'not in(': - //Strip off the ( if it exists - tokens[tokens.length - 1].value = match[2]; - return match[0]; - break; - default: - return ''; - } - }, - 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 === ":") { - // Check if this is a ternary or object key being set - if (stack[stack.length - 1] && stack[stack.length-1].value === "?") { - // Continue as normal for a ternary - } else { - // This is not a ternary so we push the token to the output where it can be handled - // when the assocated object is closed. - var key_token = output.pop(); - - if (key_token.type === Twig.expression.type.string || - key_token.type === Twig.expression.type.variable) { - token.key = key_token.value; - } else if (key_token.type === Twig.expression.type.number) { - // Convert integer keys into string keys - token.key = key_token.value.toString(); - } else if (key_token.expression && - (key_token.type === Twig.expression.type.parameter.end || - key_token.type == Twig.expression.type.subexpression.end)) { - token.params = key_token.params; - } 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) { - // handle ternary ':' operator - stack.push(token); - } else if (token.params) { - // handle "{(expression):value}" - token.key = Twig.expression.parse.apply(this, [token.params, context]); - stack.push(token); - - //If we're in a loop, we might need token.params later, especially in this form of "(expression):value" - if (!context.loop) { - delete(token.params); - } - } else { - Twig.expression.operator.parse(token.value, stack); - } - } - }, - { - type: Twig.expression.type.operator.unary, - // Match any of not - regex: /(^not\s+)/, - next: Twig.expression.set.expressions, - compile: function(token, stack, output) { - delete token.match; - - 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); - } - }, - { - /** - * Match a string. This is anything between a pair of single or double quotes. - */ - type: Twig.expression.type.string, - // See: http://blog.stevenlevithan.com/archives/match-quoted-string - regex: /^(["'])(?:(?=(\\?))\2[\s\S])*?\1/, - next: Twig.expression.set.operations_extended, - 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("\\'", "'"); - } - 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 subexpression set start. - */ - type: Twig.expression.type.subexpression.start, - regex: /^\(/, - next: Twig.expression.set.expressions.concat([Twig.expression.type.subexpression.end]), - compile: function(token, stack, output) { - token.value = '('; - output.push(token); - stack.push(token); - }, - parse: Twig.expression.fn.parse.push - }, - { - /** - * Match a subexpression set end. - */ - type: Twig.expression.type.subexpression.end, - regex: /^\)/, - next: Twig.expression.set.operations_extended, - validate: function(match, tokens) { - // Iterate back through previous tokens to ensure we follow a subexpression start - var i = tokens.length - 1, - found_subexpression_start = false, - next_subexpression_start_invalid = false, - unclosed_parameter_count = 0; - - while(!found_subexpression_start && i >= 0) { - var token = tokens[i]; - - found_subexpression_start = token.type === Twig.expression.type.subexpression.start; - - // If we have previously found a subexpression end, then this subexpression start is the start of - // that subexpression, not the subexpression we are searching for - if (found_subexpression_start && next_subexpression_start_invalid) { - next_subexpression_start_invalid = false; - found_subexpression_start = false; - } - - // Count parameter tokens to ensure we dont return truthy for a parameter opener - if (token.type === Twig.expression.type.parameter.start) { - unclosed_parameter_count++; - } else if (token.type === Twig.expression.type.parameter.end) { - unclosed_parameter_count--; - } else if (token.type === Twig.expression.type.subexpression.end) { - next_subexpression_start_invalid = true; - } - - i--; - } - - // If we found unclosed parameters, return false - // If we didnt find subexpression start, return false - // Otherwise return true - - return (found_subexpression_start && (unclosed_parameter_count === 0)); - }, - compile: function(token, stack, output) { - // This is basically a copy of parameter end compilation - var stack_token, - end_token = token; - - stack_token = stack.pop(); - while(stack.length > 0 && stack_token.type != Twig.expression.type.subexpression.start) { - output.push(stack_token); - stack_token = stack.pop(); - } - - // Move contents of parens into preceding filter - var param_stack = []; - while(token.type !== Twig.expression.type.subexpression.start) { - // Add token to arguments stack - param_stack.unshift(token); - token = output.pop(); - } - - param_stack.unshift(token); - - var is_expression = false; - - //If the token at the top of the *stack* is a function token, pop it onto the output queue. - // Get the token preceding the parameters - stack_token = stack[stack.length-1]; - - if (stack_token === undefined || - (stack_token.type !== Twig.expression.type._function && - stack_token.type !== Twig.expression.type.filter && - stack_token.type !== Twig.expression.type.test && - stack_token.type !== Twig.expression.type.key.brackets)) { - - end_token.expression = true; - - // remove start and end token from stack - param_stack.pop(); - param_stack.shift(); - - end_token.params = param_stack; - - output.push(end_token); - } else { - // This should never be hit - end_token.expression = false; - stack_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 { - throw new Twig.Error("Unexpected subexpression end when token is not marked as an expression"); - } - } - }, - { - /** - * Match a parameter set start. - */ - type: Twig.expression.type.parameter.start, - regex: /^\(/, - next: Twig.expression.set.expressions.concat([Twig.expression.type.parameter.end]), - validate: function(match, tokens) { - var last_token = tokens[tokens.length - 1]; - // We can't use the regex to test if we follow a space because expression is trimmed - return last_token && (Twig.indexOf(Twig.expression.reservedWords, last_token.value.trim()) < 0); - }, - 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: /^\)/, - 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(); - } - - // Move contents of parens into preceding filter - var param_stack = []; - while(token.type !== Twig.expression.type.parameter.start) { - // Add token to arguments stack - param_stack.unshift(token); - token = output.pop(); - } - param_stack.unshift(token); - - var is_expression = false; - - // Get the token preceding the parameters - token = output[output.length-1]; - - 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)) { - - end_token.expression = true; - - // remove start and end token from stack - 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(); - // 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; - } - new_array.unshift(value); - } - - if (!array_ended) { - throw new Twig.Error("Expected end of parameter set."); - } - - stack.push(new_array); - } - } - }, - { - type: Twig.expression.type.slice, - regex: /^\[(\d*\:\d*)\]/, - next: Twig.expression.set.operations_extended, - compile: function(token, stack, output) { - var sliceRange = token.match[1].split(':'); - - //sliceStart can be undefined when we pass parameters to the slice filter later - var sliceStart = (sliceRange[0]) ? parseInt(sliceRange[0]) : undefined; - var sliceEnd = (sliceRange[1]) ? parseInt(sliceRange[1]) : undefined; - - token.value = 'slice'; - token.params = [sliceStart, sliceEnd]; - - //sliceEnd can't be undefined as the slice filter doesn't check for this, but it does check the length - //of the params array, so just shorten it. - if (!sliceEnd) { - token.params = [sliceStart]; - } - - output.push(token); - }, - parse: function(token, stack, context) { - var input = stack.pop(), - params = token.params; - - stack.push(Twig.filter.apply(this, [token.value, input, params])); - } - }, - { - /** - * Match an array start. - */ - 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 - }, - { - /** - * Match an array end. - */ - 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; - // pop tokens off the stack until the start of the object - for(;i >= 0; i--) { - stack_token = stack.pop(); - if (stack_token.type === Twig.expression.type.array.start) { - break; - } - 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(); - // 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; - } - new_array.unshift(value); - } - if (!array_ended) { - throw new Twig.Error("Expected end of array."); - } - - stack.push(new_array); - } - }, - // Token that represents the start of a hash map '}' - // - // Hash maps take the form: - // { "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: /^\{/, - next: Twig.expression.set.expressions.concat([Twig.expression.type.object.end]), - compile: Twig.expression.fn.compile.push_both, - parse: Twig.expression.fn.parse.push - }, - - // Token that represents the end of a Hash Map '}' - // - // This is where the logic for building the internal - // representation of a hash map is defined. - { - 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; - - // pop tokens off the stack until the start of the object - for(;i >= 0; i--) { - stack_token = stack.pop(); - if (stack_token && stack_token.type === Twig.expression.type.object.start) { - break; - } - 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(); - // 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.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 - // guarantee the order of keys - if (new_object._keys === undefined) new_object._keys = []; - new_object._keys.unshift(token.key); - - // reset value check - 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); - } - }, - - // Token representing a filter - // - // Filters can follow any expression and take the form: - // expression|filter(optional, args) - // - // Filter parsing is done in the Twig.filters namespace. - { - type: Twig.expression.type.filter, - // match a | then a letter or _, then any number of letters, numbers, _ or - - regex: /^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/, - 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, - // match any letter or _, then any number of letters, numbers, _ or - followed by ( - regex: /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/, - next: Twig.expression.type.parameter.start, - validate: function(match, tokens) { - // Make sure this function is not a reserved word - return match[1] && (Twig.indexOf(Twig.expression.reservedWords, match[1]) < 0); - }, - transform: function(match, tokens) { - return '('; - }, - compile: function(token, stack, output) { - var fn = token.match[1]; - token.fn = fn; - // cleanup token - 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]) { - // Get the function from the built-in functions - value = Twig.functions[fn].apply(this, params); - - } else if (typeof context[fn] == 'function') { - // Get the function from the user/context defined functions - value = context[fn].apply(context, params); - - } else { - throw new Twig.Error(fn + ' function does not exist and is not defined in the context'); - } - - stack.push(value); - } - }, - - // 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 - // exist in the context. - { - type: Twig.expression.type.variable, - // match any letter or _, then any number of letters, numbers, _ or - - regex: /^[a-zA-Z_][a-zA-Z0-9_]*/, - 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) { - // Get the variable from the context - var value = Twig.expression.resolve.apply(this, [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, next_token) { - 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 { - value = undefined; - } - } else { - var capitalize = function (value) { - return value.substr(0, 1).toUpperCase() + value.substr(1); - }; - - // Get the variable from the context - if (typeof object === 'object' && key in object) { - value = object[key]; - } else if (object["get" + capitalize(key)] !== undefined) { - value = object["get" + capitalize(key)]; - } else if (object["is" + capitalize(key)] !== undefined) { - value = object["is" + capitalize(key)]; - } else { - value = undefined; - } - } - - // When resolving an expression we need to pass next_token in case the expression is a function - stack.push(Twig.expression.resolve.apply(this, [value, context, params, next_token])); - } - }, - { - 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; - - // The expression stack for the key - token.stack = Twig.expression.compile({ - value: match - }).stack; - - output.push(token); - }, - parse: function(token, stack, context, next_token) { - // Evaluate key - var params = token.params && Twig.expression.parse.apply(this, [token.params, context]), - key = Twig.expression.parse.apply(this, [token.stack, context]), - object = stack.pop(), - value; - - 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) { - value = object[key]; - } else { - value = null; - } - - // When resolving an expression we need to pass next_token in case the expression is a function - stack.push(Twig.expression.resolve.apply(this, [value, object, params, next_token])); - } - }, - { - /** - * Match a null value. - */ - type: Twig.expression.type._null, - // match a number - regex: /^(null|NULL|none|NONE)/, - 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 - }, - { - /** - * Match the context - */ - 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); - } - }, - { - /** - * Match a boolean - */ - type: Twig.expression.type.bool, - regex: /^(true|TRUE|false|FALSE)/, - next: Twig.expression.set.operations, - compile: function(token, stack, output) { - token.value = (token.match[0].toLowerCase( ) === "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. - * - * @param {string} key The context object key. - * @param {Object} context The render context. - */ - Twig.expression.resolve = function(value, context, params, next_token) { - if (typeof value == 'function') { - /* - If value is a function, it will have been impossible during the compile stage to determine that a following - set of parentheses were parameters for this function. - - Those parentheses will have therefore been marked as an expression, with their own parameters, which really - belong to this function. - - Those parameters will also need parsing in case they are actually an expression to pass as parameters. - */ - if (next_token && next_token.type === Twig.expression.type.parameter.end) { - //When parsing these parameters, we need to get them all back, not just the last item on the stack. - var tokens_are_parameters = true; - - params = next_token.params && Twig.expression.parse.apply(this, [next_token.params, context, tokens_are_parameters]); - - //Clean up the parentheses tokens on the next loop - next_token.cleanup = true; - } - 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; - }; - - /** - * Extend the expression parsing functionality with a new definition. - * - * Token definitions follow this format: - * { - * type: One of Twig.expression.type.[type], either pre-defined or added using - * Twig.expression.extendType - * - * next: Array of types from Twig.expression.type that can follow this token, - * - * regex: A regex or array of regex's that should match the token. - * - * compile: function(token, stack, output) called when this token is being compiled. - * Should return an object with stack and output set. - * - * parse: function(token, stack, context) called when this token is being parsed. - * Should return an object with stack and context set. - * } - * - * @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.handler[definition.type] = definition; - }; - - // Extend with built-in expressions - 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 = [], - // Keep an offset of the location in the expression for error messages. - exp_offset = 0, - // The valid next tokens of the previous token - next = null, - // Match information - type, regex, regex_array, - // The possible next token for the match - token_next, - // Has a match been found from the definitions - match_found, invalid_matches = [], match_function; - - match_function = function () { - var match = Array.prototype.slice.apply(arguments), - 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) + - "...'" - ); - // Not a match, don't change the expression - return match[0]; - } - - // Validate the token if a validation function is provided - if (Twig.expression.handler[type].validate && - !Twig.expression.handler[type].validate(match, tokens)) { - return match[0]; - } - - invalid_matches = []; - - tokens.push({ - type: type, - value: match[0], - match: match - }); - - match_found = true; - next = token_next; - exp_offset += match[0].length; - - // Does the token need to return output back to the expression string - // e.g. a function match of cycle( might return the '(' back to the expression - // This allows look-ahead to differentiate between token types (e.g. functions and variable names) - if (Twig.expression.handler[type].transform) { - return Twig.expression.handler[type].transform(match, tokens); - } - 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; - Twig.log.trace("Checking type ", type, " on ", expression); - 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); - } - // An expression token has been matched. Break the for loop and start trying to - // match the next template (if expression isn't empty.) - if (match_found) { - break; - } - } - } - 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; - }; - - /** - * 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, - // Tokenize expression - tokens = Twig.expression.tokenize(expression), - token = null, - output = [], - stack = [], - token_template = null; - - Twig.log.trace("Twig.expression.compile: ", "Compiling ", expression); - - // Push tokens into RPN stack using the Shunting-yard algorithm - // See http://en.wikipedia.org/wiki/Shunting_yard_algorithm - - while (tokens.length > 0) { - token = tokens.shift(); - token_template = Twig.expression.handler[token.type]; - - Twig.log.trace("Twig.expression.compile: ", "Compiling ", token); - - // Compile the template - token_template.compile && token_template.compile(token, stack, output); - - Twig.log.trace("Twig.expression.compile: ", "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; - }; - - - /** - * Parse an RPN expression stack within a context. - * - * @param {Array} tokens An array of compiled expression tokens. - * @param {Object} context The render context to parse the tokens with. - * - * @return {Object} The result of parsing all the tokens. The result - * can be anything, String, Array, Object, etc... based on - * the given expression. - */ - Twig.expression.parse = function (tokens, context, tokens_are_parameters) { - var that = this; - - // If the token isn't an array, make it one. - if (!(tokens instanceof Array)) { - tokens = [tokens]; - } - - // The output stack - var stack = [], - next_token, - token_template = null, - loop_token_fixups = []; - - Twig.forEach(tokens, function (token, index) { - //If the token is marked for cleanup, we don't need to parse it - if (token.cleanup) { - return; - } - - //Determine the token that follows this one so that we can pass it to the parser - if (tokens.length > index + 1) { - next_token = tokens[index + 1]; - } - - token_template = Twig.expression.handler[token.type]; - - token_template.parse && token_template.parse.apply(that, [token, stack, context, next_token]); - - //Store any binary tokens for later if we are in a loop. - if (context.loop && token.type === Twig.expression.type.operator.binary) { - loop_token_fixups.push(token); - } - }); - - //Check every fixup and remove "key" as long as they still have "params". This covers the use case where - //a ":" operator is used in a loop with a "(expression):" statement. We need to be able to evaluate the expression - Twig.forEach(loop_token_fixups, function (loop_token_fixup) { - if (loop_token_fixup.params && loop_token_fixup.key) { - delete loop_token_fixup["key"]; - } - }); - - //If parse has been called with a set of tokens that are parameters, we need to return the whole stack, - //wrapped in an Array. - if (tokens_are_parameters) { - var params = []; - while (stack.length > 0) { - params.unshift(stack.pop()); - } - - stack.push(params); - } - - // Pop the final value off the stack - return stack.pop(); - }; - - return Twig; - - }; - - -/***/ }, -/* 4 */ -/***/ function(module, exports) { - - // ## twig.expression.operator.js - // - // This file handles operator lookups and parsing. - module.exports = function (Twig) { - "use strict"; - - /** - * Operator associativity constants. - */ - Twig.expression.operator = { - leftToRight: 'leftToRight', - rightToLeft: 'rightToLeft' - }; - - var containment = function(a, b) { - if (b === undefined || b === null) { - return null; - } else if (b.indexOf !== undefined) { - // String - 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; - } - }; - - /** - * 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 "..": - token.precidence = 20; - token.associativity = Twig.expression.operator.leftToRight; - break; - - case ',': - token.precidence = 18; - token.associativity = Twig.expression.operator.leftToRight; - break; - - // Ternary - case '?:': - 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 '>=': - case 'not in': - case 'in': - token.precidence = 8; - token.associativity = Twig.expression.operator.leftToRight; - break; - - case '~': // String concatination - 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("Failed to lookup operator: " + operator + " is an unknown operator."); - } - token.operator = operator; - 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; - - if (operator === '?') { - c = stack.pop(); - } - - b = stack.pop(); - if (operator !== 'not') { - a = stack.pop(); - } - - if (operator !== 'in' && operator !== 'not in') { - if (a && Array.isArray(a)) { - a = a.length; - } - - if (b && Array.isArray(b)) { - b = b.length; - } - } - - switch (operator) { - case ':': - // Ignore - break; - - case '?:': - if (Twig.lib.boolval(a)) { - stack.push(a); - } else { - stack.push(b); - } - break; - case '?': - if (a === undefined) { - //An extended ternary. - a = b; - b = c; - c = undefined; - } - - if (Twig.lib.boolval(a)) { - stack.push(b); - } else { - stack.push(c); - } - break; - - case '+': - b = parseFloat(b); - a = parseFloat(a); - stack.push(a + b); - break; - - case '-': - b = parseFloat(b); - a = parseFloat(a); - stack.push(a - b); - break; - - case '*': - b = parseFloat(b); - a = parseFloat(a); - stack.push(a * b); - break; - - case '/': - b = parseFloat(b); - a = parseFloat(a); - stack.push(a / b); - break; - - case '//': - b = parseFloat(b); - a = parseFloat(a); - stack.push(Math.floor(a / b)); - break; - - case '%': - b = parseFloat(b); - a = parseFloat(a); - stack.push(a % b); - break; - - case '~': - stack.push( (a != null ? a.toString() : "") - + (b != null ? b.toString() : "") ); - break; - - case 'not': - case '!': - stack.push(!Twig.lib.boolval(b)); - break; - - case '<': - stack.push(a < b); - break; - - case '<=': - stack.push(a <= b); - break; - - case '>': - stack.push(a > b); - break; - - case '>=': - stack.push(a >= b); - break; - - case '===': - stack.push(a === b); - break; - - case '==': - stack.push(a == b); - break; - - case '!==': - stack.push(a !== b); - break; - - case '!=': - stack.push(a != b); - break; - - case 'or': - stack.push(a || b); - break; - - case 'and': - stack.push(a && b); - break; - - case '**': - stack.push(Math.pow(a, b)); - break; - - case 'not in': - stack.push( !containment(a, b) ); - break; - - case 'in': - stack.push( containment(a, b) ); - break; - - case '..': - stack.push( Twig.functions.range(a, b) ); - break; - - default: - debugger; - throw new Twig.Error("Failed to parse operator: " + operator + " is an unknown operator."); - } - }; - - return Twig; - - }; - - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - // ## twig.filters.js - // - // This file handles parsing filters. - module.exports = function (Twig) { - - // Determine object type - function is(type, obj) { - var clas = Object.prototype.toString.call(obj).slice(8, -1); - return obj !== undefined && obj !== null && clas === type; - } - - Twig.filters = { - // String Filters - upper: function(value) { - 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; - } - }, - - // 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 (is("Object", value)) { - 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 (is('Object', value)) { - // Sorting objects isn't obvious since the order of - // returned keys isn't guaranteed in 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) { - var a1, a2; - - // if a and b are comparable, we're fine :-) - if((value[a] > value[b]) == !(value[a] <= value[b])) { - return value[a] > value[b] ? 1 : - value[a] < value[b] ? -1 : - 0; - } - // if a and b can be parsed as numbers, we can compare - // their numeric value - else if(!isNaN(a1 = parseFloat(value[a])) && - !isNaN(b1 = parseFloat(value[b]))) { - return a1 > b1 ? 1 : - a1 < b1 ? -1 : - 0; - } - // if one of the values is a string, we convert the - // other value to string as well - else if(typeof value[a] == 'string') { - return value[a] > value[b].toString() ? 1 : - value[a] < value[b].toString() ? -1 : - 0; - } - else if(typeof value[b] == 'string') { - return value[a].toString() > value[b] ? 1 : - value[a].toString() < value[b] ? -1 : - 0; - } - // everything failed - return 'null' as sign, that - // the values are not comparable - else { - return null; - } - }); - 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; // Ignore the _keys property - if (value.hasOwnProperty(key)) { - output.push(key); - } - }); - return output; - }, - url_encode: function(value) { - if (value === undefined || value === null){ - return; - } - - var result = encodeURIComponent(value); - result = result.replace("'", "%27"); - return result; - }, - join: function(value, params) { - if (value === undefined || value === null){ - return; - } - - var join_str = "", - output = [], - keyset = null; - - if (params && params[0]) { - join_str = params[0]; - } - if (is("Array", value)) { - 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)) { - 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 === '' ) { - if (params === undefined) { - return ''; - } - - return params[0]; - } else { - return value; - } - }, - json_encode: function(value) { - if(value === undefined || value === null) { - return "null"; - } - else if ((typeof value == 'object') && (is("Array", value))) { - output = []; - - Twig.forEach(value, function(v) { - output.push(Twig.filters.json_encode(v)); - }); - - return "[" + output.join(",") + "]"; - } - else if (typeof value == 'object') { - var keyset = value._keys || Object.keys(value), - output = []; - - Twig.forEach(keyset, function(key) { - output.push(JSON.stringify(key) + ":" + Twig.filters.json_encode(value[key])); - }); - - return "{" + output.join(",") + "}"; - } - else { - return JSON.stringify(value); - } - }, - merge: function(value, params) { - var obj = [], - arr_index = 0, - keyset = []; - - // Check to see if all the objects being merged are arrays - if (!is("Array", value)) { - // Create obj as an Object - obj = { }; - } else { - Twig.forEach(params, function(param) { - if (!is("Array", param)) { - obj = { }; - } - }); - } - if (!is("Array", obj)) { - obj._keys = []; - } - - if (is("Array", value)) { - 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); - - // 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" - var int_key = parseInt(key, 10); - if (!isNaN(int_key) && int_key >= arr_index) { - arr_index = int_key + 1; - } - }); - } - - // mixin the merge arrays - Twig.forEach(params, function(param) { - if (is("Array", param)) { - 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) { - var date = Twig.functions.date(value); - var format = params && params.length ? params[0] : 'F j, Y H:i'; - return Twig.lib.date(format, date); - }, - - 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() / 1000); - } - 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 * 1000); - }, - - 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, params) { - if (value === undefined|| value === null){ - return; - } - - var strategy = "html"; - if(params && params.length && params[0] !== true) - strategy = params[0]; - - if(strategy == "html") { - var raw_value = value.toString().replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); - return Twig.Markup(raw_value, 'html'); - } else if(strategy == "js") { - var raw_value = value.toString(); - var result = ""; - - for(var i = 0; i < raw_value.length; i++) { - if(raw_value[i].match(/^[a-zA-Z0-9,\._]$/)) - result += raw_value[i]; - else { - var char_code = raw_value.charCodeAt(i); - - if(char_code < 0x80) - result += "\\x" + char_code.toString(16).toUpperCase(); - else - result += Twig.lib.sprintf("\\u%04s", char_code.toString(16).toUpperCase()); - } - } - - return Twig.Markup(result, 'js'); - } else if(strategy == "css") { - var raw_value = value.toString(); - var result = ""; - - for(var i = 0; i < raw_value.length; i++) { - if(raw_value[i].match(/^[a-zA-Z0-9]$/)) - result += raw_value[i]; - else { - var char_code = raw_value.charCodeAt(i); - result += "\\" + char_code.toString(16).toUpperCase() + " "; - } - } - - return Twig.Markup(result, 'css'); - } else if(strategy == "url") { - var result = Twig.filters.url_encode(value); - return Twig.Markup(result, 'url'); - } else if(strategy == "html_attr") { - var raw_value = value.toString(); - var result = ""; - - for(var i = 0; i < raw_value.length; i++) { - if(raw_value[i].match(/^[a-zA-Z0-9,\.\-_]$/)) - result += raw_value[i]; - else if(raw_value[i].match(/^[&<>"]$/)) - result += raw_value[i].replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, """); - else { - var char_code = raw_value.charCodeAt(i); - - // The following replaces characters undefined in HTML with - // the hex entity for the Unicode replacement character. - if(char_code <= 0x1f && char_code != 0x09 && char_code != 0x0a && char_code != 0x0d) - result += "�"; - else if(char_code < 0x80) - result += Twig.lib.sprintf("&#x%02s;", char_code.toString(16).toUpperCase()); - else - result += Twig.lib.sprintf("&#x%04s;", char_code.toString(16).toUpperCase()); - } - } - - return Twig.Markup(result, 'html_attr'); - } else { - throw new Twig.Error("escape strategy unsupported"); - } - }, - - /* Alias of escape */ - "e": function(value, params) { - return Twig.filters.escape(value, params); - }, - - nl2br: function(value) { - if (value === undefined || value === null){ - return; - } - 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); - - value = Twig.lib.replaceAll(value, linebreak_tag, "\n"); - - return Twig.Markup(value); - }, - - /** - * 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; - }; - // 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); - } - if ((s[1] || '').length < prec) { - s[1] = s[1] || ''; - s[1] += new Array(prec - s[1].length + 1).join('0'); - } - return s.join(dec); - }, - - trim: function(value, params) { - if (value === undefined|| value === null){ - return; - } - - 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'; - } - for (var i = 0; i < str.length; i++) { - if (whitespace.indexOf(str.charAt(i)) === -1) { - str = str.substring(i); - 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 : ''; - }, - - truncate: function (value, params) { - var length = 30, - preserve = false, - separator = '...'; - - value = value + ''; - if (params) { - if (params[0]) { - length = params[0]; - } - if (params[1]) { - preserve = params[1]; - } - if (params[2]) { - separator = params[2]; - } - } - - if (value.length > length) { - - if (preserve) { - length = value.indexOf(' ', length); - if (length === -1) { - return value; - } - } - - value = value.substr(0, length) + separator; - } - - return value; - }, - - 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"); - } - - // default to start of string - var start = params[0] || 0; - // default to length of string - var length = params.length > 1 ? params[1] : value.length; - // handle negative start values - 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++) { - 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"); - } - }, - - abs: function(value) { - if (value === undefined || value === null) { - return; - } - - return Math.abs(value); - }, - - first: function(value) { - if (is("Array", value)) { - return value[0]; - } else if (is("Object", value)) { - if ('_keys' in value) { - return value[value._keys[0]]; - } - } else if ( typeof value === "string" ) { - return value.substr(0, 1); - } - - 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 (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 == '') { - // empty delimiter - // "aabbcc"|split('', 2) - // -> ['aa', 'bb', 'cc'] - - while(split.length > 0) { - var temp = ""; - for (var i=0; i<limit && split.length > 0; i++) { - temp += split.shift(); - } - limitedSplit.push(temp); - } - - } else { - // non-empty delimiter - // "one,two,three,four,five"|split(',', 3) - // -> ['one', 'two', 'three,four,five'] - - for (var i=0; i<limit-1 && split.length > 0; 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]]; - } - - // string|array - return value[value.length - 1]; - }, - raw: function(value) { - return Twig.Markup(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; - - }; - - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - // ## twig.functions.js - // - // This file handles parsing filters. - module.exports = function (Twig) { - /** - * @constant - * @type {string} - */ - var TEMPLATE_NOT_FOUND_MESSAGE = 'Template "{name}" is not defined.'; - - // Determine object type - function is(type, obj) { - var clas = Object.prototype.toString.call(obj).slice(8, -1); - return obj !== undefined && obj !== null && clas === type; - } - - Twig.functions = { - // attribute, block, constant, date, dump, parent, random,. - - // Range function from http://phpjs.org/functions/range:499 - // Used under an MIT License - range: function (low, high, step) { - // http://kevin.vanzonneveld.net - // + original by: Waldo Malqui Silva - // * example 1: range ( 0, 12 ); - // * returns 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] - // * 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'] - 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); - } - }; - - // handle no argument case by dumping the entire render context - 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 || date === null || date === "") { - dateObj = new Date(); - } else if (Twig.lib.is("Date", date)) { - dateObj = date; - } else if (Twig.lib.is("String", date)) { - if (date.match(/^[0-9]+$/)) { - dateObj = new Date(date * 1000); - } - else { - 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); - } - return dateObj; - }, - block: function(block) { - if (this.originalBlockTokens[block]) { - return Twig.logic.parse.apply(this, [this.originalBlockTokens[block], this.context]).output; - } else { - return this.blocks[block]; - } - }, - parent: function() { - // Add a placeholder - return Twig.placeholders.parent; - }, - attribute: function(object, method, params) { - if (Twig.lib.is('Object', object)) { - if (object.hasOwnProperty(method)) { - if (typeof object[method] === "function") { - return object[method].apply(undefined, params); - } - else { - return object[method]; - } - } - } - // Array will return element 0-index - return object[method] || undefined; - }, - max: function(values) { - if(Twig.lib.is("Object", values)) { - delete values["_keys"]; - return Twig.lib.max(values); - } - - return Twig.lib.max.apply(null, arguments); - }, - min: function(values) { - if(Twig.lib.is("Object", values)) { - delete values["_keys"]; - return Twig.lib.min(values); - } - - return Twig.lib.min.apply(null, arguments); - }, - template_from_string: function(template) { - if (template === undefined) { - template = ''; - } - return Twig.Templates.parsers.twig({ - options: this.options, - data: template - }); - }, - random: function(value) { - var LIMIT_INT31 = 0x80000000; - - function getRandomNumber(n) { - var random = Math.floor(Math.random() * LIMIT_INT31); - var limits = [0, n]; - var min = Math.min.apply(null, limits), - max = Math.max.apply(null, limits); - return min + Math.floor((max - min + 1) * random / LIMIT_INT31); - } - - if(Twig.lib.is("Number", value)) { - return getRandomNumber(value); - } - - if(Twig.lib.is("String", value)) { - return value.charAt(getRandomNumber(value.length-1)); - } - - if(Twig.lib.is("Array", value)) { - return value[getRandomNumber(value.length-1)]; - } - - if(Twig.lib.is("Object", value)) { - var keys = Object.keys(value); - return value[keys[getRandomNumber(keys.length-1)]]; - } - - return getRandomNumber(LIMIT_INT31-1); - }, - - /** - * Returns the content of a template without rendering it - * @param {string} name - * @param {boolean} [ignore_missing=false] - * @returns {string} - */ - source: function(name, ignore_missing) { - var templateSource; - var templateFound = false; - var isNodeEnvironment = typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof window === 'undefined'; - var loader; - var path; - - //if we are running in a node.js environment, set the loader to 'fs' and ensure the - // path is relative to the CWD of the running script - //else, set the loader to 'ajax' and set the path to the value of name - if (isNodeEnvironment) { - loader = 'fs'; - path = __dirname + '/' + name; - } else { - loader = 'ajax'; - path = name; - } - - //build the params object - var params = { - id: name, - path: path, - method: loader, - parser: 'source', - async: false, - fetchTemplateSource: true - }; - - //default ignore_missing to false - if (typeof ignore_missing === 'undefined') { - ignore_missing = false; - } - - //try to load the remote template - // - //on exception, log it - try { - templateSource = Twig.Templates.loadRemote(name, params); - - //if the template is undefined or null, set the template to an empty string and do NOT flip the - // boolean indicating we found the template - // - //else, all is good! flip the boolean indicating we found the template - if (typeof templateSource === 'undefined' || templateSource === null) { - templateSource = ''; - } else { - templateFound = true; - } - } catch (e) { - Twig.log.debug('Twig.functions.source: ', 'Problem loading template ', e); - } - - //if the template was NOT found AND we are not ignoring missing templates, return the same message - // that is returned by the PHP implementation of the twig source() function - // - //else, return the template source - if (!templateFound && !ignore_missing) { - return TEMPLATE_NOT_FOUND_MESSAGE.replace('{name}', name); - } else { - return templateSource; - } - } - }; - - 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; - - }; - - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - // ## twig.lib.js - // - // This file contains 3rd party libraries used within twig. - // - // Copies of the licenses for the code included here can be found in the - // LICENSES.md file. - // - - module.exports = function(Twig) { - - // Namespace for libraries - Twig.lib = { }; - - Twig.lib.sprintf = __webpack_require__(8); - Twig.lib.vsprintf = __webpack_require__(9); - Twig.lib.round = __webpack_require__(10); - Twig.lib.max = __webpack_require__(11); - Twig.lib.min = __webpack_require__(12); - Twig.lib.strip_tags = __webpack_require__(13); - Twig.lib.strtotime = __webpack_require__(14); - Twig.lib.date = __webpack_require__(15); - Twig.lib.boolval = __webpack_require__(16); - - 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 = {}, - key; - for (key in src) - target[key] = src[key]; - - return target; - }; - - Twig.lib.extend = function (src, add) { - var keys = Object.keys(add), - i; - - i = keys.length; - - while (i--) { - src[keys[i]] = add[keys[i]]; - } - - return src; - }; - - 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; - - if (size < 1 || !Twig.lib.is("Array", arr)) { - return []; - } - - while (x < len) { - returnVal.push(arr.slice(x, x += size)); - } - - return returnVal; - }; - - return Twig; - }; - - -/***/ }, -/* 8 */ -/***/ function(module, exports) { - - 'use strict'; - - module.exports = function sprintf() { - // discuss at: http://locutus.io/php/sprintf/ - // original by: Ash Searle (http://hexmen.com/blog/) - // improved by: Michael White (http://getsprink.com) - // improved by: Jack - // improved by: Kevin van Zonneveld (http://kvz.io) - // improved by: Kevin van Zonneveld (http://kvz.io) - // improved by: Kevin van Zonneveld (http://kvz.io) - // improved by: Dj - // improved by: Allidylls - // input by: Paulo Freitas - // input by: Brett Zamir (http://brett-zamir.me) - // example 1: sprintf("%01.2f", 123.1) - // returns 1: '123.10' - // example 2: sprintf("[%10s]", 'monkey') - // returns 2: '[ monkey]' - // example 3: sprintf("[%'#10s]", 'monkey') - // returns 3: '[####monkey]' - // example 4: sprintf("%d", 123456789012345) - // returns 4: '123456789012345' - // example 5: sprintf('%-03s', 'E') - // returns 5: 'E00' - - var regex = /%%|%(\d+\$)?([\-+'#0 ]*)(\*\d+\$|\*|\d+)?(?:\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g; - var a = arguments; - var i = 0; - var format = a[i++]; - - var _pad = function _pad(str, len, chr, leftJustify) { - if (!chr) { - chr = ' '; - } - var padding = str.length >= len ? '' : new Array(1 + len - str.length >>> 0).join(chr); - return leftJustify ? str + padding : padding + str; - }; - - var justify = function justify(value, prefix, leftJustify, minWidth, zeroPad, customPadChar) { - var diff = minWidth - value.length; - if (diff > 0) { - if (leftJustify || !zeroPad) { - value = _pad(value, minWidth, customPadChar, leftJustify); - } else { - value = [value.slice(0, prefix.length), _pad('', diff, '0', true), value.slice(prefix.length)].join(''); - } - } - return value; - }; - - var _formatBaseX = function _formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad) { - // Note: casts negative numbers to positive ones - var number = value >>> 0; - prefix = prefix && number && { - '2': '0b', - '8': '0', - '16': '0x' - }[base] || ''; - value = prefix + _pad(number.toString(base), precision || 0, '0', false); - return justify(value, prefix, leftJustify, minWidth, zeroPad); - }; - - // _formatString() - var _formatString = function _formatString(value, leftJustify, minWidth, precision, zeroPad, customPadChar) { - if (precision !== null && precision !== undefined) { - value = value.slice(0, precision); - } - return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar); - }; - - // doFormat() - var doFormat = function doFormat(substring, valueIndex, flags, minWidth, precision, type) { - var number, prefix, method, textTransform, value; - - if (substring === '%%') { - return '%'; - } - - // parse flags - var leftJustify = false; - var positivePrefix = ''; - var zeroPad = false; - var prefixBaseX = false; - var customPadChar = ' '; - var flagsl = flags.length; - var j; - for (j = 0; j < flagsl; j++) { - switch (flags.charAt(j)) { - case ' ': - positivePrefix = ' '; - break; - case '+': - positivePrefix = '+'; - break; - case '-': - leftJustify = true; - break; - case "'": - customPadChar = flags.charAt(j + 1); - break; - case '0': - zeroPad = true; - customPadChar = '0'; - break; - case '#': - prefixBaseX = true; - break; - } - } - - // parameters may be null, undefined, empty-string or real valued - // we want to ignore null, undefined and empty-string values - if (!minWidth) { - minWidth = 0; - } else if (minWidth === '*') { - minWidth = +a[i++]; - } else if (minWidth.charAt(0) === '*') { - minWidth = +a[minWidth.slice(1, -1)]; - } else { - minWidth = +minWidth; - } - - // Note: undocumented perl feature: - if (minWidth < 0) { - minWidth = -minWidth; - leftJustify = true; - } - - if (!isFinite(minWidth)) { - throw new Error('sprintf: (minimum-)width must be finite'); - } - - if (!precision) { - precision = 'fFeE'.indexOf(type) > -1 ? 6 : type === 'd' ? 0 : undefined; - } else if (precision === '*') { - precision = +a[i++]; - } else if (precision.charAt(0) === '*') { - precision = +a[precision.slice(1, -1)]; - } else { - precision = +precision; - } - - // grab value using valueIndex if required? - value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; - - switch (type) { - case 's': - return _formatString(value + '', leftJustify, minWidth, precision, zeroPad, customPadChar); - case 'c': - return _formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad); - case 'b': - return _formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad); - case 'o': - return _formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad); - case 'x': - return _formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad); - case 'X': - return _formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase(); - case 'u': - return _formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad); - case 'i': - case 'd': - number = +value || 0; - // Plain Math.round doesn't just truncate - number = Math.round(number - number % 1); - prefix = number < 0 ? '-' : positivePrefix; - value = prefix + _pad(String(Math.abs(number)), precision, '0', false); - return justify(value, prefix, leftJustify, minWidth, zeroPad); - case 'e': - case 'E': - case 'f': // @todo: Should handle locales (as per setlocale) - case 'F': - case 'g': - case 'G': - number = +value; - prefix = number < 0 ? '-' : positivePrefix; - method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; - textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; - value = prefix + Math.abs(number)[method](precision); - return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform](); - default: - return substring; - } - }; - - return format.replace(regex, doFormat); - }; - //# sourceMappingURL=sprintf.js.map - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - module.exports = function vsprintf(format, args) { - // discuss at: http://locutus.io/php/vsprintf/ - // original by: ejsanders - // example 1: vsprintf('%04d-%02d-%02d', [1988, 8, 1]) - // returns 1: '1988-08-01' - - var sprintf = __webpack_require__(8); - - return sprintf.apply(this, [format].concat(args)); - }; - //# sourceMappingURL=vsprintf.js.map - -/***/ }, -/* 10 */ -/***/ function(module, exports) { - - 'use strict'; - - module.exports = function round(value, precision, mode) { - // discuss at: http://locutus.io/php/round/ - // original by: Philip Peterson - // revised by: Onno Marsman (https://twitter.com/onnomarsman) - // 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 1: Great work. Ideas for improvement: - // note 1: - code more compliant with developer guidelines - // note 1: - for implementing PHP constant arguments look at - // note 1: the pathinfo() function, it offers the greatest - // note 1: 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 - // making sure precision is integer - precision |= 0; - m = Math.pow(10, precision); - value *= m; - // sign of the number - sgn = value > 0 | -(value < 0); - isHalf = value % 1 === 0.5 * sgn; - f = Math.floor(value); - - if (isHalf) { - switch (mode) { - case 'PHP_ROUND_HALF_DOWN': - // rounds .5 toward zero - value = f + (sgn < 0); - break; - case 'PHP_ROUND_HALF_EVEN': - // rouds .5 towards the next even integer - value = f + f % 2 * sgn; - break; - case 'PHP_ROUND_HALF_ODD': - // rounds .5 towards the next odd integer - value = f + !(f % 2); - break; - default: - // rounds .5 away from zero - value = f + (sgn > 0); - } - } - - return (isHalf ? value : Math.round(value)) / m; - }; - //# sourceMappingURL=round.js.map - -/***/ }, -/* 11 */ -/***/ function(module, exports) { - - 'use strict'; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; - - module.exports = function max() { - // discuss at: http://locutus.io/php/max/ - // original by: Onno Marsman (https://twitter.com/onnomarsman) - // revised by: Onno Marsman (https://twitter.com/onnomarsman) - // improved by: Jack - // note 1: Long code cause we're aiming for maximum PHP compatibility - // example 1: max(1, 3, 5, 6, 7) - // returns 1: 7 - // example 2: max([2, 4, 5]) - // returns 2: 5 - // example 3: max(0, 'hello') - // returns 3: 0 - // example 4: max('hello', 0) - // returns 4: 'hello' - // example 5: max(-1, 'hello') - // returns 5: 'hello' - // example 6: max([2, 4, 8], [2, 5, 7]) - // returns 6: [2, 5, 7] - - var ar; - var retVal; - var i = 0; - var n = 0; - var argv = arguments; - var argc = argv.length; - var _obj2Array = function _obj2Array(obj) { - if (Object.prototype.toString.call(obj) === '[object Array]') { - return obj; - } else { - var ar = []; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - ar.push(obj[i]); - } - } - return ar; - } - }; - var _compare = function _compare(current, next) { - var i = 0; - var n = 0; - var tmp = 0; - var nl = 0; - var cl = 0; - - if (current === next) { - return 0; - } else if ((typeof current === 'undefined' ? 'undefined' : _typeof(current)) === 'object') { - if ((typeof next === 'undefined' ? 'undefined' : _typeof(next)) === 'object') { - current = _obj2Array(current); - next = _obj2Array(next); - cl = current.length; - nl = next.length; - if (nl > cl) { - return 1; - } else if (nl < cl) { - return -1; - } - for (i = 0, n = cl; i < n; ++i) { - tmp = _compare(current[i], next[i]); - if (tmp === 1) { - return 1; - } else if (tmp === -1) { - return -1; - } - } - return 0; - } - return -1; - } else if ((typeof next === 'undefined' ? 'undefined' : _typeof(next)) === 'object') { - return 1; - } else if (isNaN(next) && !isNaN(current)) { - if (current === 0) { - return 0; - } - return current < 0 ? 1 : -1; - } else if (isNaN(current) && !isNaN(next)) { - if (next === 0) { - return 0; - } - return next > 0 ? 1 : -1; - } - - if (next === current) { - return 0; - } - - return next > current ? 1 : -1; - }; - - if (argc === 0) { - throw new Error('At least one value should be passed to max()'); - } else if (argc === 1) { - if (_typeof(argv[0]) === 'object') { - ar = _obj2Array(argv[0]); - } else { - throw new Error('Wrong parameter count for max()'); - } - if (ar.length === 0) { - throw new Error('Array must contain at least one element for max()'); - } - } else { - ar = argv; - } - - retVal = ar[0]; - for (i = 1, n = ar.length; i < n; ++i) { - if (_compare(retVal, ar[i]) === 1) { - retVal = ar[i]; - } - } - - return retVal; - }; - //# sourceMappingURL=max.js.map - -/***/ }, -/* 12 */ -/***/ function(module, exports) { - - 'use strict'; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; - - module.exports = function min() { - // discuss at: http://locutus.io/php/min/ - // original by: Onno Marsman (https://twitter.com/onnomarsman) - // revised by: Onno Marsman (https://twitter.com/onnomarsman) - // improved by: Jack - // note 1: Long code cause we're aiming for maximum PHP compatibility - // example 1: min(1, 3, 5, 6, 7) - // returns 1: 1 - // example 2: min([2, 4, 5]) - // returns 2: 2 - // example 3: min(0, 'hello') - // returns 3: 0 - // example 4: min('hello', 0) - // returns 4: 'hello' - // example 5: min(-1, 'hello') - // returns 5: -1 - // example 6: min([2, 4, 8], [2, 5, 7]) - // returns 6: [2, 4, 8] - - var ar; - var retVal; - var i = 0; - var n = 0; - var argv = arguments; - var argc = argv.length; - var _obj2Array = function _obj2Array(obj) { - if (Object.prototype.toString.call(obj) === '[object Array]') { - return obj; - } - var ar = []; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - ar.push(obj[i]); - } - } - return ar; - }; - - var _compare = function _compare(current, next) { - var i = 0; - var n = 0; - var tmp = 0; - var nl = 0; - var cl = 0; - - if (current === next) { - return 0; - } else if ((typeof current === 'undefined' ? 'undefined' : _typeof(current)) === 'object') { - if ((typeof next === 'undefined' ? 'undefined' : _typeof(next)) === 'object') { - current = _obj2Array(current); - next = _obj2Array(next); - cl = current.length; - nl = next.length; - if (nl > cl) { - return 1; - } else if (nl < cl) { - return -1; - } - for (i = 0, n = cl; i < n; ++i) { - tmp = _compare(current[i], next[i]); - if (tmp === 1) { - return 1; - } else if (tmp === -1) { - return -1; - } - } - return 0; - } - return -1; - } else if ((typeof next === 'undefined' ? 'undefined' : _typeof(next)) === 'object') { - return 1; - } else if (isNaN(next) && !isNaN(current)) { - if (current === 0) { - return 0; - } - return current < 0 ? 1 : -1; - } else if (isNaN(current) && !isNaN(next)) { - if (next === 0) { - return 0; - } - return next > 0 ? 1 : -1; - } - - if (next === current) { - return 0; - } - - return next > current ? 1 : -1; - }; - - if (argc === 0) { - throw new Error('At least one value should be passed to min()'); - } else if (argc === 1) { - if (_typeof(argv[0]) === 'object') { - ar = _obj2Array(argv[0]); - } else { - throw new Error('Wrong parameter count for min()'); - } - - if (ar.length === 0) { - throw new Error('Array must contain at least one element for min()'); - } - } else { - ar = argv; - } - - retVal = ar[0]; - - for (i = 1, n = ar.length; i < n; ++i) { - if (_compare(retVal, ar[i]) === -1) { - retVal = ar[i]; - } - } - - return retVal; - }; - //# sourceMappingURL=min.js.map - -/***/ }, -/* 13 */ -/***/ function(module, exports) { - - 'use strict'; - - module.exports = function strip_tags(input, allowed) { - // eslint-disable-line camelcase - // discuss at: http://locutus.io/php/strip_tags/ - // original by: Kevin van Zonneveld (http://kvz.io) - // improved by: Luke Godfrey - // improved by: Kevin van Zonneveld (http://kvz.io) - // input by: Pul - // input by: Alex - // input by: Marc Palau - // input by: Brett Zamir (http://brett-zamir.me) - // input by: Bobby Drake - // input by: Evertjan Garretsen - // bugfixed by: Kevin van Zonneveld (http://kvz.io) - // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) - // bugfixed by: Kevin van Zonneveld (http://kvz.io) - // bugfixed by: Kevin van Zonneveld (http://kvz.io) - // bugfixed by: Eric Nagel - // bugfixed by: Kevin van Zonneveld (http://kvz.io) - // bugfixed by: Tomasz Wesolowski - // revised by: Rafał Kukawski (http://blog.kukawski.pl) - // example 1: strip_tags('<p>Kevin</p> <br /><b>van</b> <i>Zonneveld</i>', '<i><b>') - // returns 1: 'Kevin <b>van</b> <i>Zonneveld</i>' - // example 2: strip_tags('<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>', '<p>') - // returns 2: '<p>Kevin van Zonneveld</p>' - // example 3: strip_tags("<a href='http://kvz.io'>Kevin van Zonneveld</a>", "<a>") - // returns 3: "<a href='http://kvz.io'>Kevin van Zonneveld</a>" - // example 4: strip_tags('1 < 5 5 > 1') - // returns 4: '1 < 5 5 > 1' - // example 5: strip_tags('1 <br/> 1') - // returns 5: '1 1' - // example 6: strip_tags('1 <br/> 1', '<br>') - // returns 6: '1 <br/> 1' - // example 7: strip_tags('1 <br/> 1', '<br><br/>') - // returns 7: '1 <br/> 1' - - // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>) - allowed = (((allowed || '') + '').toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); - - var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi; - var commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi; - - return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) { - return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; - }); - }; - //# sourceMappingURL=strip_tags.js.map - -/***/ }, -/* 14 */ -/***/ function(module, exports) { - - 'use strict'; - - module.exports = function strtotime(text, now) { - // discuss at: http://locutus.io/php/strtotime/ - // original by: Caio Ariede (http://caioariede.com) - // improved by: Kevin van Zonneveld (http://kvz.io) - // improved by: Caio Ariede (http://caioariede.com) - // improved by: A. Matías Quezada (http://amatiasq.com) - // improved by: preuter - // improved by: Brett Zamir (http://brett-zamir.me) - // improved by: Mirko Faber - // input by: David - // bugfixed by: Wagner B. Soares - // bugfixed by: Artur Tchernychev - // bugfixed by: Stephan Bösch-Plepelits (http://github.com/plepe) - // note 1: Examples all have a fixed timestamp to prevent - // note 1: tests to fail because of variable time(zones) - // example 1: strtotime('+1 day', 1129633200) - // returns 1: 1129719600 - // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200) - // returns 2: 1130425202 - // example 3: strtotime('last month', 1129633200) - // returns 3: 1127041200 - // example 4: strtotime('2009-05-04 08:30:00 GMT') - // returns 4: 1241425800 - // example 5: strtotime('2009-05-04 08:30:00+00') - // returns 5: 1241425800 - // example 6: strtotime('2009-05-04 08:30:00+02:00') - // returns 6: 1241418600 - // example 7: strtotime('2009-05-04T08:30:00Z') - // returns 7: 1241425800 - - var parsed; - var match; - var today; - var year; - var date; - var days; - var ranges; - var len; - var times; - var regex; - var i; - var fail = false; - - if (!text) { - return fail; - } - - // Unecessary spaces - text = text.replace(/^\s+|\s+$/g, '').replace(/\s{2,}/g, ' ').replace(/[\t\r\n]/g, '').toLowerCase(); - - // in contrast to php, js Date.parse function interprets: - // dates given as yyyy-mm-dd as in timezone: UTC, - // dates with "." or "-" as MDY instead of DMY - // dates with two-digit years differently - // etc...etc... - // ...therefore we manually parse lots of common date formats - var pattern = new RegExp(['^(\\d{1,4})', '([\\-\\.\\/:])', '(\\d{1,2})', '([\\-\\.\\/:])', '(\\d{1,4})', '(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?', '(?:\\s([A-Z]+)?)?$'].join('')); - match = text.match(pattern); - - if (match && match[2] === match[4]) { - if (match[1] > 1901) { - switch (match[2]) { - case '-': - // YYYY-M-D - if (match[3] > 12 || match[5] > 31) { - return fail; - } - - return new Date(match[1], parseInt(match[3], 10) - 1, match[5], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - case '.': - // YYYY.M.D is not parsed by strtotime() - return fail; - case '/': - // YYYY/M/D - if (match[3] > 12 || match[5] > 31) { - return fail; - } - - return new Date(match[1], parseInt(match[3], 10) - 1, match[5], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - } else if (match[5] > 1901) { - switch (match[2]) { - case '-': - // D-M-YYYY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - - return new Date(match[5], parseInt(match[3], 10) - 1, match[1], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - case '.': - // D.M.YYYY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - - return new Date(match[5], parseInt(match[3], 10) - 1, match[1], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - case '/': - // M/D/YYYY - if (match[1] > 12 || match[3] > 31) { - return fail; - } - - return new Date(match[5], parseInt(match[1], 10) - 1, match[3], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - } else { - switch (match[2]) { - case '-': - // YY-M-D - if (match[3] > 12 || match[5] > 31 || match[1] < 70 && match[1] > 38) { - return fail; - } - - year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1]; - return new Date(year, parseInt(match[3], 10) - 1, match[5], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - case '.': - // D.M.YY or H.MM.SS - if (match[5] >= 70) { - // D.M.YY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - - return new Date(match[5], parseInt(match[3], 10) - 1, match[1], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - if (match[5] < 60 && !match[6]) { - // H.MM.SS - if (match[1] > 23 || match[3] > 59) { - return fail; - } - - today = new Date(); - return new Date(today.getFullYear(), today.getMonth(), today.getDate(), match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000; - } - - // invalid format, cannot be parsed - return fail; - case '/': - // M/D/YY - if (match[1] > 12 || match[3] > 31 || match[5] < 70 && match[5] > 38) { - return fail; - } - - year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5]; - return new Date(year, parseInt(match[1], 10) - 1, match[3], match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - case ':': - // HH:MM:SS - if (match[1] > 23 || match[3] > 59 || match[5] > 59) { - return fail; - } - - today = new Date(); - return new Date(today.getFullYear(), today.getMonth(), today.getDate(), match[1] || 0, match[3] || 0, match[5] || 0) / 1000; - } - } - } - - // other formats and "now" should be parsed by Date.parse() - if (text === 'now') { - return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0; - } - if (!isNaN(parsed = Date.parse(text))) { - return parsed / 1000 | 0; - } - // Browsers !== Chrome have problems parsing ISO 8601 date strings, as they do - // not accept lower case characters, space, or shortened time zones. - // Therefore, fix these problems and try again. - // Examples: - // 2015-04-15 20:33:59+02 - // 2015-04-15 20:33:59z - // 2015-04-15t20:33:59+02:00 - pattern = new RegExp(['^([0-9]{4}-[0-9]{2}-[0-9]{2})', '[ t]', '([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)', '([\\+-][0-9]{2}(:[0-9]{2})?|z)'].join('')); - match = text.match(pattern); - if (match) { - // @todo: time zone information - if (match[4] === 'z') { - match[4] = 'Z'; - } else if (match[4].match(/^([\+-][0-9]{2})$/)) { - match[4] = match[4] + ':00'; - } - - if (!isNaN(parsed = Date.parse(match[1] + 'T' + match[2] + match[4]))) { - return parsed / 1000 | 0; - } - } - - date = now ? new Date(now * 1000) : new Date(); - days = { - 'sun': 0, - 'mon': 1, - 'tue': 2, - 'wed': 3, - 'thu': 4, - 'fri': 5, - 'sat': 6 - }; - ranges = { - 'yea': 'FullYear', - 'mon': 'Month', - 'day': 'Date', - 'hou': 'Hours', - 'min': 'Minutes', - 'sec': 'Seconds' - }; - - function lastNext(type, range, modifier) { - var diff; - var day = days[range]; - - if (typeof day !== 'undefined') { - diff = day - date.getDay(); - - if (diff === 0) { - diff = 7 * modifier; - } else if (diff > 0 && type === 'last') { - diff -= 7; - } else if (diff < 0 && type === 'next') { - diff += 7; - } - - date.setDate(date.getDate() + diff); - } - } - - function process(val) { - // @todo: Reconcile this with regex using \s, taking into account - // browser issues with split and regexes - var splt = val.split(' '); - var type = splt[0]; - var range = splt[1].substring(0, 3); - var typeIsNumber = /\d+/.test(type); - var ago = splt[2] === 'ago'; - var num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1); - - if (typeIsNumber) { - num *= parseInt(type, 10); - } - - if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { - return date['set' + ranges[range]](date['get' + ranges[range]]() + num); - } - - if (range === 'wee') { - return date.setDate(date.getDate() + num * 7); - } - - if (type === 'next' || type === 'last') { - lastNext(type, range, num); - } else if (!typeIsNumber) { - return false; - } - - return true; - } - - times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'; - regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'; - - match = text.match(new RegExp(regex, 'gi')); - if (!match) { - return fail; - } - - for (i = 0, len = match.length; i < len; i++) { - if (!process(match[i])) { - return fail; - } - } - - return date.getTime() / 1000; - }; - //# sourceMappingURL=strtotime.js.map - -/***/ }, -/* 15 */ -/***/ function(module, exports) { - - 'use strict'; - - module.exports = function date(format, timestamp) { - // discuss at: http://locutus.io/php/date/ - // original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com) - // original by: gettimeofday - // parts by: Peter-Paul Koch (http://www.quirksmode.org/js/beat.html) - // improved by: Kevin van Zonneveld (http://kvz.io) - // improved by: MeEtc (http://yass.meetcweb.com) - // improved by: Brad Touesnard - // improved by: Tim Wiel - // improved by: Bryan Elliott - // improved by: David Randall - // improved by: Theriault (https://github.com/Theriault) - // improved by: Theriault (https://github.com/Theriault) - // improved by: Brett Zamir (http://brett-zamir.me) - // improved by: Theriault (https://github.com/Theriault) - // improved by: Thomas Beaucourt (http://www.webapp.fr) - // improved by: JT - // improved by: Theriault (https://github.com/Theriault) - // improved by: Rafał Kukawski (http://blog.kukawski.pl) - // improved by: Theriault (https://github.com/Theriault) - // input by: Brett Zamir (http://brett-zamir.me) - // input by: majak - // input by: Alex - // input by: Martin - // input by: Alex Wilson - // input by: Haravikk - // bugfixed by: Kevin van Zonneveld (http://kvz.io) - // bugfixed by: majak - // bugfixed by: Kevin van Zonneveld (http://kvz.io) - // bugfixed by: Brett Zamir (http://brett-zamir.me) - // bugfixed by: omid (http://locutus.io/php/380:380#comment_137122) - // bugfixed by: Chris (http://www.devotis.nl/) - // note 1: Uses global: locutus to store the default timezone - // note 1: Although the function potentially allows timezone info - // note 1: (see notes), it currently does not set - // note 1: per a timezone specified by date_default_timezone_set(). Implementers might use - // note 1: $locutus.currentTimezoneOffset and - // note 1: $locutus.currentTimezoneDST set by that function - // note 1: in order to adjust the dates in this function - // note 1: (or our other date functions!) accordingly - // example 1: date('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400) - // returns 1: '07:09:40 m is month' - // example 2: date('F j, Y, g:i a', 1062462400) - // returns 2: 'September 2, 2003, 12:26 am' - // example 3: date('Y W o', 1062462400) - // returns 3: '2003 36 2003' - // example 4: var $x = date('Y m d', (new Date()).getTime() / 1000) - // example 4: $x = $x + '' - // example 4: var $result = $x.length // 2009 01 09 - // returns 4: 10 - // example 5: date('W', 1104534000) - // returns 5: '52' - // example 6: date('B t', 1104534000) - // returns 6: '999 31' - // example 7: date('W U', 1293750000.82); // 2010-12-31 - // returns 7: '52 1293750000' - // example 8: date('W', 1293836400); // 2011-01-01 - // returns 8: '52' - // example 9: date('W Y-m-d', 1293974054); // 2011-01-02 - // returns 9: '52 2011-01-02' - // test: skip-1 skip-2 skip-5 - - var jsdate, f; - // Keep this here (works, but for code commented-out below for file size reasons) - // var tal= []; - var txtWords = ['Sun', 'Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - // trailing backslash -> (dropped) - // a backslash followed by any character (including backslash) -> the character - // empty string -> empty string - var formatChr = /\\?(.?)/gi; - var formatChrCb = function formatChrCb(t, s) { - return f[t] ? f[t]() : s; - }; - var _pad = function _pad(n, c) { - n = String(n); - while (n.length < c) { - n = '0' + n; - } - return n; - }; - f = { - // Day - d: function d() { - // Day of month w/leading 0; 01..31 - return _pad(f.j(), 2); - }, - D: function D() { - // Shorthand day name; Mon...Sun - return f.l().slice(0, 3); - }, - j: function j() { - // Day of month; 1..31 - return jsdate.getDate(); - }, - l: function l() { - // Full day name; Monday...Sunday - return txtWords[f.w()] + 'day'; - }, - N: function N() { - // ISO-8601 day of week; 1[Mon]..7[Sun] - return f.w() || 7; - }, - S: function S() { - // Ordinal suffix for day of month; st, nd, rd, th - var j = f.j(); - var i = j % 10; - if (i <= 3 && parseInt(j % 100 / 10, 10) === 1) { - i = 0; - } - return ['st', 'nd', 'rd'][i - 1] || 'th'; - }, - w: function w() { - // Day of week; 0[Sun]..6[Sat] - return jsdate.getDay(); - }, - z: function z() { - // Day of year; 0..365 - var a = new Date(f.Y(), f.n() - 1, f.j()); - var b = new Date(f.Y(), 0, 1); - return Math.round((a - b) / 864e5); - }, - - // Week - W: function W() { - // ISO-8601 week number - var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3); - var b = new Date(a.getFullYear(), 0, 4); - return _pad(1 + Math.round((a - b) / 864e5 / 7), 2); - }, - - // Month - F: function F() { - // Full month name; January...December - return txtWords[6 + f.n()]; - }, - m: function m() { - // Month w/leading 0; 01...12 - return _pad(f.n(), 2); - }, - M: function M() { - // Shorthand month name; Jan...Dec - return f.F().slice(0, 3); - }, - n: function n() { - // Month; 1...12 - return jsdate.getMonth() + 1; - }, - t: function t() { - // Days in month; 28...31 - return new Date(f.Y(), f.n(), 0).getDate(); - }, - - // Year - L: function L() { - // Is leap year?; 0 or 1 - var j = f.Y(); - return j % 4 === 0 & j % 100 !== 0 | j % 400 === 0; - }, - o: function o() { - // ISO-8601 year - var n = f.n(); - var W = f.W(); - var Y = f.Y(); - return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0); - }, - Y: function Y() { - // Full year; e.g. 1980...2010 - return jsdate.getFullYear(); - }, - y: function y() { - // Last two digits of year; 00...99 - return f.Y().toString().slice(-2); - }, - - // Time - a: function a() { - // am or pm - return jsdate.getHours() > 11 ? 'pm' : 'am'; - }, - A: function A() { - // AM or PM - return f.a().toUpperCase(); - }, - B: function B() { - // Swatch Internet time; 000..999 - var H = jsdate.getUTCHours() * 36e2; - // Hours - var i = jsdate.getUTCMinutes() * 60; - // Minutes - // Seconds - var s = jsdate.getUTCSeconds(); - return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3); - }, - g: function g() { - // 12-Hours; 1..12 - return f.G() % 12 || 12; - }, - G: function G() { - // 24-Hours; 0..23 - return jsdate.getHours(); - }, - h: function h() { - // 12-Hours w/leading 0; 01..12 - return _pad(f.g(), 2); - }, - H: function H() { - // 24-Hours w/leading 0; 00..23 - return _pad(f.G(), 2); - }, - i: function i() { - // Minutes w/leading 0; 00..59 - return _pad(jsdate.getMinutes(), 2); - }, - s: function s() { - // Seconds w/leading 0; 00..59 - return _pad(jsdate.getSeconds(), 2); - }, - u: function u() { - // Microseconds; 000000-999000 - return _pad(jsdate.getMilliseconds() * 1000, 6); - }, - - // Timezone - e: function e() { - // Timezone identifier; e.g. Atlantic/Azores, ... - // The following works, but requires inclusion of the very large - // timezone_abbreviations_list() function. - /* return that.date_default_timezone_get(); - */ - var msg = 'Not supported (see source code of date() for timezone on how to add support)'; - throw new Error(msg); - }, - I: function I() { - // DST observed?; 0 or 1 - // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC. - // If they are not equal, then DST is observed. - var a = new Date(f.Y(), 0); - // Jan 1 - var c = Date.UTC(f.Y(), 0); - // Jan 1 UTC - var b = new Date(f.Y(), 6); - // Jul 1 - // Jul 1 UTC - var d = Date.UTC(f.Y(), 6); - return a - c !== b - d ? 1 : 0; - }, - O: function O() { - // Difference to GMT in hour format; e.g. +0200 - var tzo = jsdate.getTimezoneOffset(); - var a = Math.abs(tzo); - return (tzo > 0 ? '-' : '+') + _pad(Math.floor(a / 60) * 100 + a % 60, 4); - }, - P: function P() { - // Difference to GMT w/colon; e.g. +02:00 - var O = f.O(); - return O.substr(0, 3) + ':' + O.substr(3, 2); - }, - T: function T() { - // The following works, but requires inclusion of the very - // large timezone_abbreviations_list() function. - /* var abbr, i, os, _default; - if (!tal.length) { - tal = that.timezone_abbreviations_list(); - } - if ($locutus && $locutus.default_timezone) { - _default = $locutus.default_timezone; - for (abbr in tal) { - for (i = 0; i < tal[abbr].length; i++) { - if (tal[abbr][i].timezone_id === _default) { - return abbr.toUpperCase(); - } - } - } - } - for (abbr in tal) { - for (i = 0; i < tal[abbr].length; i++) { - os = -jsdate.getTimezoneOffset() * 60; - if (tal[abbr][i].offset === os) { - return abbr.toUpperCase(); - } - } - } - */ - return 'UTC'; - }, - Z: function Z() { - // Timezone offset in seconds (-43200...50400) - return -jsdate.getTimezoneOffset() * 60; - }, - - // Full Date/Time - c: function c() { - // ISO-8601 date. - return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); - }, - r: function r() { - // RFC 2822 - return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); - }, - U: function U() { - // Seconds since UNIX epoch - return jsdate / 1000 | 0; - } - }; - - var _date = function _date(format, timestamp) { - jsdate = timestamp === undefined ? new Date() // Not provided - : timestamp instanceof Date ? new Date(timestamp) // JS Date() - : new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int) - ; - return format.replace(formatChr, formatChrCb); - }; - - return _date(format, timestamp); - }; - //# sourceMappingURL=date.js.map - -/***/ }, -/* 16 */ -/***/ function(module, exports) { - - 'use strict'; - - module.exports = function boolval(mixedVar) { - // original by: Will Rowe - // example 1: boolval(true) - // returns 1: true - // example 2: boolval(false) - // returns 2: false - // example 3: boolval(0) - // returns 3: false - // example 4: boolval(0.0) - // returns 4: false - // example 5: boolval('') - // returns 5: false - // example 6: boolval('0') - // returns 6: false - // example 7: boolval([]) - // returns 7: false - // example 8: boolval('') - // returns 8: false - // example 9: boolval(null) - // returns 9: false - // example 10: boolval(undefined) - // returns 10: false - // example 11: boolval('true') - // returns 11: true - - if (mixedVar === false) { - return false; - } - - if (mixedVar === 0 || mixedVar === 0.0) { - return false; - } - - if (mixedVar === '' || mixedVar === '0') { - return false; - } - - if (Array.isArray(mixedVar) && mixedVar.length === 0) { - return false; - } - - if (mixedVar === null || mixedVar === undefined) { - return false; - } - - return true; - }; - //# sourceMappingURL=boolval.js.map - -/***/ }, -/* 17 */ -/***/ function(module, exports) { - - module.exports = function(Twig) { - 'use strict'; - - Twig.Templates.registerLoader('ajax', function(location, params, callback, error_callback) { - var template, - xmlhttp, - precompiled = params.precompiled, - parser = this.parsers[params.parser] || this.parser.twig; - - if (typeof XMLHttpRequest === "undefined") { - throw new Twig.Error('Unsupported platform: Unable to do ajax requests ' + - 'because there is no "XMLHTTPRequest" implementation'); - } - - xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function() { - var data = null; - - if(xmlhttp.readyState === 4) { - if (xmlhttp.status === 200 || (window.cordova && xmlhttp.status == 0)) { - 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 = parser.call(this, params); - - if (typeof callback === 'function') { - callback(template); - } - } else { - if (typeof error_callback === 'function') { - error_callback(xmlhttp); - } - } - } - }; - xmlhttp.open("GET", location, !!params.async); - xmlhttp.send(); - - if (params.async) { - // TODO: return deferred promise - return true; - } else { - return template; - } - }); - - }; - - -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = function(Twig) { - 'use strict'; - - var fs, path; - - try { - // require lib dependencies at runtime - fs = __webpack_require__(19); - path = __webpack_require__(20); - } catch (e) { - // NOTE: this is in a try/catch to avoid errors cross platform - } - - Twig.Templates.registerLoader('fs', function(location, params, callback, error_callback) { - var template, - data = null, - precompiled = params.precompiled, - parser = this.parsers[params.parser] || this.parser.twig; - - if (!fs || !path) { - throw new Twig.Error('Unsupported platform: Unable to load from file ' + - 'because there is no "fs" or "path" implementation'); - } - - var loadTemplateFn = function(err, data) { - if (err) { - if (typeof error_callback === 'function') { - error_callback(err); - } - return; - } - - if (precompiled === true) { - data = JSON.parse(data); - } - - params.data = data; - params.path = params.path || location; - - // template is in data - template = parser.call(this, params); - - if (typeof callback === 'function') { - callback(template); - } - }; - params.path = params.path || location; - - if (params.async) { - fs.stat(params.path, function (err, stats) { - if (err || !stats.isFile()) { - if (typeof error_callback === 'function') { - error_callback(new Twig.Error('Unable to find template file ' + params.path)); - } - return; - } - fs.readFile(params.path, 'utf8', loadTemplateFn); - }); - // TODO: return deferred promise - return true; - } else { - try { - if (!fs.statSync(params.path).isFile()) { - throw new Twig.Error('Unable to find template file ' + params.path); - } - } catch (err) { - throw new Twig.Error('Unable to find template file ' + params.path); - } - data = fs.readFileSync(params.path, 'utf8'); - loadTemplateFn(undefined, data); - return template - } - }); - - }; - - -/***/ }, -/* 19 */ -/***/ function(module, exports) { - - module.exports = require("fs"); - -/***/ }, -/* 20 */ -/***/ function(module, exports) { - - module.exports = require("path"); - -/***/ }, -/* 21 */ -/***/ function(module, exports) { - - // ## twig.logic.js - // - // This file handles tokenizing, compiling and parsing logic tokens. {% ... %} - module.exports = 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', - shortblock: 'Twig.logic.type.shortblock', - 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', - embed: 'Twig.logic.type.embed', - endembed: 'Twig.logic.type.endembed' - }; - - - // Regular expressions for handling logic tokens. - // - // Properties: - // - // type: The type of expression this matches - // - // regex: A regular expression that matches the format of the token - // - // next: What logic tokens (if any) pop this token off the logic stack. If empty, the - // 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. - // - // 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\S]+)$/, - next: [ - Twig.logic.type.else_, - Twig.logic.type.elseif, - Twig.logic.type.endif - ], - open: true, - compile: function (token) { - var expression = token.match[1]; - // Compile the expression. - 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 = '', - // Parse the expression - result = Twig.expression.parse.apply(this, [token.stack, context]); - - // Start a new logic chain - chain = true; - - if (Twig.lib.boolval(result)) { - chain = false; - // parse if output - output = Twig.parse.apply(this, [token.output, context]); - } - return { - chain: chain, - output: output - }; - } - }, - { - /** - * Else if type logic tokens. - * - * Format: {% elseif expression %} - */ - 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]; - // Compile the expression. - 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]); - - if (chain && Twig.lib.boolval(result)) { - chain = false; - // parse if output - output = Twig.parse.apply(this, [token.output, context]); - } - - return { - chain: chain, - output: output - }; - } - }, - { - /** - * Else if type logic tokens. - * - * Format: {% elseif expression %} - */ - 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 - }; - } - }, - { - /** - * End if type logic tokens. - * - * Format: {% endif %} - */ - type: Twig.logic.type.endif, - regex: /^endif$/, - next: [ ], - 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].*))?$/, - 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; - } - - // Valid expressions for a for loop - // for item in expression - // for key,item in expression - - // Compile the expression. - token.expression = Twig.expression.compile.apply(this, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - // Compile the conditional (if available) - 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) { - // Parse expression - 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 - }; - }, - // run once for each iteration of the loop - loop = function(key, value) { - var inner_context = Twig.ChildContext(context); - - inner_context[token.value_var] = value; - - if (token.key_var) { - inner_context[token.key_var] = key; - } - - // Loop object - 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; - } - - // Delete loop-related variables from the context - delete inner_context['loop']; - delete inner_context[token.value_var]; - delete inner_context[token.key_var]; - - // Merge in values that exist in context but have changed - // in inner_context. - Twig.merge(context, inner_context, true); - }; - - - if (Twig.lib.is('Array', result)) { - len = result.length; - Twig.forEach(result, function (value) { - var key = index; - - loop(key, value); - }); - } else if (Twig.lib.is('Object', result)) { - if (result._keys !== undefined) { - keyset = result._keys; - } else { - keyset = Object.keys(result); - } - len = keyset.length; - Twig.forEach(keyset, function(key) { - // Ignore the _keys property, it's internal to twig.js - if (key === "_keys") return; - - loop(key, result[key]); - }); - } - - // Only allow else statements if no output was generated - continue_chain = (output.length === 0); - - return { - chain: continue_chain, - output: Twig.output.apply(this, [output]) - }; - } - }, - { - /** - * End if type logic tokens. - * - * Format: {% endif %} - */ - type: Twig.logic.type.endfor, - regex: /^endfor$/, - next: [ ], - 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*([\s\S]+)$/, - next: [ ], - 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, [{ - 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; - - if (value === context) { - /* If storing the context in a variable, it needs to be a clone of the current state of context. - Otherwise we have a context with infinite recursion. - Fixes #341 - */ - value = Twig.lib.copy(value); - } - - context[key] = value; - - 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]+)$/, - 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; - - // set on both the global and local context - this.context[key] = value; - context[key] = value; - - return { - chain: continue_chain, - context: context - }; - } - }, - { - /** - * End set type block logic tokens. - * - * Format: {% endset %} - */ - type: Twig.logic.type.endset, - regex: /^endset$/, - next: [ ], - open: false - }, - { - /** - * Filter logic tokens. - * - * Format: {% filter upper %} or {% filter lower|escape %} - */ - type: Twig.logic.type.filter, - regex: /^filter\s+(.+)$/, - next: [ - Twig.logic.type.endfilter - ], - open: true, - compile: function (token) { - var expression = "|" + token.match[1].trim(); - // Compile the expression. - 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 - }; - } - }, - { - /** - * End filter logic tokens. - * - * Format: {% endfilter %} - */ - type: Twig.logic.type.endfilter, - regex: /^endfilter$/, - next: [ ], - open: false - }, - { - /** - * Block logic tokens. - * - * Format: {% block title %} - */ - 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, - isImported = Twig.indexOf(this.importedBlocks, token.block) > -1, - hasParent = this.blocks[token.block] && Twig.indexOf(this.blocks[token.block], Twig.placeholders.parent) > -1; - - // Don't override previous blocks unless they're imported with "use" - // Loops should be exempted as well. - if (this.blocks[token.block] === undefined || isImported || hasParent || context.loop || token.overwrite) { - if (token.expression) { - // Short blocks have output as an expression on the open tag (no body) - block_output = Twig.expression.parse.apply(this, [{ - type: Twig.expression.type.string, - value: Twig.expression.parse.apply(this, [token.output, context]) - }, context]); - } else { - block_output = Twig.expression.parse.apply(this, [{ - type: Twig.expression.type.string, - value: Twig.parse.apply(this, [token.output, context]) - }, context]); - } - - if (isImported) { - // once the block is overridden, remove it from the list of imported blocks - this.importedBlocks.splice(this.importedBlocks.indexOf(token.block), 1); - } - - if (hasParent) { - this.blocks[token.block] = Twig.Markup(this.blocks[token.block].replace(Twig.placeholders.parent, block_output)); - } else { - this.blocks[token.block] = block_output; - } - - this.originalBlockTokens[token.block] = { - type: token.type, - block: token.block, - output: token.output, - overwrite: true - }; - } - - // 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]; - } - - return { - chain: chain, - output: output - }; - } - }, - { - /** - * Block shorthand logic tokens. - * - * Format: {% block title expression %} - */ - type: Twig.logic.type.shortblock, - regex: /^block\s+([a-zA-Z0-9_]+)\s+(.+)$/, - next: [ ], - open: true, - compile: function (token) { - token.expression = token.match[2].trim(); - - token.output = Twig.expression.compile({ - type: Twig.expression.type.expression, - value: token.expression - }).stack; - - token.block = token.match[1].trim(); - delete token.match; - return token; - }, - parse: function (token, context, chain) { - return Twig.logic.handler[Twig.logic.type.block].parse.apply(this, arguments); - } - }, - { - /** - * End block logic tokens. - * - * Format: {% endblock %} - */ - type: Twig.logic.type.endblock, - regex: /^endblock(?:\s+([a-zA-Z0-9_]+))?$/, - next: [ ], - open: false - }, - { - /** - * Block logic tokens. - * - * Format: {% extends "template.twig" %} - */ - 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 template, - innerContext = Twig.ChildContext(context); - // Resolve filename - var file = Twig.expression.parse.apply(this, [token.stack, context]); - - // Set parent template - this.extend = file; - - if (file instanceof Twig.Template) { - template = file; - } else { - // Import file - template = this.importFile(file); - } - - // Render the template in case it puts anything in its context - template.render(innerContext); - - // Extend the parent context with the extended context - Twig.lib.extend(context, innerContext); - - return { - chain: chain, - output: '' - }; - } - }, - { - /** - * Block logic tokens. - * - * Format: {% use "template.twig" %} - */ - 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) { - // Resolve filename - var file = Twig.expression.parse.apply(this, [token.stack, context]); - - // Import blocks - this.importBlocks(file); - - return { - chain: chain, - output: '' - }; - } - }, - { - /** - * Block logic tokens. - * - * Format: {% includes "template.twig" [with {some: 'values'} only] %} - */ - type: Twig.logic.type.include, - regex: /^include\s+(.+?)(?:\s|$)(ignore missing(?:\s|$))?(?:with\s+([\S\s]+?))?(?:\s|$)(only)?$/, - next: [ ], - open: true, - compile: function (token) { - var match = token.match, - expression = match[1].trim(), - ignoreMissing = match[2] !== undefined, - withContext = match[3], - only = ((match[4] !== undefined) && match[4].length); - - delete token.match; - - token.only = only; - token.ignoreMissing = ignoreMissing; - - 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) { - // Resolve filename - var innerContext = {}, - withContext, - i, - template; - - if (!token.only) { - innerContext = Twig.ChildContext(context); - } - - 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, context]); - - if (file instanceof Twig.Template) { - template = file; - } else { - // Import file - try { - template = this.importFile(file); - } catch (err) { - if (token.ignoreMissing) { - return { - chain: chain, - output: '' - } - } - - throw err; - } - } - - return { - chain: chain, - output: template.render(innerContext) - }; - } - }, - { - type: Twig.logic.type.spaceless, - regex: /^spaceless$/, - next: [ - Twig.logic.type.endspaceless - ], - open: true, - - // Parse the html and return it without any spaces between tags - parse: function (token, context, chain) { - var // Parse the output without any filter - unfiltered = Twig.parse.apply(this, [token.output, context]), - // A regular expression to find closing and opening tags with spaces between them - rBetweenTagSpaces = />\s+</g, - // Replace all space between closing and opening html tags - output = unfiltered.replace(rBetweenTagSpaces,'><').trim(); - // Rewrap output as a Twig.Markup - output = Twig.Markup(output); - return { - chain: chain, - output: output - }; - } - }, - - // Add the {% endspaceless %} token - { - type: Twig.logic.type.endspaceless, - regex: /^endspaceless$/, - next: [ ], - 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*\(\s*((?:[a-zA-Z0-9_]+(?:,\s*)?)*)\s*\)$/, - next: [ - Twig.logic.type.endmacro - ], - open: true, - compile: function (token) { - var macroName = token.match[1], - parameters = token.match[2].split(/[\s,]+/); - - //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]); - } - } - } - - token.macroName = macroName; - token.parameters = parameters; - - delete token.match; - return token; - }, - parse: function (token, context, chain) { - var template = this; - this.macros[token.macroName] = function() { - // Pass global context and other macros - 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]; - if(typeof arguments[i] !== 'undefined') { - macroContext[prop] = arguments[i]; - } else { - macroContext[prop] = undefined; - } - } - // Render - return Twig.parse.apply(template, [token.output, macroContext]) - }; - - return { - chain: chain, - output: '' - }; - - } - }, - { - /** - * End macro logic tokens. - * - * Format: {% endmacro %} - */ - type: Twig.logic.type.endmacro, - regex: /^endmacro$/, - next: [ ], - 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_]+)$/, - next: [ ], - 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, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - return token; - }, - parse: function (token, context, chain) { - if (token.expression !== "_self") { - var file = Twig.expression.parse.apply(this, [token.stack, context]); - var template = this.importFile(file || token.expression); - context[token.contextName] = template.render({}, {output: 'macros'}); - } - else { - context[token.contextName] = this.macros; - } - - return { - chain: chain, - 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_, ]+)$/, - next: [ ], - 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]; - - // 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(); - } - else if (res.match(/^([a-zA-Z0-9_]+)$/)) { - macroNames[res] = res; - } - else { - // ignore import - } - - } - - delete token.match; - - token.expression = expression; - token.macroNames = macroNames; - - token.stack = Twig.expression.compile.apply(this, [{ - type: Twig.expression.type.expression, - value: expression - }]).stack; - - return token; - }, - parse: function (token, context, chain) { - var macros; - - if (token.expression !== "_self") { - var file = Twig.expression.parse.apply(this, [token.stack, context]); - var template = this.importFile(file || token.expression); - macros = template.render({}, {output: 'macros'}); - } - else { - macros = this.macros; - } - - for (var macroName in token.macroNames) { - if (macros.hasOwnProperty(macroName)) { - context[token.macroNames[macroName]] = macros[macroName]; - } - } - - return { - chain: chain, - output: '' - } - - } - }, - { - /** - * The embed tag combines the behaviour of include and extends. - * It allows you to include another template's contents, just like include does. - * - * Format: {% embed "template.twig" [with {some: 'values'} only] %} - */ - type: Twig.logic.type.embed, - regex: /^embed\s+(.+?)(?:\s|$)(ignore missing(?:\s|$))?(?:with\s+([\S\s]+?))?(?:\s|$)(only)?$/, - next: [ - Twig.logic.type.endembed - ], - open: true, - compile: function (token) { - var match = token.match, - expression = match[1].trim(), - ignoreMissing = match[2] !== undefined, - withContext = match[3], - only = ((match[4] !== undefined) && match[4].length); - - delete token.match; - - token.only = only; - token.ignoreMissing = ignoreMissing; - - 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) { - // Resolve filename - 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]); - - if (file instanceof Twig.Template) { - template = file; - } else { - // Import file - try { - template = this.importFile(file); - } catch (err) { - if (token.ignoreMissing) { - return { - chain: chain, - output: '' - } - } - - throw err; - } - } - - // reset previous blocks - this.blocks = {}; - - // parse tokens. output will be not used - var output = Twig.parse.apply(this, [token.output, innerContext]); - - // render tempalte with blocks defined in embed block - return { - chain: chain, - output: template.render(innerContext, {'blocks':this.blocks}) - }; - } - }, - /* Add the {% endembed %} token - * - */ - { - type: Twig.logic.type.endembed, - regex: /^endembed$/, - next: [ ], - open: false - } - - ]; - - - /** - * 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.type[type] = value; - }; - - /** - * Extend the logic parsing functionality with a new token definition. - * - * // Define a new tag - * Twig.logic.extend({ - * type: Twig.logic.type.{type}, - * // The pattern to match for this token - * regex: ..., - * // What token types can follow this token, leave blank if any. - * next: [ ... ] - * // Create and return compiled version of the token - * compile: function(token) { ... } - * // Parse the compiled token with the context provided by the render call - * // and whether this token chain is complete. - * parse: function(token, context, chain) { ... } - * }); - * - * @param {Object} definition The new logic expression. - */ - Twig.logic.extend = function (definition) { - - if (!definition.type) { - throw new Twig.Error("Unable to extend logic definition. No type provided for " + definition); - } else { - Twig.logic.extendType(definition.type); - } - Twig.logic.handler[definition.type] = definition; - }; - - // Extend with built-in expressions - 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]), - 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); - } - - return token; - }; - - /** - * Tokenize logic expressions. This function matches token expressions against regular - * expressions provided in token definitions provided with Twig.logic.extend. - * - * @param {string} expression the logic token expression to tokenize - * (i.e. what's between {% and %}) - * - * @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; - - // Ignore whitespace around expressions. - expression = expression.trim(); - - for (token_template_type in Twig.logic.handler) { - if (Twig.logic.handler.hasOwnProperty(token_template_type)) { - // Get the type and regex for this template type - token_type = Twig.logic.handler[token_template_type].type; - token_regex = Twig.logic.handler[token_template_type].regex; - - // Handle multiple regular expressions per type. - regex_array = []; - if (token_regex instanceof Array) { - regex_array = token_regex; - } else { - regex_array.push(token_regex); - } - - // Check regular expressions in the order they were specified in the definition. - while (regex_array.length > 0) { - 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; - } - } - } - } - - // No regex matches - throw new Twig.Error("Unable to parse '" + expression.trim() + "'"); - }; - - /** - * Parse a logic token within a given context. - * - * What are logic chains? - * Logic chains represent a series of tokens that are connected, - * for example: - * {% if ... %} {% else %} {% endif %} - * - * The chain parameter is used to signify if a chain is open of closed. - * open: - * More tokens in this chain should be parsed. - * closed: - * This token chain has completed parsing and any additional - * tokens (else, elseif, etc...) should be ignored. - * - * @param {Object} token The compiled token. - * @param {Object} context The render context. - * @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 = '', - 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; - - }; - - -/***/ }, -/* 22 */ -/***/ function(module, exports) { - - module.exports = function(Twig) { - 'use strict'; - - Twig.Templates.registerParser('source', function(params) { - return params.data || ''; - }); - }; - - -/***/ }, -/* 23 */ -/***/ function(module, exports) { - - module.exports = function(Twig) { - 'use strict'; - - Twig.Templates.registerParser('twig', function(params) { - return new Twig.Template(params); - }); - }; - - -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { - - // ## twig.path.js - // - // This file handles path parsing - module.exports = function (Twig) { - "use strict"; - - /** - * Namespace for path handling. - */ - Twig.path = {}; - - /** - * Generate the canonical version of a url based on the given base path and file path and in - * the previously registered namespaces. - * - * @param {string} template The Twig Template - * @param {string} file The file path, may be relative and may contain namespaces. - * - * @return {string} The canonical version of the path - */ - Twig.path.parsePath = function(template, file) { - var namespaces = null, - file = file || ""; - - if (typeof template === 'object' && typeof template.options === 'object') { - namespaces = template.options.namespaces; - } - - if (typeof namespaces === 'object' && (file.indexOf('::') > 0) || file.indexOf('@') >= 0){ - for (var k in namespaces){ - if (namespaces.hasOwnProperty(k)) { - file = file.replace(k + '::', namespaces[k]); - file = file.replace('@' + k, namespaces[k]); - } - } - - return file; - } - - return Twig.path.relativePath(template, file); - }; - - /** - * Generate the relative canonical version of a url based on the given base path and file path. - * - * @param {Twig.Template} template The Twig.Template. - * @param {string} file The file path, relative to the base path. - * - * @return {string} The canonical version of the path. - */ - Twig.path.relativePath = function(template, file) { - var base, - base_path, - sep_chr = "/", - new_path = [], - file = file || "", - 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) { - // Get the system-specific path separator - var path = __webpack_require__(20), - 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 = path.normalize(template.path); - } - - base = base.replace(sep+sep, sep); - sep_chr = sep; - } else if ((template.name || template.id) && template.method && template.method !== 'fs' && template.method !== 'ajax') { - // Custom registered loader - base = template.base || template.name || template.id; - } else { - throw new Twig.Error("Cannot extend an inline template."); - } - - base_path = base.split(sep_chr); - - // Remove file from url - base_path.pop(); - base_path = base_path.concat(file.split(sep_chr)); - - while (base_path.length > 0) { - val = base_path.shift(); - if (val == ".") { - // Ignore - } 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; - }; - - -/***/ }, -/* 25 */ -/***/ function(module, exports) { - - // ## twig.tests.js - // - // This file handles expression tests. (is empty, is not defined, etc...) - module.exports = function (Twig) { - "use strict"; - Twig.tests = { - empty: function(value) { - if (value === null || value === undefined) return true; - // Handler numbers - if (typeof value === "number") return false; // numbers are never "empty" - // Handle strings and arrays - if (value.length && value.length > 0) return false; - // Handle objects - for (var key in value) { - if (value.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); // Alias of none - }, - 'same as': function(value, params) { - return value === params[0]; - }, - sameas: function(value, params) { - console.warn('`sameas` is deprecated use `same as`'); - return Twig.tests['same as'](value, params); - }, - iterable: function(value) { - return value && (Twig.lib.is("Array", value) || Twig.lib.is("Object", value)); - } - /* - constant ? - */ - }; - - 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; - }; - - -/***/ }, -/* 26 */ -/***/ function(module, exports) { - - // ## twig.exports.js - // - // This file provides extension points and other hooks into the twig functionality. - - module.exports = 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, - options = { - strict_variables: params.strict_variables || false, - // TODO: turn autoscape on in the next major version - autoescape: params.autoescape != null && params.autoescape || false, - allowInlineIncludes: params.allowInlineIncludes || false, - rethrow: params.rethrow || false, - namespaces: params.namespaces - }; - - if (Twig.cache && 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 Twig.Templates.parsers.twig({ - data: params.data, - path: params.hasOwnProperty('path') ? params.path : undefined, - 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.method !== undefined) { - if (!Twig.Templates.isRegisteredLoader(params.method)) { - throw new Twig.Error('Loader for "' + params.method + '" is not defined.'); - } - return Twig.Templates.loadRemote(params.name || params.href || params.path || id || undefined, { - id: id, - method: params.method, - parser: params.parser || 'twig', - base: params.base, - module: params.module, - precompiled: params.precompiled, - async: params.async, - options: options - - }, params.load, params.error); - - } else if (params.href !== undefined) { - return Twig.Templates.loadRemote(params.href, { - id: id, - method: 'ajax', - parser: params.parser || 'twig', - 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', - parser: params.parser || 'twig', - base: params.base, - module: params.module, - precompiled: params.precompiled, - async: params.async, - options: options - - }, params.load, params.error); - } - }; - - // Extend Twig with a new filter. - Twig.exports.extendFilter = function(filter, definition) { - Twig.filter.extend(filter, definition); - }; - - // Extend Twig with a new function. - Twig.exports.extendFunction = function(fn, definition) { - Twig._function.extend(fn, definition); - }; - - // Extend Twig with a new test. - Twig.exports.extendTest = function(test, definition) { - Twig.test.extend(test, definition); - }; - - // Extend Twig with a new definition. - Twig.exports.extendTag = function(definition) { - Twig.logic.extend(definition); - }; - - // Provide an environment for extending Twig core. - // Calls fn with the internal Twig object. - Twig.exports.extend = function(fn) { - fn(Twig); - }; - - - /** - * Provide an extension for use with express 2. - * - * @param {string} markup The template markup. - * @param {array} options The express options. - * - * @return {string} The rendered template. - */ - 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({ - data: markup, - path: path, - id: id, - options: options.settings['twig options'] - }); // Twig.Templates.load(id) || - - 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. - * @param {Object|Function} The options or callback. - * @param {Function} fn callback. - * - * @throws Twig.Error - */ - Twig.exports.renderFile = function(path, options, fn) { - // handle callback in options - if (typeof options === 'function') { - fn = options; - options = {}; - } - - options = options || {}; - - var settings = options.settings || {}; - - var params = { - path: path, - base: settings.views, - load: function(template) { - // render and return template as a simple string, see https://github.com/twigjs/twig.js/pull/348 for more information - fn(null, '' + template.render(options)); - } - }; - - // mixin any options provided to the express app. - var view_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); - }; - - // Express 3 handler - 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.cache = cache; - }; - - //We need to export the path module so we can effectively test it - Twig.exports.path = Twig.path; - - //Export our filters. - //Resolves #307 - Twig.exports.filters = Twig.filters; - - return Twig; - }; - - -/***/ } -/******/ ]) -}); -;
\ No newline at end of file diff --git a/node_modules/twig/twig.min.js b/node_modules/twig/twig.min.js deleted file mode 100644 index 581573f..0000000 --- a/node_modules/twig/twig.min.js +++ /dev/null @@ -1,10 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Twig=t():e.Twig=t()}(this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){/** - * Twig.js - * - * @copyright 2011-2016 John Roepke and the Twig.js Contributors - * @license Available under the BSD 2-Clause License - * @link https://github.com/twigjs/twig.js - */ -var n={VERSION:"0.10.2"};r(1)(n),r(2)(n),r(3)(n),r(5)(n),r(6)(n),r(7)(n),r(17)(n),r(18)(n),r(22)(n),r(23)(n),r(24)(n),r(25)(n),r(26)(n),r(27)(n),e.exports=n.exports},function(e,t){e.exports=function(e){"use strict";function t(e,t){var r=Object.prototype.toString.call(t).slice(8,-1);return void 0!==t&&null!==t&&r===e}return e.trace=!1,e.debug=!1,e.cache=!0,e.placeholders={parent:"{{|PARENT|}}"},e.indexOf=function(e,t){if(Array.prototype.hasOwnProperty("indexOf"))return e.indexOf(t);if(void 0===e||null===e)throw new TypeError;var r=Object(e),n=r.length>>>0;if(0===n)return-1;var o=0;if(arguments.length>0&&(o=Number(arguments[1]),o!==o?o=0:0!==o&&o!==1/0&&o!==-(1/0)&&(o=(o>0||-1)*Math.floor(Math.abs(o)))),o>=n)return-1;for(var i=o>=0?o:Math.max(n-Math.abs(o),0);i<n;i++)if(i in r&&r[i]===t)return i;return e==t?0:-1},e.forEach=function(e,t,r){if(Array.prototype.forEach)return e.forEach(t,r);var n,o;if(null==e)throw new TypeError(" this is null or not defined");var i=Object(e),s=i.length>>>0;if("[object Function]"!={}.toString.call(t))throw new TypeError(t+" is not a function");for(r&&(n=r),o=0;o<s;){var p;o in i&&(p=i[o],t.call(n,p,o,i)),o++}},e.merge=function(t,r,n){return e.forEach(Object.keys(r),function(e){(!n||e in t)&&(t[e]=r[e])}),t},e.Error=function(e){this.message=e,this.name="TwigException",this.type="TwigException"},e.Error.prototype.toString=function(){var e=this.name+": "+this.message;return e},e.log={trace:function(){e.trace&&console&&console.log(Array.prototype.slice.call(arguments))},debug:function(){e.debug&&console&&console.log(Array.prototype.slice.call(arguments))}},"undefined"!=typeof console?"undefined"!=typeof console.error?e.log.error=function(){console.error.apply(console,arguments)}:"undefined"!=typeof console.log&&(e.log.error=function(){console.log.apply(console,arguments)}):e.log.error=function(){},e.ChildContext=function(e){var t=function(){};return t.prototype=e,new t},e.token={},e.token.type={output:"output",logic:"logic",comment:"comment",raw:"raw",output_whitespace_pre:"output_whitespace_pre",output_whitespace_post:"output_whitespace_post",output_whitespace_both:"output_whitespace_both",logic_whitespace_pre:"logic_whitespace_pre",logic_whitespace_post:"logic_whitespace_post",logic_whitespace_both:"logic_whitespace_both"},e.token.definitions=[{type:e.token.type.raw,open:"{% raw %}",close:"{% endraw %}"},{type:e.token.type.raw,open:"{% verbatim %}",close:"{% endverbatim %}"},{type:e.token.type.output_whitespace_pre,open:"{{-",close:"}}"},{type:e.token.type.output_whitespace_post,open:"{{",close:"-}}"},{type:e.token.type.output_whitespace_both,open:"{{-",close:"-}}"},{type:e.token.type.logic_whitespace_pre,open:"{%-",close:"%}"},{type:e.token.type.logic_whitespace_post,open:"{%",close:"-%}"},{type:e.token.type.logic_whitespace_both,open:"{%-",close:"-%}"},{type:e.token.type.output,open:"{{",close:"}}"},{type:e.token.type.logic,open:"{%",close:"%}"},{type:e.token.type.comment,open:"{#",close:"#}"}],e.token.strings=['"',"'"],e.token.findStart=function(t){var r,n,o,i,s={position:null,close_position:null,def:null};for(r=0;r<e.token.definitions.length;r++)n=e.token.definitions[r],o=t.indexOf(n.open),i=t.indexOf(n.close),e.log.trace("Twig.token.findStart: ","Searching for ",n.open," found at ",o),o>=0&&n.open.length!==n.close.length&&i<0||(o>=0&&(null===s.position||o<s.position)?(s.position=o,s.def=n,s.close_position=i):o>=0&&null!==s.position&&o===s.position&&(n.open.length>s.def.open.length?(s.position=o,s.def=n,s.close_position=i):n.open.length===s.def.open.length&&(n.close.length>s.def.close.length?i>=0&&i<s.close_position&&(s.position=o,s.def=n,s.close_position=i):i>=0&&i<s.close_position&&(s.position=o,s.def=n,s.close_position=i))));return delete s.close_position,s},e.token.findEnd=function(t,r,n){for(var o,i,s=null,p=!1,a=0,l=null,c=null,u=null,f=null,h=null,y=null;!p;){if(l=null,c=null,u=t.indexOf(r.close,a),!(u>=0))throw new e.Error("Unable to find closing bracket '"+r.close+"' opened near template position "+n);if(s=u,p=!0,r.type===e.token.type.comment)break;if(r.type===e.token.type.raw)break;for(i=e.token.strings.length,o=0;o<i;o+=1)h=t.indexOf(e.token.strings[o],a),h>0&&h<u&&(null===l||h<l)&&(l=h,c=e.token.strings[o]);if(null!==l)for(f=l+1,s=null,p=!1;;){if(y=t.indexOf(c,f),y<0)throw"Unclosed string in template";if("\\"!==t.substr(y-1,1)){a=y+1;break}f=y+1}}return s},e.tokenize=function(t){for(var r=[],n=0,o=null,i=null;t.length>0;)if(o=e.token.findStart(t),e.log.trace("Twig.tokenize: ","Found token: ",o),null!==o.position){if(o.position>0&&r.push({type:e.token.type.raw,value:t.substring(0,o.position)}),t=t.substr(o.position+o.def.open.length),n+=o.position+o.def.open.length,i=e.token.findEnd(t,o.def,n),e.log.trace("Twig.tokenize: ","Token ends at ",i),r.push({type:o.def.type,value:t.substring(0,i).trim()}),"\n"===t.substr(i+o.def.close.length,1))switch(o.def.type){case"logic_whitespace_pre":case"logic_whitespace_post":case"logic_whitespace_both":case"logic":i+=1}t=t.substr(i+o.def.close.length),n+=i+o.def.close.length}else r.push({type:e.token.type.raw,value:t}),t="";return r},e.compile=function(t){try{for(var r=[],n=[],o=[],i=null,s=null,p=null,a=null,l=null,c=null,u=null,f=null,h=null,y=null,d=null,g=null,m=function(t){e.expression.compile.apply(this,[t]),n.length>0?o.push(t):r.push(t)},x=function(t){if(s=e.logic.compile.apply(this,[t]),y=s.type,d=e.logic.handler[y].open,g=e.logic.handler[y].next,e.log.trace("Twig.compile: ","Compiled logic token to ",s," next is: ",g," open is : ",d),void 0!==d&&!d){if(a=n.pop(),u=e.logic.handler[a.type],e.indexOf(u.next,y)<0)throw new Error(y+" not expected after a "+a.type);a.output=a.output||[],a.output=a.output.concat(o),o=[],h={type:e.token.type.logic,token:a},n.length>0?o.push(h):r.push(h)}void 0!==g&&g.length>0?(e.log.trace("Twig.compile: ","Pushing ",s," to logic stack."),n.length>0&&(a=n.pop(),a.output=a.output||[],a.output=a.output.concat(o),n.push(a),o=[]),n.push(s)):void 0!==d&&d&&(h={type:e.token.type.logic,token:s},n.length>0?o.push(h):r.push(h))};t.length>0;){switch(i=t.shift(),l=r[r.length-1],c=o[o.length-1],f=t[0],e.log.trace("Compiling token ",i),i.type){case e.token.type.raw:n.length>0?o.push(i):r.push(i);break;case e.token.type.logic:x.call(this,i);break;case e.token.type.comment:break;case e.token.type.output:m.call(this,i);break;case e.token.type.logic_whitespace_pre:case e.token.type.logic_whitespace_post:case e.token.type.logic_whitespace_both:case e.token.type.output_whitespace_pre:case e.token.type.output_whitespace_post:case e.token.type.output_whitespace_both:switch(i.type!==e.token.type.output_whitespace_post&&i.type!==e.token.type.logic_whitespace_post&&(l&&l.type===e.token.type.raw&&(r.pop(),null===l.value.match(/^\s*$/)&&(l.value=l.value.trim(),r.push(l))),c&&c.type===e.token.type.raw&&(o.pop(),null===c.value.match(/^\s*$/)&&(c.value=c.value.trim(),o.push(c)))),i.type){case e.token.type.output_whitespace_pre:case e.token.type.output_whitespace_post:case e.token.type.output_whitespace_both:m.call(this,i);break;case e.token.type.logic_whitespace_pre:case e.token.type.logic_whitespace_post:case e.token.type.logic_whitespace_both:x.call(this,i)}i.type!==e.token.type.output_whitespace_pre&&i.type!==e.token.type.logic_whitespace_pre&&f&&f.type===e.token.type.raw&&(t.shift(),null===f.value.match(/^\s*$/)&&(f.value=f.value.trim(),t.unshift(f)))}e.log.trace("Twig.compile: "," Output: ",r," Logic Stack: ",n," Pending Output: ",o)}if(n.length>0)throw p=n.pop(),new Error("Unable to find an end tag for "+p.type+", expecting one of "+p.next);return r}catch(t){if(this.options.rethrow)throw t;e.log.error("Error compiling twig template "+this.id+": "),t.stack?e.log.error(t.stack):e.log.error(t.toString())}},e.parse=function(t,r){try{var n=[],o=!0,i=this;return e.forEach(t,function(t){switch(e.log.debug("Twig.parse: ","Parsing token: ",t),t.type){case e.token.type.raw:n.push(e.filters.raw(t.value));break;case e.token.type.logic:var s=t.token,p=e.logic.parse.apply(i,[s,r,o]);void 0!==p.chain&&(o=p.chain),void 0!==p.context&&(r=p.context),void 0!==p.output&&n.push(p.output);break;case e.token.type.comment:break;case e.token.type.output_whitespace_pre:case e.token.type.output_whitespace_post:case e.token.type.output_whitespace_both:case e.token.type.output:e.log.debug("Twig.parse: ","Output token: ",t.stack),n.push(e.expression.parse.apply(i,[t.stack,r]))}}),e.output.apply(this,[n])}catch(t){if(this.options.rethrow)throw t;if(e.log.error("Error parsing twig template "+this.id+": "),t.stack?e.log.error(t.stack):e.log.error(t.toString()),e.debug)return t.toString()}},e.prepare=function(t){var r,n;return e.log.debug("Twig.prepare: ","Tokenizing ",t),n=e.tokenize.apply(this,[t]),e.log.debug("Twig.prepare: ","Compiling ",n),r=e.compile.apply(this,[n]),e.log.debug("Twig.prepare: ","Compiled ",r),r},e.output=function(t){if(!this.options.autoescape)return t.join("");var r="html";"string"==typeof this.options.autoescape&&(r=this.options.autoescape);var n=[];return e.forEach(t,function(t){t&&t.twig_markup!==!0&&t.twig_markup!=r&&(t=e.filters.escape(t,[r])),n.push(t)}),e.Markup(n.join(""))},e.Templates={loaders:{},parsers:{},registry:{}},e.validateId=function(t){if("prototype"===t)throw new e.Error(t+" is not a valid twig identifier");if(e.cache&&e.Templates.registry.hasOwnProperty(t))throw new e.Error("There is already a template with the ID "+t);return!0},e.Templates.registerLoader=function(t,r,n){if("function"!=typeof r)throw new e.Error("Unable to add loader for "+t+": Invalid function reference given.");n&&(r=r.bind(n)),this.loaders[t]=r},e.Templates.unRegisterLoader=function(e){this.isRegisteredLoader(e)&&delete this.loaders[e]},e.Templates.isRegisteredLoader=function(e){return this.loaders.hasOwnProperty(e)},e.Templates.registerParser=function(t,r,n){if("function"!=typeof r)throw new e.Error("Unable to add parser for "+t+": Invalid function regerence given.");n&&(r=r.bind(n)),this.parsers[t]=r},e.Templates.unRegisterParser=function(e){this.isRegisteredParser(e)&&delete this.parsers[e]},e.Templates.isRegisteredParser=function(e){return this.parsers.hasOwnProperty(e)},e.Templates.save=function(t){if(void 0===t.id)throw new e.Error("Unable to save template with no id");e.Templates.registry[t.id]=t},e.Templates.load=function(t){return e.Templates.registry.hasOwnProperty(t)?e.Templates.registry[t]:null},e.Templates.loadRemote=function(t,r,n,o){var i;return void 0===r.async&&(r.async=!0),void 0===r.id&&(r.id=t),e.cache&&e.Templates.registry.hasOwnProperty(r.id)?("function"==typeof n&&n(e.Templates.registry[r.id]),e.Templates.registry[r.id]):(r.parser=r.parser||"twig",i=this.loaders[r.method]||this.loaders.fs,i.apply(this,arguments))},e.Template=function(r){var n=r.data,o=r.id,i=r.blocks,s=r.macros||{},p=r.base,a=r.path,l=r.url,c=r.name,u=r.method,f=r.options;this.id=o,this.method=u,this.base=p,this.path=a,this.url=l,this.name=c,this.macros=s,this.options=f,this.reset(i),t("String",n)?this.tokens=e.prepare.apply(this,[n]):this.tokens=n,void 0!==o&&e.Templates.save(this)},e.Template.prototype.reset=function(t){e.log.debug("Twig.Template.reset","Reseting template "+this.id),this.blocks={},this.importedBlocks=[],this.originalBlockTokens={},this.child={blocks:t||{}},this.extend=null},e.Template.prototype.render=function(t,r){r=r||{};var n,o;if(this.context=t||{},this.reset(),r.blocks&&(this.blocks=r.blocks),r.macros&&(this.macros=r.macros),n=e.parse.apply(this,[this.tokens,this.context]),this.extend){var i;return this.options.allowInlineIncludes&&(i=e.Templates.load(this.extend),i&&(i.options=this.options)),i||(o=e.path.parsePath(this,this.extend),i=e.Templates.loadRemote(o,{method:this.getLoaderMethod(),base:this.base,async:!1,id:o,options:this.options})),this.parent=i,this.parent.render(this.context,{blocks:this.blocks})}return"blocks"==r.output?this.blocks:"macros"==r.output?this.macros:n},e.Template.prototype.importFile=function(t){var r,n;if(!this.url&&this.options.allowInlineIncludes){if(t=this.path?this.path+"/"+t:t,n=e.Templates.load(t),!n&&(n=e.Templates.loadRemote(r,{id:t,method:this.getLoaderMethod(),async:!1,path:t,options:this.options}),!n))throw new e.Error("Unable to find the template "+t);return n.options=this.options,n}return r=e.path.parsePath(this,t),n=e.Templates.loadRemote(r,{method:this.getLoaderMethod(),base:this.base,async:!1,options:this.options,id:r})},e.Template.prototype.importBlocks=function(t,r){var n=this.importFile(t),o=this.context,i=this;r=r||!1,n.render(o),e.forEach(Object.keys(n.blocks),function(e){(r||void 0===i.blocks[e])&&(i.blocks[e]=n.blocks[e],i.importedBlocks.push(e))})},e.Template.prototype.importMacros=function(t){var r=e.path.parsePath(this,t),n=e.Templates.loadRemote(r,{method:this.getLoaderMethod(),async:!1,id:r});return n},e.Template.prototype.getLoaderMethod=function(){return this.path?"fs":this.url?"ajax":this.method||"fs"},e.Template.prototype.compile=function(t){return e.compiler.compile(this,t)},e.Markup=function(e,t){return"undefined"==typeof t&&(t=!0),"string"==typeof e&&e.length>0&&(e=new String(e),e.twig_markup=t),e},e}},function(e,t){e.exports=function(e){return e.compiler={module:{}},e.compiler.compile=function(t,r){var n,o=JSON.stringify(t.tokens),i=t.id;if(r.module){if(void 0===e.compiler.module[r.module])throw new e.Error("Unable to find module type "+r.module);n=e.compiler.module[r.module](i,o,r.twig)}else n=e.compiler.wrap(i,o);return n},e.compiler.module={amd:function(t,r,n){return'define(["'+n+'"], function (Twig) {\n\tvar twig, templates;\ntwig = Twig.twig;\ntemplates = '+e.compiler.wrap(t,r)+"\n\treturn templates;\n});"},node:function(t,r){return'var twig = require("twig").twig;\nexports.template = '+e.compiler.wrap(t,r)},cjs2:function(t,r,n){return'module.declare([{ twig: "'+n+'" }], function (require, exports, module) {\n\tvar twig = require("twig").twig;\n\texports.template = '+e.compiler.wrap(t,r)+"\n});"}},e.compiler.wrap=function(e,t){return'twig({id:"'+e.replace('"','\\"')+'", data:'+t+", precompiled: true});\n"},e}},function(e,t,r){e.exports=function(e){"use strict";e.expression={},r(4)(e),e.expression.reservedWords=["true","false","null","TRUE","FALSE","NULL","_context","and","or","in","not in","if"],e.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",slice:"Twig.expression.type.slice",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"},subexpression:{start:"Twig.expression.type.subexpression.start",end:"Twig.expression.type.subexpression.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"},e.expression.set={operations:[e.expression.type.filter,e.expression.type.operator.unary,e.expression.type.operator.binary,e.expression.type.array.end,e.expression.type.object.end,e.expression.type.parameter.end,e.expression.type.subexpression.end,e.expression.type.comma,e.expression.type.test],expressions:[e.expression.type._function,e.expression.type.bool,e.expression.type.string,e.expression.type.variable,e.expression.type.number,e.expression.type._null,e.expression.type.context,e.expression.type.parameter.start,e.expression.type.array.start,e.expression.type.object.start,e.expression.type.subexpression.start]},e.expression.set.operations_extended=e.expression.set.operations.concat([e.expression.type.key.period,e.expression.type.key.brackets,e.expression.type.slice]),e.expression.fn={compile:{push:function(e,t,r){r.push(e)},push_both:function(e,t,r){r.push(e),t.push(e)}},parse:{push:function(e,t,r){t.push(e)},push_value:function(e,t,r){t.push(e.value)}}},e.expression.definitions=[{type:e.expression.type.test,regex:/^is\s+(not)?\s*([a-zA-Z_][a-zA-Z0-9_]*(\s?as)?)/,next:e.expression.set.operations.concat([e.expression.type.parameter.start]),compile:function(e,t,r){e.filter=e.match[2],e.modifier=e.match[1],delete e.match,delete e.value,r.push(e)},parse:function(t,r,n){var o=r.pop(),i=t.params&&e.expression.parse.apply(this,[t.params,n]),s=e.test(t.filter,o,i);"not"==t.modifier?r.push(!s):r.push(s)}},{type:e.expression.type.comma,regex:/^,/,next:e.expression.set.expressions.concat([e.expression.type.array.end,e.expression.type.object.end]),compile:function(t,r,n){var o,i=r.length-1;for(delete t.match,delete t.value;i>=0;i--){if(o=r.pop(),o.type===e.expression.type.object.start||o.type===e.expression.type.parameter.start||o.type===e.expression.type.array.start){r.push(o);break}n.push(o)}n.push(t)}},{type:e.expression.type.number,regex:/^\-?\d+(\.\d+)?/,next:e.expression.set.operations,compile:function(e,t,r){e.value=Number(e.value),r.push(e)},parse:e.expression.fn.parse.push_value},{type:e.expression.type.operator.binary,regex:/(^\?\:|^[\+\-~%\?]|^[\:](?!\d\])|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^(and)[\(|\s+]|^(or)[\(|\s+]|^(in)[\(|\s+]|^(not in)[\(|\s+]|^\.\.)/,next:e.expression.set.expressions.concat([e.expression.type.operator.unary]),transform:function(e,t){switch(e[0]){case"and(":case"or(":case"in(":case"not in(":return t[t.length-1].value=e[2],e[0];default:return""}},compile:function(t,r,n){delete t.match,t.value=t.value.trim();var o=t.value,i=e.expression.operator.lookup(o,t);for(e.log.trace("Twig.expression.compile: ","Operator: ",i," from ",o);r.length>0&&(r[r.length-1].type==e.expression.type.operator.unary||r[r.length-1].type==e.expression.type.operator.binary)&&(i.associativity===e.expression.operator.leftToRight&&i.precidence>=r[r.length-1].precidence||i.associativity===e.expression.operator.rightToLeft&&i.precidence>r[r.length-1].precidence);){var s=r.pop();n.push(s)}if(":"===o){if(!r[r.length-1]||"?"!==r[r.length-1].value){var p=n.pop();if(p.type===e.expression.type.string||p.type===e.expression.type.variable)t.key=p.value;else if(p.type===e.expression.type.number)t.key=p.value.toString();else{if(!p.expression||p.type!==e.expression.type.parameter.end&&p.type!=e.expression.type.subexpression.end)throw new e.Error("Unexpected value before ':' of "+p.type+" = "+p.value);t.params=p.params}return void n.push(t)}}else r.push(i)},parse:function(t,r,n){t.key?r.push(t):t.params?(t.key=e.expression.parse.apply(this,[t.params,n]),r.push(t),n.loop||delete t.params):e.expression.operator.parse(t.value,r)}},{type:e.expression.type.operator.unary,regex:/(^not\s+)/,next:e.expression.set.expressions,compile:function(t,r,n){delete t.match,t.value=t.value.trim();var o=t.value,i=e.expression.operator.lookup(o,t);for(e.log.trace("Twig.expression.compile: ","Operator: ",i," from ",o);r.length>0&&(r[r.length-1].type==e.expression.type.operator.unary||r[r.length-1].type==e.expression.type.operator.binary)&&(i.associativity===e.expression.operator.leftToRight&&i.precidence>=r[r.length-1].precidence||i.associativity===e.expression.operator.rightToLeft&&i.precidence>r[r.length-1].precidence);){var s=r.pop();n.push(s)}r.push(i)},parse:function(t,r,n){e.expression.operator.parse(t.value,r)}},{type:e.expression.type.string,regex:/^(["'])(?:(?=(\\?))\2[\s\S])*?\1/,next:e.expression.set.operations_extended,compile:function(t,r,n){var o=t.value;delete t.match,o='"'===o.substring(0,1)?o.replace('\\"','"'):o.replace("\\'","'"),t.value=o.substring(1,o.length-1).replace(/\\n/g,"\n").replace(/\\r/g,"\r"),e.log.trace("Twig.expression.compile: ","String value: ",t.value),n.push(t)},parse:e.expression.fn.parse.push_value},{type:e.expression.type.subexpression.start,regex:/^\(/,next:e.expression.set.expressions.concat([e.expression.type.subexpression.end]),compile:function(e,t,r){e.value="(",r.push(e),t.push(e)},parse:e.expression.fn.parse.push},{type:e.expression.type.subexpression.end,regex:/^\)/,next:e.expression.set.operations_extended,validate:function(t,r){for(var n=r.length-1,o=!1,i=!1,s=0;!o&&n>=0;){var p=r[n];o=p.type===e.expression.type.subexpression.start,o&&i&&(i=!1,o=!1),p.type===e.expression.type.parameter.start?s++:p.type===e.expression.type.parameter.end?s--:p.type===e.expression.type.subexpression.end&&(i=!0),n--}return o&&0===s},compile:function(t,r,n){var o,i=t;for(o=r.pop();r.length>0&&o.type!=e.expression.type.subexpression.start;)n.push(o),o=r.pop();for(var s=[];t.type!==e.expression.type.subexpression.start;)s.unshift(t),t=n.pop();s.unshift(t);o=r[r.length-1],void 0===o||o.type!==e.expression.type._function&&o.type!==e.expression.type.filter&&o.type!==e.expression.type.test&&o.type!==e.expression.type.key.brackets?(i.expression=!0,s.pop(),s.shift(),i.params=s,n.push(i)):(i.expression=!1,o.params=s)},parse:function(t,r,n){var o=null;if(!t.expression)throw new e.Error("Unexpected subexpression end when token is not marked as an expression");o=e.expression.parse.apply(this,[t.params,n]),r.push(o)}},{type:e.expression.type.parameter.start,regex:/^\(/,next:e.expression.set.expressions.concat([e.expression.type.parameter.end]),validate:function(t,r){var n=r[r.length-1];return n&&e.indexOf(e.expression.reservedWords,n.value.trim())<0},compile:e.expression.fn.compile.push_both,parse:e.expression.fn.parse.push},{type:e.expression.type.parameter.end,regex:/^\)/,next:e.expression.set.operations_extended,compile:function(t,r,n){var o,i=t;for(o=r.pop();r.length>0&&o.type!=e.expression.type.parameter.start;)n.push(o),o=r.pop();for(var s=[];t.type!==e.expression.type.parameter.start;)s.unshift(t),t=n.pop();s.unshift(t);t=n[n.length-1],void 0===t||t.type!==e.expression.type._function&&t.type!==e.expression.type.filter&&t.type!==e.expression.type.test&&t.type!==e.expression.type.key.brackets?(i.expression=!0,s.pop(),s.shift(),i.params=s,n.push(i)):(i.expression=!1,t.params=s)},parse:function(t,r,n){var o=[],i=!1,s=null;if(t.expression)s=e.expression.parse.apply(this,[t.params,n]),r.push(s);else{for(;r.length>0;){if(s=r.pop(),s&&s.type&&s.type==e.expression.type.parameter.start){i=!0;break}o.unshift(s)}if(!i)throw new e.Error("Expected end of parameter set.");r.push(o)}}},{type:e.expression.type.slice,regex:/^\[(\d*\:\d*)\]/,next:e.expression.set.operations_extended,compile:function(e,t,r){var n=e.match[1].split(":"),o=n[0]?parseInt(n[0]):void 0,i=n[1]?parseInt(n[1]):void 0;e.value="slice",e.params=[o,i],i||(e.params=[o]),r.push(e)},parse:function(t,r,n){var o=r.pop(),i=t.params;r.push(e.filter.apply(this,[t.value,o,i]))}},{type:e.expression.type.array.start,regex:/^\[/,next:e.expression.set.expressions.concat([e.expression.type.array.end]),compile:e.expression.fn.compile.push_both,parse:e.expression.fn.parse.push},{type:e.expression.type.array.end,regex:/^\]/,next:e.expression.set.operations_extended,compile:function(t,r,n){for(var o,i=r.length-1;i>=0&&(o=r.pop(),o.type!==e.expression.type.array.start);i--)n.push(o);n.push(t)},parse:function(t,r,n){for(var o=[],i=!1,s=null;r.length>0;){if(s=r.pop(),s.type&&s.type==e.expression.type.array.start){i=!0;break}o.unshift(s)}if(!i)throw new e.Error("Expected end of array.");r.push(o)}},{type:e.expression.type.object.start,regex:/^\{/,next:e.expression.set.expressions.concat([e.expression.type.object.end]),compile:e.expression.fn.compile.push_both,parse:e.expression.fn.parse.push},{type:e.expression.type.object.end,regex:/^\}/,next:e.expression.set.operations_extended,compile:function(t,r,n){for(var o,i=r.length-1;i>=0&&(o=r.pop(),!o||o.type!==e.expression.type.object.start);i--)n.push(o);n.push(t)},parse:function(t,r,n){for(var o={},i=!1,s=null,p=!1,a=null;r.length>0;){if(s=r.pop(),s&&s.type&&s.type===e.expression.type.object.start){i=!0;break}if(s&&s.type&&(s.type===e.expression.type.operator.binary||s.type===e.expression.type.operator.unary)&&s.key){if(!p)throw new e.Error("Missing value for key '"+s.key+"' in object definition.");o[s.key]=a,void 0===o._keys&&(o._keys=[]),o._keys.unshift(s.key),a=null,p=!1}else p=!0,a=s}if(!i)throw new e.Error("Unexpected end of object.");r.push(o)}},{type:e.expression.type.filter,regex:/^\|\s?([a-zA-Z_][a-zA-Z0-9_\-]*)/,next:e.expression.set.operations_extended.concat([e.expression.type.parameter.start]),compile:function(e,t,r){e.value=e.match[1],r.push(e)},parse:function(t,r,n){var o=r.pop(),i=t.params&&e.expression.parse.apply(this,[t.params,n]);r.push(e.filter.apply(this,[t.value,o,i]))}},{type:e.expression.type._function,regex:/^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/,next:e.expression.type.parameter.start,validate:function(t,r){return t[1]&&e.indexOf(e.expression.reservedWords,t[1])<0},transform:function(e,t){return"("},compile:function(e,t,r){var n=e.match[1];e.fn=n,delete e.match,delete e.value,r.push(e)},parse:function(t,r,n){var o,i=t.params&&e.expression.parse.apply(this,[t.params,n]),s=t.fn;if(e.functions[s])o=e.functions[s].apply(this,i);else{if("function"!=typeof n[s])throw new e.Error(s+" function does not exist and is not defined in the context");o=n[s].apply(n,i)}r.push(o)}},{type:e.expression.type.variable,regex:/^[a-zA-Z_][a-zA-Z0-9_]*/,next:e.expression.set.operations_extended.concat([e.expression.type.parameter.start]),compile:e.expression.fn.compile.push,validate:function(t,r){return e.indexOf(e.expression.reservedWords,t[0])<0},parse:function(t,r,n){var o=e.expression.resolve.apply(this,[n[t.value],n]);r.push(o)}},{type:e.expression.type.key.period,regex:/^\.([a-zA-Z0-9_]+)/,next:e.expression.set.operations_extended.concat([e.expression.type.parameter.start]),compile:function(e,t,r){e.key=e.match[1],delete e.match,delete e.value,r.push(e)},parse:function(t,r,n,o){var i,s=t.params&&e.expression.parse.apply(this,[t.params,n]),p=t.key,a=r.pop();if(null===a||void 0===a){if(this.options.strict_variables)throw new e.Error("Can't access a key "+p+" on an null or undefined object.");i=void 0}else{var l=function(e){return e.substr(0,1).toUpperCase()+e.substr(1)};i="object"==typeof a&&p in a?a[p]:void 0!==a["get"+l(p)]?a["get"+l(p)]:void 0!==a["is"+l(p)]?a["is"+l(p)]:void 0}r.push(e.expression.resolve.apply(this,[i,n,s,o]))}},{type:e.expression.type.key.brackets,regex:/^\[([^\]\:]*)\]/,next:e.expression.set.operations_extended.concat([e.expression.type.parameter.start]),compile:function(t,r,n){var o=t.match[1];delete t.value,delete t.match,t.stack=e.expression.compile({value:o}).stack,n.push(t)},parse:function(t,r,n,o){var i,s=t.params&&e.expression.parse.apply(this,[t.params,n]),p=e.expression.parse.apply(this,[t.stack,n]),a=r.pop();if(null===a||void 0===a){if(this.options.strict_variables)throw new e.Error("Can't access a key "+p+" on an null or undefined object.");return null}i="object"==typeof a&&p in a?a[p]:null,r.push(e.expression.resolve.apply(this,[i,a,s,o]))}},{type:e.expression.type._null,regex:/^(null|NULL|none|NONE)/,next:e.expression.set.operations,compile:function(e,t,r){delete e.match,e.value=null,r.push(e)},parse:e.expression.fn.parse.push_value},{type:e.expression.type.context,regex:/^_context/,next:e.expression.set.operations_extended.concat([e.expression.type.parameter.start]),compile:e.expression.fn.compile.push,parse:function(e,t,r){t.push(r)}},{type:e.expression.type.bool,regex:/^(true|TRUE|false|FALSE)/,next:e.expression.set.operations,compile:function(e,t,r){e.value="true"===e.match[0].toLowerCase(),delete e.match,r.push(e)},parse:e.expression.fn.parse.push_value}],e.expression.resolve=function(t,r,n,o){if("function"==typeof t){if(o&&o.type===e.expression.type.parameter.end){var i=!0;n=o.params&&e.expression.parse.apply(this,[o.params,r,i]),o.cleanup=!0}return t.apply(r,n||[])}return t},e.expression.handler={},e.expression.extendType=function(t){e.expression.type[t]="Twig.expression.type."+t},e.expression.extend=function(t){if(!t.type)throw new e.Error("Unable to extend logic definition. No type provided for "+t);e.expression.handler[t.type]=t};for(;e.expression.definitions.length>0;)e.expression.extend(e.expression.definitions.shift());return e.expression.tokenize=function(t){var r,n,o,i,s,p,a=[],l=0,c=null,u=[];for(p=function(){var t=Array.prototype.slice.apply(arguments);t.pop(),t.pop();return e.log.trace("Twig.expression.tokenize","Matched a ",r," regular expression of ",t),c&&e.indexOf(c,r)<0?(u.push(r+" cannot follow a "+a[a.length-1].type+" at template:"+l+" near '"+t[0].substring(0,20)+"...'"),t[0]):e.expression.handler[r].validate&&!e.expression.handler[r].validate(t,a)?t[0]:(u=[],a.push({type:r,value:t[0],match:t}),s=!0,c=i,l+=t[0].length,e.expression.handler[r].transform?e.expression.handler[r].transform(t,a):"")},e.log.debug("Twig.expression.tokenize","Tokenizing expression ",t);t.length>0;){t=t.trim();for(r in e.expression.handler)if(e.expression.handler.hasOwnProperty(r)){for(i=e.expression.handler[r].next,n=e.expression.handler[r].regex,e.log.trace("Checking type ",r," on ",t),o=n instanceof Array?n:[n],s=!1;o.length>0;)n=o.pop(),t=t.replace(n,p);if(s)break}if(!s)throw u.length>0?new e.Error(u.join(" OR ")):new e.Error("Unable to parse '"+t+"' at template position"+l)}return e.log.trace("Twig.expression.tokenize","Tokenized to ",a),a},e.expression.compile=function(t){var r=t.value,n=e.expression.tokenize(r),o=null,i=[],s=[],p=null;for(e.log.trace("Twig.expression.compile: ","Compiling ",r);n.length>0;)o=n.shift(),p=e.expression.handler[o.type],e.log.trace("Twig.expression.compile: ","Compiling ",o),p.compile&&p.compile(o,s,i),e.log.trace("Twig.expression.compile: ","Stack is",s),e.log.trace("Twig.expression.compile: ","Output is",i);for(;s.length>0;)i.push(s.pop());return e.log.trace("Twig.expression.compile: ","Final output is",i),t.stack=i,delete t.value,t},e.expression.parse=function(t,r,n){var o=this;t instanceof Array||(t=[t]);var i,s=[],p=null,a=[];if(e.forEach(t,function(n,l){n.cleanup||(t.length>l+1&&(i=t[l+1]),p=e.expression.handler[n.type],p.parse&&p.parse.apply(o,[n,s,r,i]),r.loop&&n.type===e.expression.type.operator.binary&&a.push(n))}),e.forEach(a,function(e){e.params&&e.key&&delete e.key}),n){for(var l=[];s.length>0;)l.unshift(s.pop());s.push(l)}return s.pop()},e}},function(e,t){e.exports=function(e){"use strict";e.expression.operator={leftToRight:"leftToRight",rightToLeft:"rightToLeft"};var t=function(e,t){if(void 0===t||null===t)return null;if(void 0!==t.indexOf)return e===t||""!==e&&t.indexOf(e)>-1;var r;for(r in t)if(t.hasOwnProperty(r)&&t[r]===e)return!0;return!1};return e.expression.operator.lookup=function(t,r){switch(t){case"..":r.precidence=20,r.associativity=e.expression.operator.leftToRight;break;case",":r.precidence=18,r.associativity=e.expression.operator.leftToRight;break;case"?:":case"?":case":":r.precidence=16,r.associativity=e.expression.operator.rightToLeft;break;case"or":r.precidence=14,r.associativity=e.expression.operator.leftToRight;break;case"and":r.precidence=13,r.associativity=e.expression.operator.leftToRight;break;case"==":case"!=":r.precidence=9,r.associativity=e.expression.operator.leftToRight;break;case"<":case"<=":case">":case">=":case"not in":case"in":r.precidence=8,r.associativity=e.expression.operator.leftToRight;break;case"~":case"+":case"-":r.precidence=6,r.associativity=e.expression.operator.leftToRight;break;case"//":case"**":case"*":case"/":case"%":r.precidence=5,r.associativity=e.expression.operator.leftToRight;break;case"not":r.precidence=3,r.associativity=e.expression.operator.rightToLeft;break;default:throw new e.Error("Failed to lookup operator: "+t+" is an unknown operator.")}return r.operator=t,r},e.expression.operator.parse=function(r,n){e.log.trace("Twig.expression.operator.parse: ","Handling ",r);var o,i,s;switch("?"===r&&(s=n.pop()),i=n.pop(),"not"!==r&&(o=n.pop()),"in"!==r&&"not in"!==r&&(o&&Array.isArray(o)&&(o=o.length),i&&Array.isArray(i)&&(i=i.length)),r){case":":break;case"?:":e.lib.boolval(o)?n.push(o):n.push(i); -break;case"?":void 0===o&&(o=i,i=s,s=void 0),e.lib.boolval(o)?n.push(i):n.push(s);break;case"+":i=parseFloat(i),o=parseFloat(o),n.push(o+i);break;case"-":i=parseFloat(i),o=parseFloat(o),n.push(o-i);break;case"*":i=parseFloat(i),o=parseFloat(o),n.push(o*i);break;case"/":i=parseFloat(i),o=parseFloat(o),n.push(o/i);break;case"//":i=parseFloat(i),o=parseFloat(o),n.push(Math.floor(o/i));break;case"%":i=parseFloat(i),o=parseFloat(o),n.push(o%i);break;case"~":n.push((null!=o?o.toString():"")+(null!=i?i.toString():""));break;case"not":case"!":n.push(!e.lib.boolval(i));break;case"<":n.push(o<i);break;case"<=":n.push(o<=i);break;case">":n.push(o>i);break;case">=":n.push(o>=i);break;case"===":n.push(o===i);break;case"==":n.push(o==i);break;case"!==":n.push(o!==i);break;case"!=":n.push(o!=i);break;case"or":n.push(o||i);break;case"and":n.push(o&&i);break;case"**":n.push(Math.pow(o,i));break;case"not in":n.push(!t(o,i));break;case"in":n.push(t(o,i));break;case"..":n.push(e.functions.range(o,i));break;default:throw new e.Error("Failed to parse operator: "+r+" is an unknown operator.")}},e}},function(e,t){e.exports=function(e){function t(e,t){var r=Object.prototype.toString.call(t).slice(8,-1);return void 0!==t&&null!==t&&r===e}return e.filters={upper:function(e){return"string"!=typeof e?e:e.toUpperCase()},lower:function(e){return"string"!=typeof e?e:e.toLowerCase()},capitalize:function(e){return"string"!=typeof e?e:e.substr(0,1).toUpperCase()+e.toLowerCase().substr(1)},title:function(e){return"string"!=typeof e?e:e.toLowerCase().replace(/(^|\s)([a-z])/g,function(e,t,r){return t+r.toUpperCase()})},length:function(t){return e.lib.is("Array",t)||"string"==typeof t?t.length:e.lib.is("Object",t)?void 0===t._keys?Object.keys(t).length:t._keys.length:0},reverse:function(e){if(t("Array",e))return e.reverse();if(t("String",e))return e.split("").reverse().join("");if(t("Object",e)){var r=e._keys||Object.keys(e).reverse();return e._keys=r,e}},sort:function(e){if(t("Array",e))return e.sort();if(t("Object",e)){delete e._keys;var r=Object.keys(e),n=r.sort(function(t,r){var n;return e[t]>e[r]==!(e[t]<=e[r])?e[t]>e[r]?1:e[t]<e[r]?-1:0:isNaN(n=parseFloat(e[t]))||isNaN(b1=parseFloat(e[r]))?"string"==typeof e[t]?e[t]>e[r].toString()?1:e[t]<e[r].toString()?-1:0:"string"==typeof e[r]?e[t].toString()>e[r]?1:e[t].toString()<e[r]?-1:0:null:n>b1?1:n<b1?-1:0});return e._keys=n,e}},keys:function(t){if(void 0!==t&&null!==t){var r=t._keys||Object.keys(t),n=[];return e.forEach(r,function(e){"_keys"!==e&&t.hasOwnProperty(e)&&n.push(e)}),n}},url_encode:function(e){if(void 0!==e&&null!==e){var t=encodeURIComponent(e);return t=t.replace("'","%27")}},join:function(r,n){if(void 0!==r&&null!==r){var o="",i=[],s=null;return n&&n[0]&&(o=n[0]),t("Array",r)?i=r:(s=r._keys||Object.keys(r),e.forEach(s,function(e){"_keys"!==e&&r.hasOwnProperty(e)&&i.push(r[e])})),i.join(o)}},default:function(t,r){if(void 0!==r&&r.length>1)throw new e.Error("default filter expects one argument");return void 0===t||null===t||""===t?void 0===r?"":r[0]:t},json_encode:function(r){if(void 0===r||null===r)return"null";if("object"==typeof r&&t("Array",r))return o=[],e.forEach(r,function(t){o.push(e.filters.json_encode(t))}),"["+o.join(",")+"]";if("object"==typeof r){var n=r._keys||Object.keys(r),o=[];return e.forEach(n,function(t){o.push(JSON.stringify(t)+":"+e.filters.json_encode(r[t]))}),"{"+o.join(",")+"}"}return JSON.stringify(r)},merge:function(r,n){var o=[],i=0,s=[];if(t("Array",r)?e.forEach(n,function(e){t("Array",e)||(o={})}):o={},t("Array",o)||(o._keys=[]),t("Array",r)?e.forEach(r,function(e){o._keys&&o._keys.push(i),o[i]=e,i++}):(s=r._keys||Object.keys(r),e.forEach(s,function(e){o[e]=r[e],o._keys.push(e);var t=parseInt(e,10);!isNaN(t)&&t>=i&&(i=t+1)})),e.forEach(n,function(r){t("Array",r)?e.forEach(r,function(e){o._keys&&o._keys.push(i),o[i]=e,i++}):(s=r._keys||Object.keys(r),e.forEach(s,function(e){o[e]||o._keys.push(e),o[e]=r[e];var t=parseInt(e,10);!isNaN(t)&&t>=i&&(i=t+1)}))}),0===n.length)throw new e.Error("Filter merge expects at least one parameter");return o},date:function(t,r){var n=e.functions.date(t),o=r&&r.length?r[0]:"F j, Y H:i";return e.lib.date(o,n)},date_modify:function(t,r){if(void 0!==t&&null!==t){if(void 0===r||1!==r.length)throw new e.Error("date_modify filter expects 1 argument");var n,o=r[0];return e.lib.is("Date",t)&&(n=e.lib.strtotime(o,t.getTime()/1e3)),e.lib.is("String",t)&&(n=e.lib.strtotime(o,e.lib.strtotime(t))),e.lib.is("Number",t)&&(n=e.lib.strtotime(o,t)),new Date(1e3*n)}},replace:function(t,r){if(void 0!==t&&null!==t){var n,o=r[0];for(n in o)o.hasOwnProperty(n)&&"_keys"!==n&&(t=e.lib.replaceAll(t,n,o[n]));return t}},format:function(t,r){if(void 0!==t&&null!==t)return e.lib.vsprintf(t,r)},striptags:function(t){if(void 0!==t&&null!==t)return e.lib.strip_tags(t)},escape:function(t,r){if(void 0!==t&&null!==t){var n="html";if(r&&r.length&&r[0]!==!0&&(n=r[0]),"html"==n){var o=t.toString().replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");return e.Markup(o,"html")}if("js"==n){for(var o=t.toString(),i="",s=0;s<o.length;s++)if(o[s].match(/^[a-zA-Z0-9,\._]$/))i+=o[s];else{var p=o.charCodeAt(s);i+=p<128?"\\x"+p.toString(16).toUpperCase():e.lib.sprintf("\\u%04s",p.toString(16).toUpperCase())}return e.Markup(i,"js")}if("css"==n){for(var o=t.toString(),i="",s=0;s<o.length;s++)if(o[s].match(/^[a-zA-Z0-9]$/))i+=o[s];else{var p=o.charCodeAt(s);i+="\\"+p.toString(16).toUpperCase()+" "}return e.Markup(i,"css")}if("url"==n){var i=e.filters.url_encode(t);return e.Markup(i,"url")}if("html_attr"==n){for(var o=t.toString(),i="",s=0;s<o.length;s++)if(o[s].match(/^[a-zA-Z0-9,\.\-_]$/))i+=o[s];else if(o[s].match(/^[&<>"]$/))i+=o[s].replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");else{var p=o.charCodeAt(s);i+=p<=31&&9!=p&&10!=p&&13!=p?"�":p<128?e.lib.sprintf("&#x%02s;",p.toString(16).toUpperCase()):e.lib.sprintf("&#x%04s;",p.toString(16).toUpperCase())}return e.Markup(i,"html_attr")}throw new e.Error("escape strategy unsupported")}},e:function(t,r){return e.filters.escape(t,r)},nl2br:function(t){if(void 0!==t&&null!==t){var r="BACKSLASH_n_replace",n="<br />"+r;return t=e.filters.escape(t).replace(/\r\n/g,n).replace(/\r/g,n).replace(/\n/g,n),t=e.lib.replaceAll(t,r,"\n"),e.Markup(t)}},number_format:function(e,t){var r=e,n=t&&t[0]?t[0]:void 0,o=t&&void 0!==t[1]?t[1]:".",i=t&&void 0!==t[2]?t[2]:",";r=(r+"").replace(/[^0-9+\-Ee.]/g,"");var s=isFinite(+r)?+r:0,p=isFinite(+n)?Math.abs(n):0,a="",l=function(e,t){var r=Math.pow(10,t);return""+Math.round(e*r)/r};return a=(p?l(s,p):""+Math.round(s)).split("."),a[0].length>3&&(a[0]=a[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,i)),(a[1]||"").length<p&&(a[1]=a[1]||"",a[1]+=new Array(p-a[1].length+1).join("0")),a.join(o)},trim:function(t,r){if(void 0!==t&&null!==t){var n,o=e.filters.escape(""+t);n=r&&r[0]?""+r[0]:" \n\r\t\f\v \u2028\u2029 ";for(var i=0;i<o.length;i++)if(n.indexOf(o.charAt(i))===-1){o=o.substring(i);break}for(i=o.length-1;i>=0;i--)if(n.indexOf(o.charAt(i))===-1){o=o.substring(0,i+1);break}return n.indexOf(o.charAt(0))===-1?o:""}},truncate:function(e,t){var r=30,n=!1,o="...";if(e+="",t&&(t[0]&&(r=t[0]),t[1]&&(n=t[1]),t[2]&&(o=t[2])),e.length>r){if(n&&(r=e.indexOf(" ",r),r===-1))return e;e=e.substr(0,r)+o}return e},slice:function(t,r){if(void 0!==t&&null!==t){if(void 0===r||r.length<1)throw new e.Error("slice filter expects at least 1 argument");var n=r[0]||0,o=r.length>1?r[1]:t.length,i=n>=0?n:Math.max(t.length+n,0);if(e.lib.is("Array",t)){for(var s=[],p=i;p<i+o&&p<t.length;p++)s.push(t[p]);return s}if(e.lib.is("String",t))return t.substr(i,o);throw new e.Error("slice filter expects value to be an array or string")}},abs:function(e){if(void 0!==e&&null!==e)return Math.abs(e)},first:function(e){if(t("Array",e))return e[0];if(t("Object",e)){if("_keys"in e)return e[e._keys[0]]}else if("string"==typeof e)return e.substr(0,1)},split:function(t,r){if(void 0!==t&&null!==t){if(void 0===r||r.length<1||r.length>2)throw new e.Error("split filter expects 1 or 2 argument");if(e.lib.is("String",t)){var n=r[0],o=r[1],i=t.split(n);if(void 0===o)return i;if(o<0)return t.split(n,i.length+o);var s=[];if(""==n)for(;i.length>0;){for(var p="",a=0;a<o&&i.length>0;a++)p+=i.shift();s.push(p)}else{for(var a=0;a<o-1&&i.length>0;a++)s.push(i.shift());i.length>0&&s.push(i.join(n))}return s}throw new e.Error("split filter expects value to be a string")}},last:function(t){if(e.lib.is("Object",t)){var r;return r=void 0===t._keys?Object.keys(t):t._keys,t[r[r.length-1]]}return t[t.length-1]},raw:function(t){return e.Markup(t)},batch:function(t,r){var n,o,i,s=r.shift(),p=r.shift();if(!e.lib.is("Array",t))throw new e.Error("batch filter expects items to be an array");if(!e.lib.is("Number",s))throw new e.Error("batch filter expects size to be a number");if(s=Math.ceil(s),n=e.lib.chunkArray(t,s),p&&t.length%s!=0){for(o=n.pop(),i=s-o.length;i--;)o.push(p);n.push(o)}return n},round:function(t,r){r=r||[];var n=r.length>0?r[0]:0,o=r.length>1?r[1]:"common";if(t=parseFloat(t),n&&!e.lib.is("Number",n))throw new e.Error("round filter expects precision to be a number");if("common"===o)return e.lib.round(t,n);if(!e.lib.is("Function",Math[o]))throw new e.Error("round filter expects method to be 'floor', 'ceil', or 'common'");return Math[o](t*Math.pow(10,n))/Math.pow(10,n)}},e.filter=function(t,r,n){if(!e.filters[t])throw"Unable to find filter "+t;return e.filters[t].apply(this,[r,n])},e.filter.extend=function(t,r){e.filters[t]=r},e}},function(e,t){e.exports=function(t){var r='Template "{name}" is not defined.';return t.functions={range:function(e,t,r){var n,o,i,s=[],p=r||1,a=!1;if(isNaN(e)||isNaN(t)?isNaN(e)&&isNaN(t)?(a=!0,n=e.charCodeAt(0),o=t.charCodeAt(0)):(n=isNaN(e)?0:e,o=isNaN(t)?0:t):(n=parseInt(e,10),o=parseInt(t,10)),i=!(n>o))for(;n<=o;)s.push(a?String.fromCharCode(n):n),n+=p;else for(;n>=o;)s.push(a?String.fromCharCode(n):n),n-=p;return s},cycle:function(e,t){var r=t%e.length;return e[r]},dump:function(){var e="\n",r=" ",n=0,o="",i=Array.prototype.slice.call(arguments),s=function(e){for(var t="";e>0;)e--,t+=r;return t},p=function(t){o+=s(n),"object"==typeof t?a(t):"function"==typeof t?o+="function()"+e:"string"==typeof t?o+="string("+t.length+') "'+t+'"'+e:"number"==typeof t?o+="number("+t+")"+e:"boolean"==typeof t&&(o+="bool("+t+")"+e)},a=function(t){var r;if(null===t)o+="NULL"+e;else if(void 0===t)o+="undefined"+e;else if("object"==typeof t){o+=s(n)+typeof t,n++,o+="("+function(e){var t,r=0;for(t in e)e.hasOwnProperty(t)&&r++;return r}(t)+") {"+e;for(r in t)o+=s(n)+"["+r+"]=> "+e,p(t[r]);n--,o+=s(n)+"}"+e}else p(t)};return 0==i.length&&i.push(this.context),t.forEach(i,function(e){a(e)}),o},date:function(e,r){var n;if(void 0===e||null===e||""===e)n=new Date;else if(t.lib.is("Date",e))n=e;else if(t.lib.is("String",e))n=e.match(/^[0-9]+$/)?new Date(1e3*e):new Date(1e3*t.lib.strtotime(e));else{if(!t.lib.is("Number",e))throw new t.Error("Unable to parse date "+e);n=new Date(1e3*e)}return n},block:function(e){return this.originalBlockTokens[e]?t.logic.parse.apply(this,[this.originalBlockTokens[e],this.context]).output:this.blocks[e]},parent:function(){return t.placeholders.parent},attribute:function(e,r,n){return t.lib.is("Object",e)&&e.hasOwnProperty(r)?"function"==typeof e[r]?e[r].apply(void 0,n):e[r]:e[r]||void 0},max:function(e){return t.lib.is("Object",e)?(delete e._keys,t.lib.max(e)):t.lib.max.apply(null,arguments)},min:function(e){return t.lib.is("Object",e)?(delete e._keys,t.lib.min(e)):t.lib.min.apply(null,arguments)},template_from_string:function(e){return void 0===e&&(e=""),t.Templates.parsers.twig({options:this.options,data:e})},random:function(e){function r(e){var t=Math.floor(Math.random()*n),r=[0,e],o=Math.min.apply(null,r),i=Math.max.apply(null,r);return o+Math.floor((i-o+1)*t/n)}var n=2147483648;if(t.lib.is("Number",e))return r(e);if(t.lib.is("String",e))return e.charAt(r(e.length-1));if(t.lib.is("Array",e))return e[r(e.length-1)];if(t.lib.is("Object",e)){var o=Object.keys(e);return e[o[r(o.length-1)]]}return r(n-1)},source:function(n,o){var i,s,p,a=!1,l="undefined"!=typeof e&&"undefined"!=typeof e.exports&&"undefined"==typeof window;l?(s="fs",p=__dirname+"/"+n):(s="ajax",p=n);var c={id:n,path:p,method:s,parser:"source",async:!1,fetchTemplateSource:!0};"undefined"==typeof o&&(o=!1);try{i=t.Templates.loadRemote(n,c),"undefined"==typeof i||null===i?i="":a=!0}catch(e){t.log.debug("Twig.functions.source: ","Problem loading template ",e)}return a||o?i:r.replace("{name}",n)}},t._function=function(e,r,n){if(!t.functions[e])throw"Unable to find function "+e;return t.functions[e](r,n)},t._function.extend=function(e,r){t.functions[e]=r},t}},function(e,t,r){e.exports=function(e){return e.lib={},e.lib.sprintf=r(8),e.lib.vsprintf=r(9),e.lib.round=r(10),e.lib.max=r(11),e.lib.min=r(12),e.lib.strip_tags=r(13),e.lib.strtotime=r(14),e.lib.date=r(15),e.lib.boolval=r(16),e.lib.is=function(e,t){var r=Object.prototype.toString.call(t).slice(8,-1);return void 0!==t&&null!==t&&r===e},e.lib.copy=function(e){var t,r={};for(t in e)r[t]=e[t];return r},e.lib.extend=function(e,t){var r,n=Object.keys(t);for(r=n.length;r--;)e[n[r]]=t[n[r]];return e},e.lib.replaceAll=function(e,t,r){return e.split(t).join(r)},e.lib.chunkArray=function(t,r){var n=[],o=0,i=t.length;if(r<1||!e.lib.is("Array",t))return[];for(;o<i;)n.push(t.slice(o,o+=r));return n},e}},function(e,t){"use strict";e.exports=function(){var e=/%%|%(\d+\$)?([\-+'#0 ]*)(\*\d+\$|\*|\d+)?(?:\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g,t=arguments,r=0,n=t[r++],o=function(e,t,r,n){r||(r=" ");var o=e.length>=t?"":new Array(1+t-e.length>>>0).join(r);return n?e+o:o+e},i=function(e,t,r,n,i,s){var p=n-e.length;return p>0&&(e=r||!i?o(e,n,s,r):[e.slice(0,t.length),o("",p,"0",!0),e.slice(t.length)].join("")),e},s=function(e,t,r,n,s,p,a){var l=e>>>0;return r=r&&l&&{2:"0b",8:"0",16:"0x"}[t]||"",e=r+o(l.toString(t),p||0,"0",!1),i(e,r,n,s,a)},p=function(e,t,r,n,o,s){return null!==n&&void 0!==n&&(e=e.slice(0,n)),i(e,"",t,r,o,s)},a=function(e,n,a,l,c,u){var f,h,y,d,g;if("%%"===e)return"%";var m,x=!1,v="",b=!1,w=!1,k=" ",_=a.length;for(m=0;m<_;m++)switch(a.charAt(m)){case" ":v=" ";break;case"+":v="+";break;case"-":x=!0;break;case"'":k=a.charAt(m+1);break;case"0":b=!0,k="0";break;case"#":w=!0}if(l=l?"*"===l?+t[r++]:"*"===l.charAt(0)?+t[l.slice(1,-1)]:+l:0,l<0&&(l=-l,x=!0),!isFinite(l))throw new Error("sprintf: (minimum-)width must be finite");switch(c=c?"*"===c?+t[r++]:"*"===c.charAt(0)?+t[c.slice(1,-1)]:+c:"fFeE".indexOf(u)>-1?6:"d"===u?0:void 0,g=n?t[n.slice(0,-1)]:t[r++],u){case"s":return p(g+"",x,l,c,b,k);case"c":return p(String.fromCharCode(+g),x,l,c,b);case"b":return s(g,2,w,x,l,c,b);case"o":return s(g,8,w,x,l,c,b);case"x":return s(g,16,w,x,l,c,b);case"X":return s(g,16,w,x,l,c,b).toUpperCase();case"u":return s(g,10,w,x,l,c,b);case"i":case"d":return f=+g||0,f=Math.round(f-f%1),h=f<0?"-":v,g=h+o(String(Math.abs(f)),c,"0",!1),i(g,h,x,l,b);case"e":case"E":case"f":case"F":case"g":case"G":return f=+g,h=f<0?"-":v,y=["toExponential","toFixed","toPrecision"]["efg".indexOf(u.toLowerCase())],d=["toString","toUpperCase"]["eEfFgG".indexOf(u)%2],g=h+Math.abs(f)[y](c),i(g,h,x,l,b)[d]();default:return e}};return n.replace(e,a)}},function(e,t,r){"use strict";e.exports=function(e,t){var n=r(8);return n.apply(this,[e].concat(t))}},function(e,t){"use strict";e.exports=function(e,t,r){var n,o,i,s;if(t|=0,n=Math.pow(10,t),e*=n,s=e>0|-(e<0),i=e%1===.5*s,o=Math.floor(e),i)switch(r){case"PHP_ROUND_HALF_DOWN":e=o+(s<0);break;case"PHP_ROUND_HALF_EVEN":e=o+o%2*s;break;case"PHP_ROUND_HALF_ODD":e=o+!(o%2);break;default:e=o+(s>0)}return(i?e:Math.round(e))/n}},function(e,t){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};e.exports=function(){var e,t,n=0,o=0,i=arguments,s=i.length,p=function(e){if("[object Array]"===Object.prototype.toString.call(e))return e;var t=[];for(var r in e)e.hasOwnProperty(r)&&t.push(e[r]);return t},a=function e(t,n){var o=0,i=0,s=0,a=0,l=0;if(t===n)return 0;if("object"===("undefined"==typeof t?"undefined":r(t))){if("object"===("undefined"==typeof n?"undefined":r(n))){if(t=p(t),n=p(n),l=t.length,a=n.length,a>l)return 1;if(a<l)return-1;for(o=0,i=l;o<i;++o){if(s=e(t[o],n[o]),1===s)return 1;if(s===-1)return-1}return 0}return-1}return"object"===("undefined"==typeof n?"undefined":r(n))?1:isNaN(n)&&!isNaN(t)?0===t?0:t<0?1:-1:isNaN(t)&&!isNaN(n)?0===n?0:n>0?1:-1:n===t?0:n>t?1:-1};if(0===s)throw new Error("At least one value should be passed to max()");if(1===s){if("object"!==r(i[0]))throw new Error("Wrong parameter count for max()");if(e=p(i[0]),0===e.length)throw new Error("Array must contain at least one element for max()")}else e=i;for(t=e[0],n=1,o=e.length;n<o;++n)1===a(t,e[n])&&(t=e[n]);return t}},function(e,t){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};e.exports=function(){var e,t,n=0,o=0,i=arguments,s=i.length,p=function(e){if("[object Array]"===Object.prototype.toString.call(e))return e;var t=[];for(var r in e)e.hasOwnProperty(r)&&t.push(e[r]);return t},a=function e(t,n){var o=0,i=0,s=0,a=0,l=0;if(t===n)return 0;if("object"===("undefined"==typeof t?"undefined":r(t))){if("object"===("undefined"==typeof n?"undefined":r(n))){if(t=p(t),n=p(n),l=t.length,a=n.length,a>l)return 1;if(a<l)return-1;for(o=0,i=l;o<i;++o){if(s=e(t[o],n[o]),1===s)return 1;if(s===-1)return-1}return 0}return-1}return"object"===("undefined"==typeof n?"undefined":r(n))?1:isNaN(n)&&!isNaN(t)?0===t?0:t<0?1:-1:isNaN(t)&&!isNaN(n)?0===n?0:n>0?1:-1:n===t?0:n>t?1:-1};if(0===s)throw new Error("At least one value should be passed to min()");if(1===s){if("object"!==r(i[0]))throw new Error("Wrong parameter count for min()");if(e=p(i[0]),0===e.length)throw new Error("Array must contain at least one element for min()")}else e=i;for(t=e[0],n=1,o=e.length;n<o;++n)a(t,e[n])===-1&&(t=e[n]);return t}},function(e,t){"use strict";e.exports=function(e,t){t=(((t||"")+"").toLowerCase().match(/<[a-z][a-z0-9]*>/g)||[]).join("");var r=/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,n=/<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;return e.replace(n,"").replace(r,function(e,r){return t.indexOf("<"+r.toLowerCase()+">")>-1?e:""})}},function(e,t){"use strict";e.exports=function(e,t){function r(e,t,r){var n,o=l[t];"undefined"!=typeof o&&(n=o-a.getDay(),0===n?n=7*r:n>0&&"last"===e?n-=7:n<0&&"next"===e&&(n+=7),a.setDate(a.getDate()+n))}function n(e){var t=e.split(" "),n=t[0],o=t[1].substring(0,3),i=/\d+/.test(n),s="ago"===t[2],p=("last"===n?-1:1)*(s?-1:1);if(i&&(p*=parseInt(n,10)),c.hasOwnProperty(o)&&!t[1].match(/^mon(day|\.)?$/i))return a["set"+c[o]](a["get"+c[o]]()+p);if("wee"===o)return a.setDate(a.getDate()+7*p);if("next"===n||"last"===n)r(n,o,p);else if(!i)return!1;return!0}var o,i,s,p,a,l,c,u,f,h,y,d=!1;if(!e)return d;e=e.replace(/^\s+|\s+$/g,"").replace(/\s{2,}/g," ").replace(/[\t\r\n]/g,"").toLowerCase();var g=new RegExp(["^(\\d{1,4})","([\\-\\.\\/:])","(\\d{1,2})","([\\-\\.\\/:])","(\\d{1,4})","(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?","(?:\\s([A-Z]+)?)?$"].join(""));if(i=e.match(g),i&&i[2]===i[4])if(i[1]>1901)switch(i[2]){case"-":return i[3]>12||i[5]>31?d:new Date(i[1],parseInt(i[3],10)-1,i[5],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3;case".":return d;case"/":return i[3]>12||i[5]>31?d:new Date(i[1],parseInt(i[3],10)-1,i[5],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3}else if(i[5]>1901)switch(i[2]){case"-":return i[3]>12||i[1]>31?d:new Date(i[5],parseInt(i[3],10)-1,i[1],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3;case".":return i[3]>12||i[1]>31?d:new Date(i[5],parseInt(i[3],10)-1,i[1],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3;case"/":return i[1]>12||i[3]>31?d:new Date(i[5],parseInt(i[1],10)-1,i[3],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3}else switch(i[2]){case"-":return i[3]>12||i[5]>31||i[1]<70&&i[1]>38?d:(p=i[1]>=0&&i[1]<=38?+i[1]+2e3:i[1],new Date(p,parseInt(i[3],10)-1,i[5],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3);case".":return i[5]>=70?i[3]>12||i[1]>31?d:new Date(i[5],parseInt(i[3],10)-1,i[1],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3:i[5]<60&&!i[6]?i[1]>23||i[3]>59?d:(s=new Date,new Date(s.getFullYear(),s.getMonth(),s.getDate(),i[1]||0,i[3]||0,i[5]||0,i[9]||0)/1e3):d;case"/":return i[1]>12||i[3]>31||i[5]<70&&i[5]>38?d:(p=i[5]>=0&&i[5]<=38?+i[5]+2e3:i[5],new Date(p,parseInt(i[1],10)-1,i[3],i[6]||0,i[7]||0,i[8]||0,i[9]||0)/1e3);case":":return i[1]>23||i[3]>59||i[5]>59?d:(s=new Date,new Date(s.getFullYear(),s.getMonth(),s.getDate(),i[1]||0,i[3]||0,i[5]||0)/1e3)}if("now"===e)return null===t||isNaN(t)?(new Date).getTime()/1e3|0:0|t;if(!isNaN(o=Date.parse(e)))return o/1e3|0;if(g=new RegExp(["^([0-9]{4}-[0-9]{2}-[0-9]{2})","[ t]","([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)","([\\+-][0-9]{2}(:[0-9]{2})?|z)"].join("")),i=e.match(g),i&&("z"===i[4]?i[4]="Z":i[4].match(/^([\+-][0-9]{2})$/)&&(i[4]=i[4]+":00"),!isNaN(o=Date.parse(i[1]+"T"+i[2]+i[4]))))return o/1e3|0;if(a=t?new Date(1e3*t):new Date,l={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6},c={yea:"FullYear",mon:"Month",day:"Date",hou:"Hours",min:"Minutes",sec:"Seconds"},f="(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)",h="([+-]?\\d+\\s"+f+"|(last|next)\\s"+f+")(\\sago)?",i=e.match(new RegExp(h,"gi")),!i)return d;for(y=0,u=i.length;y<u;y++)if(!n(i[y]))return d;return a.getTime()/1e3}},function(e,t){"use strict";e.exports=function(e,t){var r,n,o=["Sun","Mon","Tues","Wednes","Thurs","Fri","Satur","January","February","March","April","May","June","July","August","September","October","November","December"],i=/\\?(.?)/gi,s=function(e,t){return n[e]?n[e]():t},p=function(e,t){for(e=String(e);e.length<t;)e="0"+e;return e};n={d:function(){return p(n.j(),2)},D:function(){return n.l().slice(0,3)},j:function(){return r.getDate()},l:function(){return o[n.w()]+"day"},N:function(){return n.w()||7},S:function(){var e=n.j(),t=e%10;return t<=3&&1===parseInt(e%100/10,10)&&(t=0),["st","nd","rd"][t-1]||"th"},w:function(){return r.getDay()},z:function(){var e=new Date(n.Y(),n.n()-1,n.j()),t=new Date(n.Y(),0,1);return Math.round((e-t)/864e5)},W:function(){var e=new Date(n.Y(),n.n()-1,n.j()-n.N()+3),t=new Date(e.getFullYear(),0,4);return p(1+Math.round((e-t)/864e5/7),2)},F:function(){return o[6+n.n()]},m:function(){return p(n.n(),2)},M:function(){return n.F().slice(0,3)},n:function(){return r.getMonth()+1},t:function(){return new Date(n.Y(),n.n(),0).getDate()},L:function(){var e=n.Y();return e%4===0&e%100!==0|e%400===0},o:function(){var e=n.n(),t=n.W(),r=n.Y();return r+(12===e&&t<9?1:1===e&&t>9?-1:0)},Y:function(){return r.getFullYear()},y:function(){return n.Y().toString().slice(-2)},a:function(){return r.getHours()>11?"pm":"am"},A:function(){return n.a().toUpperCase()},B:function(){var e=3600*r.getUTCHours(),t=60*r.getUTCMinutes(),n=r.getUTCSeconds();return p(Math.floor((e+t+n+3600)/86.4)%1e3,3)},g:function(){return n.G()%12||12},G:function(){return r.getHours()},h:function(){return p(n.g(),2)},H:function(){return p(n.G(),2)},i:function(){return p(r.getMinutes(),2)},s:function(){return p(r.getSeconds(),2)},u:function(){return p(1e3*r.getMilliseconds(),6)},e:function(){var e="Not supported (see source code of date() for timezone on how to add support)";throw new Error(e)},I:function(){var e=new Date(n.Y(),0),t=Date.UTC(n.Y(),0),r=new Date(n.Y(),6),o=Date.UTC(n.Y(),6);return e-t!==r-o?1:0},O:function(){var e=r.getTimezoneOffset(),t=Math.abs(e);return(e>0?"-":"+")+p(100*Math.floor(t/60)+t%60,4)},P:function(){var e=n.O();return e.substr(0,3)+":"+e.substr(3,2)},T:function(){return"UTC"},Z:function(){return 60*-r.getTimezoneOffset()},c:function(){return"Y-m-d\\TH:i:sP".replace(i,s)},r:function(){return"D, d M Y H:i:s O".replace(i,s)},U:function(){return r/1e3|0}};var a=function(e,t){return r=void 0===t?new Date:t instanceof Date?new Date(t):new Date(1e3*t),e.replace(i,s)};return a(e,t)}},function(e,t){"use strict";e.exports=function(e){return e!==!1&&(0!==e&&0!==e&&(""!==e&&"0"!==e&&((!Array.isArray(e)||0!==e.length)&&(null!==e&&void 0!==e))))}},function(e,t){e.exports=function(e){"use strict";e.Templates.registerLoader("ajax",function(t,r,n,o){var i,s,p=r.precompiled,a=this.parsers[r.parser]||this.parser.twig;if("undefined"==typeof XMLHttpRequest)throw new e.Error('Unsupported platform: Unable to do ajax requests because there is no "XMLHTTPRequest" implementation');return s=new XMLHttpRequest,s.onreadystatechange=function(){var l=null;4===s.readyState&&(200===s.status||window.cordova&&0==s.status?(e.log.debug("Got template ",s.responseText),l=p===!0?JSON.parse(s.responseText):s.responseText,r.url=t,r.data=l,i=a.call(this,r),"function"==typeof n&&n(i)):"function"==typeof o&&o(s))},s.open("GET",t,!!r.async),s.send(),!!r.async||i})}},function(e,t,r){e.exports=function(e){"use strict";var t,n;try{t=r(19),n=r(20)}catch(e){}e.Templates.registerLoader("fs",function(r,o,i,s){var p,a=null,l=o.precompiled,c=this.parsers[o.parser]||this.parser.twig;if(!t||!n)throw new e.Error('Unsupported platform: Unable to load from file because there is no "fs" or "path" implementation');var u=function(e,t){return e?void("function"==typeof s&&s(e)):(l===!0&&(t=JSON.parse(t)),o.data=t,o.path=o.path||r,p=c.call(this,o),void("function"==typeof i&&i(p)))};if(o.path=o.path||r,o.async)return t.stat(o.path,function(r,n){return r||!n.isFile()?void("function"==typeof s&&s(new e.Error("Unable to find template file "+o.path))):void t.readFile(o.path,"utf8",u)}),!0;try{if(!t.statSync(o.path).isFile())throw new e.Error("Unable to find template file "+o.path)}catch(t){throw new e.Error("Unable to find template file "+o.path)}return a=t.readFileSync(o.path,"utf8"),u(void 0,a),p})}},function(e,t){},function(e,t,r){(function(e){function r(e,t){for(var r=0,n=e.length-1;n>=0;n--){var o=e[n];"."===o?e.splice(n,1):".."===o?(e.splice(n,1),r++):r&&(e.splice(n,1),r--)}if(t)for(;r--;r)e.unshift("..");return e}function n(e,t){if(e.filter)return e.filter(t);for(var r=[],n=0;n<e.length;n++)t(e[n],n,e)&&r.push(e[n]);return r}var o=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,i=function(e){return o.exec(e).slice(1)};t.resolve=function(){for(var t="",o=!1,i=arguments.length-1;i>=-1&&!o;i--){var s=i>=0?arguments[i]:e.cwd();if("string"!=typeof s)throw new TypeError("Arguments to path.resolve must be strings");s&&(t=s+"/"+t,o="/"===s.charAt(0))}return t=r(n(t.split("/"),function(e){return!!e}),!o).join("/"),(o?"/":"")+t||"."},t.normalize=function(e){var o=t.isAbsolute(e),i="/"===s(e,-1);return e=r(n(e.split("/"),function(e){return!!e}),!o).join("/"),e||o||(e="."),e&&i&&(e+="/"),(o?"/":"")+e},t.isAbsolute=function(e){return"/"===e.charAt(0)},t.join=function(){var e=Array.prototype.slice.call(arguments,0);return t.normalize(n(e,function(e,t){if("string"!=typeof e)throw new TypeError("Arguments to path.join must be strings");return e}).join("/"))},t.relative=function(e,r){function n(e){for(var t=0;t<e.length&&""===e[t];t++);for(var r=e.length-1;r>=0&&""===e[r];r--);return t>r?[]:e.slice(t,r-t+1)}e=t.resolve(e).substr(1),r=t.resolve(r).substr(1);for(var o=n(e.split("/")),i=n(r.split("/")),s=Math.min(o.length,i.length),p=s,a=0;a<s;a++)if(o[a]!==i[a]){p=a;break}for(var l=[],a=p;a<o.length;a++)l.push("..");return l=l.concat(i.slice(p)),l.join("/")},t.sep="/",t.delimiter=":",t.dirname=function(e){var t=i(e),r=t[0],n=t[1];return r||n?(n&&(n=n.substr(0,n.length-1)),r+n):"."},t.basename=function(e,t){var r=i(e)[2];return t&&r.substr(-1*t.length)===t&&(r=r.substr(0,r.length-t.length)),r},t.extname=function(e){return i(e)[3]};var s="b"==="ab".substr(-1)?function(e,t,r){return e.substr(t,r)}:function(e,t,r){return t<0&&(t=e.length+t),e.substr(t,r)}}).call(t,r(21))},function(e,t){function r(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function o(e){if(c===setTimeout)return setTimeout(e,0);if((c===r||!c)&&setTimeout)return c=setTimeout,setTimeout(e,0);try{return c(e,0)}catch(t){try{return c.call(null,e,0)}catch(t){return c.call(this,e,0)}}}function i(e){if(u===clearTimeout)return clearTimeout(e);if((u===n||!u)&&clearTimeout)return u=clearTimeout,clearTimeout(e);try{return u(e)}catch(t){try{return u.call(null,e)}catch(t){return u.call(this,e)}}}function s(){d&&h&&(d=!1,h.length?y=h.concat(y):g=-1,y.length&&p())}function p(){if(!d){var e=o(s);d=!0;for(var t=y.length;t;){for(h=y,y=[];++g<t;)h&&h[g].run();g=-1,t=y.length}h=null,d=!1,i(e)}}function a(e,t){this.fun=e,this.array=t}function l(){}var c,u,f=e.exports={};!function(){try{c="function"==typeof setTimeout?setTimeout:r}catch(e){c=r}try{u="function"==typeof clearTimeout?clearTimeout:n}catch(e){u=n}}();var h,y=[],d=!1,g=-1;f.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)t[r-1]=arguments[r];y.push(new a(e,t)),1!==y.length||d||o(p)},a.prototype.run=function(){this.fun.apply(null,this.array)},f.title="browser",f.browser=!0,f.env={},f.argv=[],f.version="",f.versions={},f.on=l,f.addListener=l,f.once=l,f.off=l,f.removeListener=l,f.removeAllListeners=l,f.emit=l,f.binding=function(e){throw new Error("process.binding is not supported")},f.cwd=function(){return"/"},f.chdir=function(e){throw new Error("process.chdir is not supported")},f.umask=function(){return 0}},function(e,t){e.exports=function(e){"use strict";for(e.logic={},e.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",shortblock:"Twig.logic.type.shortblock",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",embed:"Twig.logic.type.embed",endembed:"Twig.logic.type.endembed"},e.logic.definitions=[{type:e.logic.type.if_,regex:/^if\s+([\s\S]+)$/,next:[e.logic.type.else_,e.logic.type.elseif,e.logic.type.endif],open:!0,compile:function(t){var r=t.match[1];return t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,delete t.match,t},parse:function(t,r,n){var o="",i=e.expression.parse.apply(this,[t.stack,r]);return n=!0,e.lib.boolval(i)&&(n=!1,o=e.parse.apply(this,[t.output,r])),{chain:n,output:o}}},{type:e.logic.type.elseif,regex:/^elseif\s+([^\s].*)$/,next:[e.logic.type.else_,e.logic.type.elseif,e.logic.type.endif],open:!1,compile:function(t){var r=t.match[1];return t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,delete t.match,t},parse:function(t,r,n){var o="",i=e.expression.parse.apply(this,[t.stack,r]);return n&&e.lib.boolval(i)&&(n=!1,o=e.parse.apply(this,[t.output,r])),{chain:n,output:o}}},{type:e.logic.type.else_,regex:/^else$/,next:[e.logic.type.endif,e.logic.type.endfor],open:!1,parse:function(t,r,n){var o="";return n&&(o=e.parse.apply(this,[t.output,r])),{chain:n,output:o}}},{type:e.logic.type.endif,regex:/^endif$/,next:[],open:!1},{type:e.logic.type.for_,regex:/^for\s+([a-zA-Z0-9_,\s]+)\s+in\s+([^\s].*?)(?:\s+if\s+([^\s].*))?$/,next:[e.logic.type.else_,e.logic.type.endfor],open:!0,compile:function(t){var r=t.match[1],n=t.match[2],o=t.match[3],i=null;if(t.key_var=null,t.value_var=null,r.indexOf(",")>=0){if(i=r.split(","),2!==i.length)throw new e.Error("Invalid expression in for loop: "+r);t.key_var=i[0].trim(), -t.value_var=i[1].trim()}else t.value_var=r;return t.expression=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:n}]).stack,o&&(t.conditional=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:o}]).stack),delete t.match,t},parse:function(t,r,n){var o,i,s=e.expression.parse.apply(this,[t.expression,r]),p=[],a=0,l=this,c=t.conditional,u=function(e,t){var n=void 0!==c;return{index:e+1,index0:e,revindex:n?void 0:t-e,revindex0:n?void 0:t-e-1,first:0===e,last:n?void 0:e===t-1,length:n?void 0:t,parent:r}},f=function(n,i){var s=e.ChildContext(r);s[t.value_var]=i,t.key_var&&(s[t.key_var]=n),s.loop=u(a,o),(void 0===c||e.expression.parse.apply(l,[c,s]))&&(p.push(e.parse.apply(l,[t.output,s])),a+=1),delete s.loop,delete s[t.value_var],delete s[t.key_var],e.merge(r,s,!0)};return e.lib.is("Array",s)?(o=s.length,e.forEach(s,function(e){var t=a;f(t,e)})):e.lib.is("Object",s)&&(i=void 0!==s._keys?s._keys:Object.keys(s),o=i.length,e.forEach(i,function(e){"_keys"!==e&&f(e,s[e])})),n=0===p.length,{chain:n,output:e.output.apply(this,[p])}}},{type:e.logic.type.endfor,regex:/^endfor$/,next:[],open:!1},{type:e.logic.type.set,regex:/^set\s+([a-zA-Z0-9_,\s]+)\s*=\s*([\s\S]+)$/,next:[],open:!0,compile:function(t){var r=t.match[1].trim(),n=t.match[2],o=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:n}]).stack;return t.key=r,t.expression=o,delete t.match,t},parse:function(t,r,n){var o=e.expression.parse.apply(this,[t.expression,r]),i=t.key;return o===r&&(o=e.lib.copy(o)),r[i]=o,{chain:n,context:r}}},{type:e.logic.type.setcapture,regex:/^set\s+([a-zA-Z0-9_,\s]+)$/,next:[e.logic.type.endset],open:!0,compile:function(e){var t=e.match[1].trim();return e.key=t,delete e.match,e},parse:function(t,r,n){var o=e.parse.apply(this,[t.output,r]),i=t.key;return this.context[i]=o,r[i]=o,{chain:n,context:r}}},{type:e.logic.type.endset,regex:/^endset$/,next:[],open:!1},{type:e.logic.type.filter,regex:/^filter\s+(.+)$/,next:[e.logic.type.endfilter],open:!0,compile:function(t){var r="|"+t.match[1].trim();return t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,delete t.match,t},parse:function(t,r,n){var o=e.parse.apply(this,[t.output,r]),i=[{type:e.expression.type.string,value:o}].concat(t.stack),s=e.expression.parse.apply(this,[i,r]);return{chain:n,output:s}}},{type:e.logic.type.endfilter,regex:/^endfilter$/,next:[],open:!1},{type:e.logic.type.block,regex:/^block\s+([a-zA-Z0-9_]+)$/,next:[e.logic.type.endblock],open:!0,compile:function(e){return e.block=e.match[1].trim(),delete e.match,e},parse:function(t,r,n){var o,i,s=e.indexOf(this.importedBlocks,t.block)>-1,p=this.blocks[t.block]&&e.indexOf(this.blocks[t.block],e.placeholders.parent)>-1;return(void 0===this.blocks[t.block]||s||p||r.loop||t.overwrite)&&(o=t.expression?e.expression.parse.apply(this,[{type:e.expression.type.string,value:e.expression.parse.apply(this,[t.output,r])},r]):e.expression.parse.apply(this,[{type:e.expression.type.string,value:e.parse.apply(this,[t.output,r])},r]),s&&this.importedBlocks.splice(this.importedBlocks.indexOf(t.block),1),p?this.blocks[t.block]=e.Markup(this.blocks[t.block].replace(e.placeholders.parent,o)):this.blocks[t.block]=o,this.originalBlockTokens[t.block]={type:t.type,block:t.block,output:t.output,overwrite:!0}),i=this.child.blocks[t.block]?this.child.blocks[t.block]:this.blocks[t.block],{chain:n,output:i}}},{type:e.logic.type.shortblock,regex:/^block\s+([a-zA-Z0-9_]+)\s+(.+)$/,next:[],open:!0,compile:function(t){return t.expression=t.match[2].trim(),t.output=e.expression.compile({type:e.expression.type.expression,value:t.expression}).stack,t.block=t.match[1].trim(),delete t.match,t},parse:function(t,r,n){return e.logic.handler[e.logic.type.block].parse.apply(this,arguments)}},{type:e.logic.type.endblock,regex:/^endblock(?:\s+([a-zA-Z0-9_]+))?$/,next:[],open:!1},{type:e.logic.type.extends_,regex:/^extends\s+(.+)$/,next:[],open:!0,compile:function(t){var r=t.match[1].trim();return delete t.match,t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,t},parse:function(t,r,n){var o,i=e.ChildContext(r),s=e.expression.parse.apply(this,[t.stack,r]);return this.extend=s,o=s instanceof e.Template?s:this.importFile(s),o.render(i),e.lib.extend(r,i),{chain:n,output:""}}},{type:e.logic.type.use,regex:/^use\s+(.+)$/,next:[],open:!0,compile:function(t){var r=t.match[1].trim();return delete t.match,t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,t},parse:function(t,r,n){var o=e.expression.parse.apply(this,[t.stack,r]);return this.importBlocks(o),{chain:n,output:""}}},{type:e.logic.type.include,regex:/^include\s+(.+?)(?:\s|$)(ignore missing(?:\s|$))?(?:with\s+([\S\s]+?))?(?:\s|$)(only)?$/,next:[],open:!0,compile:function(t){var r=t.match,n=r[1].trim(),o=void 0!==r[2],i=r[3],s=void 0!==r[4]&&r[4].length;return delete t.match,t.only=s,t.ignoreMissing=o,t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:n}]).stack,void 0!==i&&(t.withStack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:i.trim()}]).stack),t},parse:function(t,r,n){var o,i,s,p={};if(t.only||(p=e.ChildContext(r)),void 0!==t.withStack){o=e.expression.parse.apply(this,[t.withStack,r]);for(i in o)o.hasOwnProperty(i)&&(p[i]=o[i])}var a=e.expression.parse.apply(this,[t.stack,r]);if(a instanceof e.Template)s=a;else try{s=this.importFile(a)}catch(e){if(t.ignoreMissing)return{chain:n,output:""};throw e}return{chain:n,output:s.render(p)}}},{type:e.logic.type.spaceless,regex:/^spaceless$/,next:[e.logic.type.endspaceless],open:!0,parse:function(t,r,n){var o=e.parse.apply(this,[t.output,r]),i=/>\s+</g,s=o.replace(i,"><").trim();return s=e.Markup(s),{chain:n,output:s}}},{type:e.logic.type.endspaceless,regex:/^endspaceless$/,next:[],open:!1},{type:e.logic.type.macro,regex:/^macro\s+([a-zA-Z0-9_]+)\s*\(\s*((?:[a-zA-Z0-9_]+(?:,\s*)?)*)\s*\)$/,next:[e.logic.type.endmacro],open:!0,compile:function(t){for(var r=t.match[1],n=t.match[2].split(/[\s,]+/),o=0;o<n.length;o++)for(var i=0;i<n.length;i++)if(n[o]===n[i]&&o!==i)throw new e.Error("Duplicate arguments for parameter: "+n[o]);return t.macroName=r,t.parameters=n,delete t.match,t},parse:function(t,r,n){var o=this;return this.macros[t.macroName]=function(){for(var r={_self:o.macros},n=0;n<t.parameters.length;n++){var i=t.parameters[n];"undefined"!=typeof arguments[n]?r[i]=arguments[n]:r[i]=void 0}return e.parse.apply(o,[t.output,r])},{chain:n,output:""}}},{type:e.logic.type.endmacro,regex:/^endmacro$/,next:[],open:!1},{type:e.logic.type.import_,regex:/^import\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/,next:[],open:!0,compile:function(t){var r=t.match[1].trim(),n=t.match[2].trim();return delete t.match,t.expression=r,t.contextName=n,t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,t},parse:function(t,r,n){if("_self"!==t.expression){var o=e.expression.parse.apply(this,[t.stack,r]),i=this.importFile(o||t.expression);r[t.contextName]=i.render({},{output:"macros"})}else r[t.contextName]=this.macros;return{chain:n,output:""}}},{type:e.logic.type.from,regex:/^from\s+(.+)\s+import\s+([a-zA-Z0-9_, ]+)$/,next:[],open:!0,compile:function(t){for(var r=t.match[1].trim(),n=t.match[2].trim().split(/[ ,]+/),o={},i=0;i<n.length;i++){var s=n[i],p=s.match(/^([a-zA-Z0-9_]+)\s+(.+)\s+as\s+([a-zA-Z0-9_]+)$/);p?o[p[1].trim()]=p[2].trim():s.match(/^([a-zA-Z0-9_]+)$/)&&(o[s]=s)}return delete t.match,t.expression=r,t.macroNames=o,t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:r}]).stack,t},parse:function(t,r,n){var o;if("_self"!==t.expression){var i=e.expression.parse.apply(this,[t.stack,r]),s=this.importFile(i||t.expression);o=s.render({},{output:"macros"})}else o=this.macros;for(var p in t.macroNames)o.hasOwnProperty(p)&&(r[t.macroNames[p]]=o[p]);return{chain:n,output:""}}},{type:e.logic.type.embed,regex:/^embed\s+(.+?)(?:\s|$)(ignore missing(?:\s|$))?(?:with\s+([\S\s]+?))?(?:\s|$)(only)?$/,next:[e.logic.type.endembed],open:!0,compile:function(t){var r=t.match,n=r[1].trim(),o=void 0!==r[2],i=r[3],s=void 0!==r[4]&&r[4].length;return delete t.match,t.only=s,t.ignoreMissing=o,t.stack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:n}]).stack,void 0!==i&&(t.withStack=e.expression.compile.apply(this,[{type:e.expression.type.expression,value:i.trim()}]).stack),t},parse:function(t,r,n){var o,i,s,p={};if(!t.only)for(i in r)r.hasOwnProperty(i)&&(p[i]=r[i]);if(void 0!==t.withStack){o=e.expression.parse.apply(this,[t.withStack,r]);for(i in o)o.hasOwnProperty(i)&&(p[i]=o[i])}var a=e.expression.parse.apply(this,[t.stack,p]);if(a instanceof e.Template)s=a;else try{s=this.importFile(a)}catch(e){if(t.ignoreMissing)return{chain:n,output:""};throw e}this.blocks={};e.parse.apply(this,[t.output,p]);return{chain:n,output:s.render(p,{blocks:this.blocks})}}},{type:e.logic.type.endembed,regex:/^endembed$/,next:[],open:!1}],e.logic.handler={},e.logic.extendType=function(t,r){r=r||"Twig.logic.type"+t,e.logic.type[t]=r},e.logic.extend=function(t){if(!t.type)throw new e.Error("Unable to extend logic definition. No type provided for "+t);e.logic.extendType(t.type),e.logic.handler[t.type]=t};e.logic.definitions.length>0;)e.logic.extend(e.logic.definitions.shift());return e.logic.compile=function(t){var r=t.value.trim(),n=e.logic.tokenize.apply(this,[r]),o=e.logic.handler[n.type];return o.compile&&(n=o.compile.apply(this,[n]),e.log.trace("Twig.logic.compile: ","Compiled logic token to ",n)),n},e.logic.tokenize=function(t){var r={},n=null,o=null,i=null,s=null,p=null,a=null;t=t.trim();for(n in e.logic.handler)if(e.logic.handler.hasOwnProperty(n))for(o=e.logic.handler[n].type,i=e.logic.handler[n].regex,s=[],i instanceof Array?s=i:s.push(i);s.length>0;)if(p=s.shift(),a=p.exec(t.trim()),null!==a)return r.type=o,r.match=a,e.log.trace("Twig.logic.tokenize: ","Matched a ",o," regular expression of ",a),r;throw new e.Error("Unable to parse '"+t.trim()+"'")},e.logic.parse=function(t,r,n){var o,i="";return r=r||{},e.log.debug("Twig.logic.parse: ","Parsing logic token ",t),o=e.logic.handler[t.type],o.parse&&(i=o.parse.apply(this,[t,r,n])),i},e}},function(e,t){e.exports=function(e){"use strict";e.Templates.registerParser("source",function(e){return e.data||""})}},function(e,t){e.exports=function(e){"use strict";e.Templates.registerParser("twig",function(t){return new e.Template(t)})}},function(e,t,r){e.exports=function(e){"use strict";return e.path={},e.path.parsePath=function(t,r){var n=null,r=r||"";if("object"==typeof t&&"object"==typeof t.options&&(n=t.options.namespaces),"object"==typeof n&&r.indexOf("::")>0||r.indexOf("@")>=0){for(var o in n)n.hasOwnProperty(o)&&(r=r.replace(o+"::",n[o]),r=r.replace("@"+o,n[o]));return r}return e.path.relativePath(t,r)},e.path.relativePath=function(t,n){var o,i,s,p="/",a=[],n=n||"";if(t.url)o="undefined"!=typeof t.base?t.base+("/"===t.base.charAt(t.base.length-1)?"":"/"):t.url;else if(t.path){var l=r(20),c=l.sep||p,u=new RegExp("^\\.{1,2}"+c.replace("\\","\\\\"));n=n.replace(/\//g,c),void 0!==t.base&&null==n.match(u)?(n=n.replace(t.base,""),o=t.base+c):o=l.normalize(t.path),o=o.replace(c+c,c),p=c}else{if(!t.name&&!t.id||!t.method||"fs"===t.method||"ajax"===t.method)throw new e.Error("Cannot extend an inline template.");o=t.base||t.name||t.id}for(i=o.split(p),i.pop(),i=i.concat(n.split(p));i.length>0;)s=i.shift(),"."==s||(".."==s&&a.length>0&&".."!=a[a.length-1]?a.pop():a.push(s));return a.join(p)},e}},function(e,t){e.exports=function(e){"use strict";return e.tests={empty:function(e){if(null===e||void 0===e)return!0;if("number"==typeof e)return!1;if(e.length&&e.length>0)return!1;for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},odd:function(e){return e%2===1},even:function(e){return e%2===0},divisibleby:function(e,t){return e%t[0]===0},defined:function(e){return void 0!==e},none:function(e){return null===e},null:function(e){return this.none(e)},"same as":function(e,t){return e===t[0]},sameas:function(t,r){return console.warn("`sameas` is deprecated use `same as`"),e.tests["same as"](t,r)},iterable:function(t){return t&&(e.lib.is("Array",t)||e.lib.is("Object",t))}},e.test=function(t,r,n){if(!e.tests[t])throw"Test "+t+" is not defined.";return e.tests[t](r,n)},e.test.extend=function(t,r){e.tests[t]=r},e}},function(e,t){e.exports=function(e){"use strict";return e.exports={VERSION:e.VERSION},e.exports.twig=function(t){var r=t.id,n={strict_variables:t.strict_variables||!1,autoescape:null!=t.autoescape&&t.autoescape||!1,allowInlineIncludes:t.allowInlineIncludes||!1,rethrow:t.rethrow||!1,namespaces:t.namespaces};if(e.cache&&r&&e.validateId(r),void 0!==t.debug&&(e.debug=t.debug),void 0!==t.trace&&(e.trace=t.trace),void 0!==t.data)return e.Templates.parsers.twig({data:t.data,path:t.hasOwnProperty("path")?t.path:void 0,module:t.module,id:r,options:n});if(void 0!==t.ref){if(void 0!==t.id)throw new e.Error("Both ref and id cannot be set on a twig.js template.");return e.Templates.load(t.ref)}if(void 0!==t.method){if(!e.Templates.isRegisteredLoader(t.method))throw new e.Error('Loader for "'+t.method+'" is not defined.');return e.Templates.loadRemote(t.name||t.href||t.path||r||void 0,{id:r,method:t.method,parser:t.parser||"twig",base:t.base,module:t.module,precompiled:t.precompiled,async:t.async,options:n},t.load,t.error)}return void 0!==t.href?e.Templates.loadRemote(t.href,{id:r,method:"ajax",parser:t.parser||"twig",base:t.base,module:t.module,precompiled:t.precompiled,async:t.async,options:n},t.load,t.error):void 0!==t.path?e.Templates.loadRemote(t.path,{id:r,method:"fs",parser:t.parser||"twig",base:t.base,module:t.module,precompiled:t.precompiled,async:t.async,options:n},t.load,t.error):void 0},e.exports.extendFilter=function(t,r){e.filter.extend(t,r)},e.exports.extendFunction=function(t,r){e._function.extend(t,r)},e.exports.extendTest=function(t,r){e.test.extend(t,r)},e.exports.extendTag=function(t){e.logic.extend(t)},e.exports.extend=function(t){t(e)},e.exports.compile=function(t,r){var n,o=r.filename,i=r.filename;return n=new e.Template({data:t,path:i,id:o,options:r.settings["twig options"]}),function(e){return n.render(e)}},e.exports.renderFile=function(t,r,n){"function"==typeof r&&(n=r,r={}),r=r||{};var o=r.settings||{},i={path:t,base:o.views,load:function(e){n(null,""+e.render(r))}},s=o["twig options"];if(s)for(var p in s)s.hasOwnProperty(p)&&(i[p]=s[p]);e.exports.twig(i)},e.exports.__express=e.exports.renderFile,e.exports.cache=function(t){e.cache=t},e.exports.path=e.path,e.exports.filters=e.filters,e}}])});
\ No newline at end of file |