UNPKG

twig

Version:

JS port of the Twig templating language.

1,201 lines (848 loc) 557 kB
<!DOCTYPE html> <html> <head> <title>twig.js</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul class="sections"> <li id="title"> <div class="annotation"> <h1>twig.js</h1> </div> </li> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">&#182;</a> </div> </div> <div class="content"><div class='highlight'><pre><span class="hljs-comment">/** * Twig.js 0.8.9 * * @copyright 2011-2015 John Roepke and the Twig.js Contributors * @license Available under the BSD 2-Clause License * @link https://github.com/justjohn/twig.js */</span> <span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ Twig.VERSION = <span class="hljs-string">"0.8.9"</span>; <span class="hljs-keyword">return</span> Twig; })(Twig || {});</pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">&#182;</a> </div> <pre><code>Twig.js Available under the BSD <span class="hljs-number">2</span>-Clause License https:<span class="hljs-comment">//github.com/justjohn/twig.js</span> </code></pre> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> Twig = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Twig</span>) </span>{ <span class="hljs-meta"> "use strict"</span>;</pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">&#182;</a> </div> <h2 id="twig-core-js">twig.core.js</h2> <p>This file handles template level tokenizing, compiling and parsing.</p> </div> <div class="content"><div class='highlight'><pre> Twig.trace = <span class="hljs-literal">false</span>; Twig.debug = <span class="hljs-literal">false</span>;</pre></div></div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>Default caching to true for the improved performance it offers</p> </div> <div class="content"><div class='highlight'><pre> Twig.cache = <span class="hljs-literal">true</span>; Twig.placeholders = { parent: <span class="hljs-string">"{{|PARENT|}}"</span> }; <span class="hljs-comment">/** * Fallback for Array.indexOf for IE8 et al */</span> Twig.indexOf = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">arr, searchElement <span class="hljs-comment">/*, fromIndex */</span> </span>) </span>{ <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.prototype.hasOwnProperty(<span class="hljs-string">"indexOf"</span>)) { <span class="hljs-keyword">return</span> arr.indexOf(searchElement); } <span class="hljs-keyword">if</span> (arr === <span class="hljs-keyword">void</span> <span class="hljs-number">0</span> || arr === <span class="hljs-literal">null</span>) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(); } <span class="hljs-keyword">var</span> t = <span class="hljs-built_in">Object</span>(arr); <span class="hljs-keyword">var</span> len = t.length &gt;&gt;&gt; <span class="hljs-number">0</span>; <span class="hljs-keyword">if</span> (len === <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; } <span class="hljs-keyword">var</span> n = <span class="hljs-number">0</span>; <span class="hljs-keyword">if</span> (<span class="hljs-built_in">arguments</span>.length &gt; <span class="hljs-number">0</span>) { n = <span class="hljs-built_in">Number</span>(<span class="hljs-built_in">arguments</span>[<span class="hljs-number">1</span>]); <span class="hljs-keyword">if</span> (n !== n) { <span class="hljs-comment">// shortcut for verifying if it's NaN</span> n = <span class="hljs-number">0</span>; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (n !== <span class="hljs-number">0</span> &amp;&amp; n !== <span class="hljs-literal">Infinity</span> &amp;&amp; n !== -<span class="hljs-literal">Infinity</span>) { n = (n &gt; <span class="hljs-number">0</span> || <span class="hljs-number">-1</span>) * <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.abs(n)); } } <span class="hljs-keyword">if</span> (n &gt;= len) {</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">&#182;</a> </div> <p>console.log(“indexOf not found1 “, JSON.stringify(searchElement), JSON.stringify(arr));</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; } <span class="hljs-keyword">var</span> k = n &gt;= <span class="hljs-number">0</span> ? n : <span class="hljs-built_in">Math</span>.max(len - <span class="hljs-built_in">Math</span>.abs(n), <span class="hljs-number">0</span>); <span class="hljs-keyword">for</span> (; k &lt; len; k++) { <span class="hljs-keyword">if</span> (k <span class="hljs-keyword">in</span> t &amp;&amp; t[k] === searchElement) { <span class="hljs-keyword">return</span> k; } } <span class="hljs-keyword">if</span> (arr == searchElement) { <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; }</pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">&#182;</a> </div> <p>console.log(“indexOf not found2 “, JSON.stringify(searchElement), JSON.stringify(arr));</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; } Twig.forEach = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">arr, callback, thisArg</span>) </span>{ <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.prototype.forEach ) { <span class="hljs-keyword">return</span> arr.forEach(callback, thisArg); } <span class="hljs-keyword">var</span> T, k; <span class="hljs-keyword">if</span> ( arr == <span class="hljs-literal">null</span> ) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>( <span class="hljs-string">" this is null or not defined"</span> ); }</pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">&#182;</a> </div> <ol> <li>Let O be the result of calling ToObject passing the |this| value as the argument.</li> </ol> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> O = <span class="hljs-built_in">Object</span>(arr);</pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">&#182;</a> </div> <ol> <li>Let lenValue be the result of calling the Get internal method of O with the argument “length”.</li> <li>Let len be ToUint32(lenValue).</li> </ol> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> len = O.length &gt;&gt;&gt; <span class="hljs-number">0</span>; <span class="hljs-comment">// Hack to convert O.length to a UInt32</span></pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">&#182;</a> </div> <ol> <li>If IsCallable(callback) is false, throw a TypeError exception. See: <a href="http://es5.github.com/#x9.11">http://es5.github.com/#x9.11</a></li> </ol> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( {}.toString.call(callback) != <span class="hljs-string">"[object Function]"</span> ) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>( callback + <span class="hljs-string">" is not a function"</span> ); }</pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">&#182;</a> </div> <ol> <li>If thisArg was supplied, let T be thisArg; else let T be undefined.</li> </ol> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( thisArg ) { T = thisArg; }</pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">&#182;</a> </div> <ol> <li>Let k be 0</li> </ol> </div> <div class="content"><div class='highlight'><pre> k = <span class="hljs-number">0</span>;</pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">&#182;</a> </div> <ol> <li>Repeat, while k &lt; len</li> </ol> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span>( k &lt; len ) { <span class="hljs-keyword">var</span> kValue;</pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">&#182;</a> </div> <p>a. Let Pk be ToString(k). This is implicit for LHS operands of the in operator b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. This step can be combined with c c. If kPresent is true, then</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> ( k <span class="hljs-keyword">in</span> O ) {</pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">&#182;</a> </div> <p>i. Let kValue be the result of calling the Get internal method of O with argument Pk.</p> </div> <div class="content"><div class='highlight'><pre> kValue = O[ k ];</pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">&#182;</a> </div> <p>ii. Call the Call internal method of callback with T as the this value and argument list containing kValue, k, and O.</p> </div> <div class="content"><div class='highlight'><pre> callback.call( T, kValue, k, O ); }</pre></div></div> </li> <li id="section-16"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-16">&#182;</a> </div> <p>d. Increase k by 1.</p> </div> <div class="content"><div class='highlight'><pre> k++; }</pre></div></div> </li> <li id="section-17"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-17">&#182;</a> </div> <ol> <li>return undefined</li> </ol> </div> <div class="content"><div class='highlight'><pre> }; Twig.merge = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">target, source, onlyChanged</span>) </span>{ Twig.forEach(<span class="hljs-built_in">Object</span>.keys(source), <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">key</span>) </span>{ <span class="hljs-keyword">if</span> (onlyChanged &amp;&amp; !(key <span class="hljs-keyword">in</span> target)) { <span class="hljs-keyword">return</span>; } target[key] = source[key] }); <span class="hljs-keyword">return</span> target; }; <span class="hljs-comment">/** * Exception thrown by twig.js. */</span> Twig.Error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">message</span>) </span>{ <span class="hljs-keyword">this</span>.message = message; <span class="hljs-keyword">this</span>.name = <span class="hljs-string">"TwigException"</span>; <span class="hljs-keyword">this</span>.type = <span class="hljs-string">"TwigException"</span>; }; <span class="hljs-comment">/** * Get the string representation of a Twig error. */</span> Twig.Error.prototype.toString = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> output = <span class="hljs-keyword">this</span>.name + <span class="hljs-string">": "</span> + <span class="hljs-keyword">this</span>.message; <span class="hljs-keyword">return</span> output; }; <span class="hljs-comment">/** * Wrapper for logging to the console. */</span> Twig.log = { trace: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<span class="hljs-keyword">if</span> (Twig.trace &amp;&amp; <span class="hljs-built_in">console</span>) {<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>));}}, debug: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<span class="hljs-keyword">if</span> (Twig.debug &amp;&amp; <span class="hljs-built_in">console</span>) {<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>));}} }; <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">console</span> !== <span class="hljs-string">"undefined"</span>) { <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">console</span>.error !== <span class="hljs-string">"undefined"</span>) { Twig.log.error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-built_in">console</span>.error.apply(<span class="hljs-built_in">console</span>, <span class="hljs-built_in">arguments</span>); } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">console</span>.log !== <span class="hljs-string">"undefined"</span>) { Twig.log.error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-built_in">console</span>.log.apply(<span class="hljs-built_in">console</span>, <span class="hljs-built_in">arguments</span>); } } } <span class="hljs-keyword">else</span> { Twig.log.error = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{}; } <span class="hljs-comment">/** * Wrapper for child context objects in Twig. * * @param {Object} context Values to initialize the context with. */</span> Twig.ChildContext = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{ <span class="hljs-keyword">var</span> ChildContext = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ChildContext</span>(<span class="hljs-params"></span>) </span>{}; ChildContext.prototype = context; <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ChildContext(); }; <span class="hljs-comment">/** * Container for methods related to handling high level template tokens * (for example: {{ expression }}, {% logic %}, {# comment #}, raw data) */</span> Twig.token = {}; <span class="hljs-comment">/** * Token types. */</span> Twig.token.type = { output: <span class="hljs-string">'output'</span>, logic: <span class="hljs-string">'logic'</span>, comment: <span class="hljs-string">'comment'</span>, raw: <span class="hljs-string">'raw'</span>, output_whitespace_pre: <span class="hljs-string">'output_whitespace_pre'</span>, output_whitespace_post: <span class="hljs-string">'output_whitespace_post'</span>, output_whitespace_both: <span class="hljs-string">'output_whitespace_both'</span>, logic_whitespace_pre: <span class="hljs-string">'logic_whitespace_pre'</span>, logic_whitespace_post: <span class="hljs-string">'logic_whitespace_post'</span>, logic_whitespace_both: <span class="hljs-string">'logic_whitespace_both'</span> }; <span class="hljs-comment">/** * Token syntax definitions. */</span> Twig.token.definitions = [ { type: Twig.token.type.raw, open: <span class="hljs-string">'{% raw %}'</span>, close: <span class="hljs-string">'{% endraw %}'</span> }, { type: Twig.token.type.raw, open: <span class="hljs-string">'{% verbatim %}'</span>, close: <span class="hljs-string">'{% endverbatim %}'</span> },</pre></div></div> </li> <li id="section-18"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-18">&#182;</a> </div> <p><em>Whitespace type tokens</em></p> <p>These typically take the form <code>{{- expression -}}</code> or <code>{{- expression }}</code> or <code>{{ expression -}}</code>.</p> </div> <div class="content"><div class='highlight'><pre> { type: Twig.token.type.output_whitespace_pre, open: <span class="hljs-string">'{{-'</span>, close: <span class="hljs-string">'}}'</span> }, { type: Twig.token.type.output_whitespace_post, open: <span class="hljs-string">'{{'</span>, close: <span class="hljs-string">'-}}'</span> }, { type: Twig.token.type.output_whitespace_both, open: <span class="hljs-string">'{{-'</span>, close: <span class="hljs-string">'-}}'</span> }, { type: Twig.token.type.logic_whitespace_pre, open: <span class="hljs-string">'{%-'</span>, close: <span class="hljs-string">'%}'</span> }, { type: Twig.token.type.logic_whitespace_post, open: <span class="hljs-string">'{%'</span>, close: <span class="hljs-string">'-%}'</span> }, { type: Twig.token.type.logic_whitespace_both, open: <span class="hljs-string">'{%-'</span>, close: <span class="hljs-string">'-%}'</span> },</pre></div></div> </li> <li id="section-19"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-19">&#182;</a> </div> <p><em>Output type tokens</em></p> <p>These typically take the form <code>{{ expression }}</code>.</p> </div> <div class="content"><div class='highlight'><pre> { type: Twig.token.type.output, open: <span class="hljs-string">'{{'</span>, close: <span class="hljs-string">'}}'</span> },</pre></div></div> </li> <li id="section-20"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-20">&#182;</a> </div> <p><em>Logic type tokens</em></p> <p>These typically take a form like <code>{% if expression %}</code> or <code>{% endif %}</code></p> </div> <div class="content"><div class='highlight'><pre> { type: Twig.token.type.logic, open: <span class="hljs-string">'{%'</span>, close: <span class="hljs-string">'%}'</span> },</pre></div></div> </li> <li id="section-21"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-21">&#182;</a> </div> <p><em>Comment type tokens</em></p> <p>These take the form <code>{# anything #}</code></p> </div> <div class="content"><div class='highlight'><pre> { type: Twig.token.type.comment, open: <span class="hljs-string">'{#'</span>, close: <span class="hljs-string">'#}'</span> } ]; <span class="hljs-comment">/** * What characters start "strings" in token definitions. We need this to ignore token close * strings inside an expression. */</span> Twig.token.strings = [<span class="hljs-string">'"'</span>, <span class="hljs-string">"'"</span>]; Twig.token.findStart = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">template</span>) </span>{ <span class="hljs-keyword">var</span> output = { position: <span class="hljs-literal">null</span>, close_position: <span class="hljs-literal">null</span>, def: <span class="hljs-literal">null</span> }, i, token_template, first_key_position, close_key_position; <span class="hljs-keyword">for</span> (i=<span class="hljs-number">0</span>;i&lt;Twig.token.definitions.length;i++) { token_template = Twig.token.definitions[i]; first_key_position = template.indexOf(token_template.open); close_key_position = template.indexOf(token_template.close); Twig.log.trace(<span class="hljs-string">"Twig.token.findStart: "</span>, <span class="hljs-string">"Searching for "</span>, token_template.open, <span class="hljs-string">" found at "</span>, first_key_position);</pre></div></div> </li> <li id="section-22"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-22">&#182;</a> </div> <p>Special handling for mismatched tokens</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (first_key_position &gt;= <span class="hljs-number">0</span>) {</pre></div></div> </li> <li id="section-23"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-23">&#182;</a> </div> <p>This token matches the template</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_template.open.length !== token_template.close.length) {</pre></div></div> </li> <li id="section-24"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-24">&#182;</a> </div> <p>This token has mismatched closing and opening tags</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (close_key_position &lt; <span class="hljs-number">0</span>) {</pre></div></div> </li> <li id="section-25"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-25">&#182;</a> </div> <p>This token’s closing tag does not match the template</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">continue</span>; } } }</pre></div></div> </li> <li id="section-26"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-26">&#182;</a> </div> <p>Does this token occur before any other types?</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (first_key_position &gt;= <span class="hljs-number">0</span> &amp;&amp; (output.position === <span class="hljs-literal">null</span> || first_key_position &lt; output.position)) { output.position = first_key_position; output.def = token_template; output.close_position = close_key_position; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (first_key_position &gt;= <span class="hljs-number">0</span> &amp;&amp; output.position !== <span class="hljs-literal">null</span> &amp;&amp; first_key_position === output.position) { <span class="hljs-comment">/*This token exactly matches another token, greedily match to check if this token has a greater specificity*/</span> <span class="hljs-keyword">if</span> (token_template.open.length &gt; output.def.open.length) {</pre></div></div> </li> <li id="section-27"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-27">&#182;</a> </div> <p>This token’s opening tag is more specific than the previous match</p> </div> <div class="content"><div class='highlight'><pre> output.position = first_key_position; output.def = token_template; output.close_position = close_key_position; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (token_template.open.length === output.def.open.length) { <span class="hljs-keyword">if</span> (token_template.close.length &gt; output.def.close.length) {</pre></div></div> </li> <li id="section-28"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-28">&#182;</a> </div> <p>This token’s opening tag is as specific as the previous match, but the closing tag has greater specificity</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (close_key_position &gt;= <span class="hljs-number">0</span> &amp;&amp; close_key_position &lt; output.close_position) {</pre></div></div> </li> <li id="section-29"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-29">&#182;</a> </div> <p>This token’s closing tag exists in the template, and it occurs sooner than the previous match</p> </div> <div class="content"><div class='highlight'><pre> output.position = first_key_position; output.def = token_template; output.close_position = close_key_position; } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (close_key_position &gt;= <span class="hljs-number">0</span> &amp;&amp; close_key_position &lt; output.close_position) {</pre></div></div> </li> <li id="section-30"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-30">&#182;</a> </div> <p>This token’s closing tag is not more specific than the previous match, but it occurs sooner than the previous match</p> </div> <div class="content"><div class='highlight'><pre> output.position = first_key_position; output.def = token_template; output.close_position = close_key_position; } } } } <span class="hljs-keyword">delete</span> output[<span class="hljs-string">'close_position'</span>]; <span class="hljs-keyword">return</span> output; }; Twig.token.findEnd = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">template, token_def, start</span>) </span>{ <span class="hljs-keyword">var</span> end = <span class="hljs-literal">null</span>, found = <span class="hljs-literal">false</span>, offset = <span class="hljs-number">0</span>,</pre></div></div> </li> <li id="section-31"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-31">&#182;</a> </div> <p>String position variables</p> </div> <div class="content"><div class='highlight'><pre> str_pos = <span class="hljs-literal">null</span>, str_found = <span class="hljs-literal">null</span>, pos = <span class="hljs-literal">null</span>, end_offset = <span class="hljs-literal">null</span>, this_str_pos = <span class="hljs-literal">null</span>, end_str_pos = <span class="hljs-literal">null</span>,</pre></div></div> </li> <li id="section-32"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-32">&#182;</a> </div> <p>For loop variables</p> </div> <div class="content"><div class='highlight'><pre> i, l; <span class="hljs-keyword">while</span> (!found) { str_pos = <span class="hljs-literal">null</span>; str_found = <span class="hljs-literal">null</span>; pos = template.indexOf(token_def.close, offset); <span class="hljs-keyword">if</span> (pos &gt;= <span class="hljs-number">0</span>) { end = pos; found = <span class="hljs-literal">true</span>; } <span class="hljs-keyword">else</span> {</pre></div></div> </li> <li id="section-33"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-33">&#182;</a> </div> <p>throw an exception</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Twig.Error(<span class="hljs-string">"Unable to find closing bracket '"</span> + token_def.close + <span class="hljs-string">"'"</span> + <span class="hljs-string">" opened near template position "</span> + start); }</pre></div></div> </li> <li id="section-34"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-34">&#182;</a> </div> <p>Ignore quotes within comments; just look for the next comment close sequence, regardless of what comes before it. <a href="https://github.com/justjohn/twig.js/issues/95">https://github.com/justjohn/twig.js/issues/95</a></p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_def.type === Twig.token.type.comment) { <span class="hljs-keyword">break</span>; }</pre></div></div> </li> <li id="section-35"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-35">&#182;</a> </div> <p>Ignore quotes within raw tag Fixes #283</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (token_def.type === Twig.token.type.raw) { <span class="hljs-keyword">break</span>; } l = Twig.token.strings.length; <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; l; i += <span class="hljs-number">1</span>) { this_str_pos = template.indexOf(Twig.token.strings[i], offset); <span class="hljs-keyword">if</span> (this_str_pos &gt; <span class="hljs-number">0</span> &amp;&amp; this_str_pos &lt; pos &amp;&amp; (str_pos === <span class="hljs-literal">null</span> || this_str_pos &lt; str_pos)) { str_pos = this_str_pos; str_found = Twig.token.strings[i]; } }</pre></div></div> </li> <li id="section-36"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-36">&#182;</a> </div> <p>We found a string before the end of the token, now find the string’s end and set the search offset to it</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (str_pos !== <span class="hljs-literal">null</span>) { end_offset = str_pos + <span class="hljs-number">1</span>; end = <span class="hljs-literal">null</span>; found = <span class="hljs-literal">false</span>; <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) { end_str_pos = template.indexOf(str_found, end_offset); <span class="hljs-keyword">if</span> (end_str_pos &lt; <span class="hljs-number">0</span>) { <span class="hljs-keyword">throw</span> <span class="hljs-string">"Unclosed string in template"</span>; }</pre></div></div> </li> <li id="section-37"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-37">&#182;</a> </div> <p>Ignore escaped quotes</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (template.substr(end_str_pos - <span class="hljs-number">1</span>, <span class="hljs-number">1</span>) !== <span class="hljs-string">"\\"</span>) { offset = end_str_pos + <span class="hljs-number">1</span>; <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">else</span> { end_offset = end_str_pos + <span class="hljs-number">1</span>; } } } } <span class="hljs-keyword">return</span> end; }; <span class="hljs-comment">/** * Convert a template into high-level tokens. */</span> Twig.tokenize = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">template</span>) </span>{ <span class="hljs-keyword">var</span> tokens = [],</pre></div></div> </li> <li id="section-38"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-38">&#182;</a> </div> <p>An offset for reporting errors locations in the template.</p> </div> <div class="content"><div class='highlight'><pre> error_offset = <span class="hljs-number">0</span>,</pre></div></div> </li> <li id="section-39"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-39">&#182;</a> </div> <p>The start and type of the first token found in the template.</p> </div> <div class="content"><div class='highlight'><pre> found_token = <span class="hljs-literal">null</span>,</pre></div></div> </li> <li id="section-40"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-40">&#182;</a> </div> <p>The end position of the matched token.</p> </div> <div class="content"><div class='highlight'><pre> end = <span class="hljs-literal">null</span>; <span class="hljs-keyword">while</span> (template.length &gt; <span class="hljs-number">0</span>) {</pre></div></div> </li> <li id="section-41"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-41">&#182;</a> </div> <p>Find the first occurance of any token type in the template</p> </div> <div class="content"><div class='highlight'><pre> found_token = Twig.token.findStart(template); Twig.log.trace(<span class="hljs-string">"Twig.tokenize: "</span>, <span class="hljs-string">"Found token: "</span>, found_token); <span class="hljs-keyword">if</span> (found_token.position !== <span class="hljs-literal">null</span>) {</pre></div></div> </li> <li id="section-42"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-42">&#182;</a> </div> <p>Add a raw type token for anything before the start of the token</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (found_token.position &gt; <span class="hljs-number">0</span>) { tokens.push({ type: Twig.token.type.raw, value: template.substring(<span class="hljs-number">0</span>, found_token.position) }); } template = template.substr(found_token.position + found_token.def.open.length); error_offset += found_token.position + found_token.def.open.length;</pre></div></div> </li> <li id="section-43"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-43">&#182;</a> </div> <p>Find the end of the token</p> </div> <div class="content"><div class='highlight'><pre> end = Twig.token.findEnd(template, found_token.def, error_offset); Twig.log.trace(<span class="hljs-string">"Twig.tokenize: "</span>, <span class="hljs-string">"Token ends at "</span>, end); tokens.push({ type: found_token.def.type, value: template.substring(<span class="hljs-number">0</span>, end).trim() }); <span class="hljs-keyword">if</span> (template.substr( end + found_token.def.close.length, <span class="hljs-number">1</span> ) === <span class="hljs-string">"\n"</span>) { <span class="hljs-keyword">switch</span> (found_token.def.type) { <span class="hljs-keyword">case</span> <span class="hljs-string">"logic_whitespace_pre"</span>: <span class="hljs-keyword">case</span> <span class="hljs-string">"logic_whitespace_post"</span>: <span class="hljs-keyword">case</span> <span class="hljs-string">"logic_whitespace_both"</span>: <span class="hljs-keyword">case</span> <span class="hljs-string">"logic"</span>:</pre></div></div> </li> <li id="section-44"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-44">&#182;</a> </div> <p>Newlines directly after logic tokens are ignored</p> </div> <div class="content"><div class='highlight'><pre> end += <span class="hljs-number">1</span>; <span class="hljs-keyword">break</span>; } } template = template.substr(end + found_token.def.close.length);</pre></div></div> </li> <li id="section-45"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-45">&#182;</a> </div> <p>Increment the position in the template</p> </div> <div class="content"><div class='highlight'><pre> error_offset += end + found_token.def.close.length; } <span class="hljs-keyword">else</span> {</pre></div></div> </li> <li id="section-46"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-46">&#182;</a> </div> <p>No more tokens -&gt; add the rest of the template as a raw-type token</p> </div> <div class="content"><div class='highlight'><pre> tokens.push({ type: Twig.token.type.raw, value: template }); template = <span class="hljs-string">''</span>; } } <span class="hljs-keyword">return</span> tokens; }; Twig.compile = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">tokens</span>) </span>{ <span class="hljs-keyword">try</span> {</pre></div></div> </li> <li id="section-47"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-47">&#182;</a> </div> <p>Output and intermediate stacks</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> output = [], stack = [],</pre></div></div> </li> <li id="section-48"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-48">&#182;</a> </div> <p>The tokens between open and close tags</p> </div> <div class="content"><div class='highlight'><pre> intermediate_output = [], token = <span class="hljs-literal">null</span>, logic_token = <span class="hljs-literal">null</span>, unclosed_token = <span class="hljs-literal">null</span>,</pre></div></div> </li> <li id="section-49"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-49">&#182;</a> </div> <p>Temporary previous token.</p> </div> <div class="content"><div class='highlight'><pre> prev_token = <span class="hljs-literal">null</span>,</pre></div></div> </li> <li id="section-50"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-50">&#182;</a> </div> <p>Temporary previous output.</p> </div> <div class="content"><div class='highlight'><pre> prev_output = <span class="hljs-literal">null</span>,</pre></div></div> </li> <li id="section-51"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-51">&#182;</a> </div> <p>Temporary pr