diff options
Diffstat (limited to 'node_modules/twig/twig.js')
-rw-r--r-- | node_modules/twig/twig.js | 7223 |
1 files changed, 0 insertions, 7223 deletions
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 |