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